Commit d0b7c124 authored by hjk's avatar hjk

debugger: make debugger view data store more structured

parent 90657a49
......@@ -33,6 +33,7 @@
#include "threadshandler.h"
#include "registerhandler.h"
#include "bytearrayinputstream.h"
#include "debuggeragents.h"
#include "gdb/gdbmi.h"
#ifdef Q_OS_WIN
# include "shared/dbgwinutils.h"
......@@ -99,19 +100,17 @@ QByteArray cdbAddBreakpointCommand(const Debugger::Internal::BreakpointParameter
// Remove the address separator. Format the address exactly as
// the agent does (0xhex, as taken from frame) for the location mark to trigger.
QString formatCdbDisassembler(const QList<QByteArray> &in)
Internal::DisassemblerLines formatCdbDisassembler(const QList<QByteArray> &in)
{
QString disassembly;
const QChar newLine = QLatin1Char('\n');
Internal::DisassemblerLines result;
foreach(QByteArray line, in) {
// Remove 64bit separator.
if (line.size() >= 9 && line.at(8) == '`')
line.remove(8, 1);
// Ensure address is as wide as agent's address.
disassembly += QString::fromLatin1(line);
disassembly += newLine;
result.appendLine(Internal::DisassemblerLine(line));
}
return disassembly;
return result;
}
// Fix a CDB integer value: '00000000`0012a290' -> '12a290', '0n10' ->'10'
......
......@@ -44,6 +44,7 @@ namespace Debugger {
namespace Internal {
class BreakpointData;
class BreakpointParameters;
class DisassemblerLines;
class StackFrame;
struct ThreadData;
class Register;
......@@ -56,7 +57,7 @@ namespace Cdb {
QByteArray cdbAddBreakpointCommand(const Debugger::Internal::BreakpointParameters &d, bool oneshot = false, int id = -1);
// Format CDB Dissambler output.
QString formatCdbDisassembler(const QList<QByteArray> &in);
Internal::DisassemblerLines formatCdbDisassembler(const QList<QByteArray> &in);
// Convert a CDB integer value: '00000000`0012a290' -> '12a290', '0n10' ->'10'
QByteArray fixCdbIntegerValue(QByteArray t, bool stripLeadingZeros = false, int *basePtr = 0);
......
......@@ -50,9 +50,11 @@
#include <QtCore/QDebug>
#include <QtCore/QMetaObject>
#include <QtCore/QTimer>
#include <QtGui/QMessageBox>
#include <QtGui/QPlainTextEdit>
#include <QtGui/QTextBlock>
#include <QtGui/QTextCursor>
#include <QtGui/QIcon>
......@@ -202,23 +204,25 @@ public:
void documentClosing() {}
};
struct DisassemblerViewAgentPrivate
class DisassemblerViewAgentPrivate
{
public:
DisassemblerViewAgentPrivate();
void configureMimeType();
public:
QPointer<TextEditor::ITextEditor> editor;
StackFrame frame;
bool tryMixed;
bool setMarker;
QPointer<DebuggerEngine> engine;
LocationMark2 *locationMark;
QHash<QString, QString> cache;
QHash<QString, DisassemblerLines> cache;
QString mimeType;
};
DisassemblerViewAgentPrivate::DisassemblerViewAgentPrivate() :
editor(0),
DisassemblerViewAgentPrivate::DisassemblerViewAgentPrivate()
: editor(0),
tryMixed(true),
setMarker(true),
locationMark(new LocationMark2),
......@@ -226,6 +230,7 @@ DisassemblerViewAgentPrivate::DisassemblerViewAgentPrivate() :
{
}
/*!
\class DisassemblerViewAgent
......@@ -288,7 +293,8 @@ void DisassemblerViewAgent::setFrame(const StackFrame &frame, bool tryMixed,
d->setMarker = setMarker;
d->tryMixed = tryMixed;
if (isMixed()) {
QHash<QString, QString>::ConstIterator it = d->cache.find(frameKey(frame));
QHash<QString, DisassemblerLines>::ConstIterator it =
d->cache.find(frameKey(frame));
if (it != d->cache.end()) {
QString msg = _("Use cache disassembler for '%1' in '%2'")
.arg(frame.function).arg(frame.file);
......@@ -334,37 +340,12 @@ void DisassemblerViewAgent::setMimeType(const QString &mt)
d->configureMimeType();
}
// Return a pair of <linenumber [1..n], character position> of an address
// in assembly code, assuming lines start with a sane hex address.
static QPair<int, int> lineNumberOfAddress(const QString &disassembly, quint64 address)
{
if (disassembly.isEmpty())
return QPair<int, int>(-1, -1);
int pos = 0;
const QChar newLine = QLatin1Char('\n');
const int size = disassembly.size();
for (int lineNumber = 1; pos < size; lineNumber++) {
int endOfLinePos = disassembly.indexOf(newLine, pos);
if (endOfLinePos == -1)
endOfLinePos = size;
const QString line = disassembly.mid(pos, endOfLinePos - pos);
if (DisassemblerViewAgent::addressFromDisassemblyLine(line) == address)
return QPair<int, int>(lineNumber, pos);
pos = endOfLinePos + 1;
}
return QPair<int, int>(-1, -1);;
}
void DisassemblerViewAgent::setContents(const QString &contents)
void DisassemblerViewAgent::setContents(const DisassemblerLines &contents)
{
QTC_ASSERT(d, return);
using namespace Core;
using namespace TextEditor;
d->cache.insert(frameKey(d->frame), contents);
QPlainTextEdit *plainTextEdit = 0;
EditorManager *editorManager = EditorManager::instance();
if (!d->editor) {
QString titlePattern = "Disassembler";
......@@ -380,33 +361,37 @@ void DisassemblerViewAgent::setContents(const QString &contents)
editorManager->activateEditor(d->editor);
plainTextEdit = qobject_cast<QPlainTextEdit *>(d->editor->widget());
if (plainTextEdit) {
plainTextEdit->setPlainText(contents);
plainTextEdit->setReadOnly(true);
QPlainTextEdit *plainTextEdit =
qobject_cast<QPlainTextEdit *>(d->editor->widget());
QTC_ASSERT(plainTextEdit, return);
QString str;
for (int i = 0, n = contents.size(); i != n; ++i) {
const DisassemblerLine &dl = contents.at(i);
if (dl.address) {
str += QString("0x");
str += QString::number(dl.address, 16);
str += " ";
}
str += dl.data;
str += "\n";
}
plainTextEdit->setPlainText(str);
plainTextEdit->setReadOnly(true);
if (d->setMarker)
d->editor->markableInterface()->removeMark(d->locationMark);
d->editor->setDisplayName(_("Disassembler (%1)").arg(d->frame.function));
d->cache.insert(frameKey(d->frame), contents);
const QPair<int, int> lineNumberPos =
lineNumberOfAddress(contents, d->frame.address);
if (lineNumberPos.first > 0) {
if (d->setMarker)
d->editor->markableInterface()->addMark(d->locationMark, lineNumberPos.first);
if (plainTextEdit) {
QTextCursor tc = plainTextEdit->textCursor();
tc.setPosition(lineNumberPos.second);
plainTextEdit->setTextCursor(tc);
}
}
}
int lineNumber = contents.m_rowCache[d->frame.address];
if (lineNumber && d->setMarker)
d->editor->markableInterface()->addMark(d->locationMark, lineNumber);
bool DisassemblerViewAgent::contentsCoversAddress(const QString &contents) const
{
QTC_ASSERT(d, return false);
return lineNumberOfAddress(contents, d->frame.address).first > 0;
QTextCursor tc = plainTextEdit->textCursor();
QTextBlock block = tc.document()->findBlockByNumber(lineNumber - 1);
tc.setPosition(block.position());
plainTextEdit->setTextCursor(tc);
}
quint64 DisassemblerViewAgent::address() const
......@@ -416,20 +401,54 @@ quint64 DisassemblerViewAgent::address() const
// Return address of an assembly line "0x0dfd bla"
quint64 DisassemblerViewAgent::addressFromDisassemblyLine(const QString &line)
{
return DisassemblerLine(line).address;
}
DisassemblerLine::DisassemblerLine(const QString &unparsed)
{
// Mac gdb has an overflow reporting 64bit addresses causing the instruction
// to follow the last digit "0x000000013fff4810mov 1,1". Truncate here.
const int pos = qMin(line.indexOf(QLatin1Char(' ')), 19);
if (pos < 0)
return 0;
QString addressS = line.left(pos);
if (addressS.endsWith(':')) // clang
addressS.chop(1);
if (addressS.startsWith(QLatin1String("0x")))
addressS.remove(0, 2);
const int pos = qMin(unparsed.indexOf(QLatin1Char(' ')), 19);
if (pos < 0) {
address = 0;
data = unparsed;
return;
}
QString addr = unparsed.left(pos);
if (addr.endsWith(':')) // clang
addr.chop(1);
if (addr.startsWith(QLatin1String("0x")))
addr.remove(0, 2);
bool ok;
const quint64 address = addressS.toULongLong(&ok, 16);
return ok ? address : quint64(0);
address = addr.toULongLong(&ok, 16);
if (address)
data = unparsed.mid(pos + 1);
else
data = unparsed;
}
int DisassemblerLines::lineForAddress(quint64 address) const
{
return m_rowCache.value(address);
}
bool DisassemblerLines::coversAddress(quint64 address) const
{
return m_rowCache.value(address) != 0;
}
void DisassemblerLines::appendComment(const QString &comment)
{
DisassemblerLine dl;
dl.data = comment;
m_data.append(dl);
}
void DisassemblerLines::appendLine(const DisassemblerLine &dl)
{
m_data.append(dl);
m_rowCache[dl.address] = m_data.size();
}
} // namespace Internal
......
......@@ -31,7 +31,9 @@
#define DEBUGGER_AGENTS_H
#include <QtCore/QObject>
#include <QtCore/QHash>
#include <QtCore/QPointer>
#include <QtCore/QVector>
namespace Core {
class IEditor;
......@@ -44,6 +46,8 @@ class DebuggerEngine;
namespace Internal {
class StackFrame;
class DisassemblerViewAgent;
class DisassemblerViewAgentPrivate;
class MemoryViewAgent : public QObject
{
......@@ -72,7 +76,32 @@ private:
QPointer<Debugger::DebuggerEngine> m_engine;
};
struct DisassemblerViewAgentPrivate;
class DisassemblerLine
{
public:
DisassemblerLine() : address(0) {}
DisassemblerLine(const QString &unparsed);
quint64 address;
QString data;
};
class DisassemblerLines
{
public:
DisassemblerLines() {}
bool coversAddress(quint64 address) const;
void appendLine(const DisassemblerLine &dl);
void appendComment(const QString &comment);
int size() const { return m_data.size(); }
const DisassemblerLine &at(int i) const { return m_data.at(i); }
int lineForAddress(quint64 address) const;
private:
friend class DisassemblerViewAgent;
QVector<DisassemblerLine> m_data;
QHash<quint64, int> m_rowCache;
};
class DisassemblerViewAgent : public QObject
{
......@@ -86,7 +115,7 @@ public:
void setFrame(const StackFrame &frame, bool tryMixed, bool setMarker);
const StackFrame &frame() const;
void resetLocation();
Q_SLOT void setContents(const QString &contents);
void setContents(const DisassemblerLines &contents);
// Mimetype: "text/a-asm" or some specialized architecture
QString mimeType() const;
......@@ -98,8 +127,7 @@ public:
bool isMixed() const;
// Return address of an assembly line "0x0dfd bla"
static quint64 addressFromDisassemblyLine(const QString &line);
static quint64 addressFromDisassemblyLine(const QString &data);
private:
DisassemblerViewAgentPrivate *d;
};
......
......@@ -874,6 +874,23 @@ void GdbEngine::handleResultRecord(GdbResponse *response)
//shutdown();
//showMessageBox(QMessageBox::Critical,
// tr("Executable failed"), QString::fromLocal8Bit(msg));
} else if (msg.contains("Cannot insert breakpoint")) {
// For breakpoints set by address to non-existent addresses we
// might get something like "6^error,msg="Warning:\nCannot insert
// breakpoint 3.\nError accessing memory address 0x34592327:
// Input/output error.\nCannot insert breakpoint 4.\nError
// accessing memory address 0x34592335: Input/output error.\n".
// This should not stop us from proceeding.
// Most notably, that happens after a "6^running" and "*running"
// We are probably sitting at _start and can't proceed as
// long as the breakpoints are enabled.
// FIXME: Should we silently disable the offending breakpoints?
showMessage(_("APPLYING WORKAROUND #5"));
showMessageBox(QMessageBox::Critical,
tr("Setting breakpoints failed"), QString::fromLocal8Bit(msg));
QTC_ASSERT(state() == InferiorRunOk, /**/);
notifyInferiorSpontaneousStop();
notifyEngineIll();
} else {
showMessageBox(QMessageBox::Critical,
tr("Executable failed"), QString::fromLocal8Bit(msg));
......@@ -3876,24 +3893,20 @@ void GdbEngine::fetchDisassemblerByAddressCli(const DisassemblerAgentCookie &ac0
QVariant::fromValue(ac));
}
static QByteArray parseLine(const GdbMi &line)
static DisassemblerLine parseLine(const GdbMi &line)
{
QByteArray ba;
ba.reserve(200);
DisassemblerLine dl;
QByteArray address = line.findChild("address").data();
//QByteArray funcName = line.findChild("func-name").data();
//QByteArray offset = line.findChild("offset").data();
QByteArray inst = line.findChild("inst").data();
ba += address;
ba += QByteArray(15 - address.size(), ' ');
dl.address = address.toULongLong();
//ba += funcName + "+" + offset + " ";
//ba += QByteArray(30 - funcName.size() - offset.size(), ' ');
ba += inst;
ba += '\n';
return ba;
dl.data = _(line.findChild("inst").data());
return dl;
}
QString GdbEngine::parseDisassembler(const GdbMi &lines)
DisassemblerLines GdbEngine::parseDisassembler(const GdbMi &lines)
{
// ^done,data={asm_insns=[src_and_asm_line={line="1243",file=".../app.cpp",
// line_asm_insn=[{address="0x08054857",func-name="main",offset="27",
......@@ -3906,10 +3919,9 @@ QString GdbEngine::parseDisassembler(const GdbMi &lines)
// {address="0x0805acf8",func-name="...",offset="25",inst="and $0xe8,%al"},
// {address="0x0805acfa",func-name="...",offset="27",inst="pop %esp"},
QList<QByteArray> fileContents;
QStringList fileContents;
bool fileLoaded = false;
QByteArray ba;
ba.reserve(200 * lines.children().size());
DisassemblerLines result;
// FIXME: Performance?
foreach (const GdbMi &child, lines.children()) {
......@@ -3920,21 +3932,22 @@ QString GdbEngine::parseDisassembler(const GdbMi &lines)
fileName = cleanupFullName(fileName);
QFile file(fileName);
file.open(QIODevice::ReadOnly);
fileContents = file.readAll().split('\n');
QTextStream ts(&file);
fileContents = ts.readAll().split(QLatin1Char('\n'));
fileLoaded = true;
}
int line = child.findChild("line").data().toInt();
if (line >= 1 && line <= fileContents.size())
ba += " " + fileContents.at(line - 1) + '\n';
result.appendComment(fileContents.at(line - 1));
GdbMi insn = child.findChild("line_asm_insn");
foreach (const GdbMi &line, insn.children())
ba += parseLine(line);
foreach (const GdbMi &item, insn.children())
result.appendLine(parseLine(item));
} else {
// The non-mixed version.
ba += parseLine(child);
result.appendLine(parseLine(child));
}
}
return _(ba);
return result;
}
void GdbEngine::handleFetchDisassemblerByLine(const GdbResponse &response)
......@@ -3950,10 +3963,10 @@ void GdbEngine::handleFetchDisassemblerByLine(const GdbResponse &response)
&& lines.childAt(0).findChild("line").data() == "0")
fetchDisassemblerByAddress(ac, true);
else {
QString contents = parseDisassembler(lines);
if (ac.agent->contentsCoversAddress(contents)) {
DisassemblerLines dlines = parseDisassembler(lines);
if (dlines.coversAddress(ac.agent->address())) {
// All is well.
ac.agent->setContents(contents);
ac.agent->setContents(dlines);
} else {
// Can happen e.g. for initializer list on symbian/rvct where
// we get a file name and line number but where the 'fully
......@@ -3985,9 +3998,9 @@ void GdbEngine::handleFetchDisassemblerByAddress1(const GdbResponse &response)
if (lines.children().isEmpty())
fetchDisassemblerByAddress(ac, false);
else {
QString contents = parseDisassembler(lines);
if (ac.agent->contentsCoversAddress(contents)) {
ac.agent->setContents(parseDisassembler(lines));
DisassemblerLines dlines = parseDisassembler(lines);
if (dlines.coversAddress(ac.agent->address())) {
ac.agent->setContents(dlines);
} else {
showMessage(_("FALL BACK TO NON-MIXED"));
fetchDisassemblerByAddress(ac, false);
......@@ -4030,11 +4043,11 @@ void GdbEngine::handleFetchDisassemblerByCli(const GdbResponse &response)
if (lines.isValid()) {
ac.agent->setContents(parseDisassembler(lines));
} else {
const QByteArray someSpace = " ";
const QString someSpace = _(" ");
// First line is something like
// "Dump of assembler code from 0xb7ff598f to 0xb7ff5a07:"
GdbMi output = response.data.findChild("consolestreamoutput");
QByteArray res;
DisassemblerLines dlines;
QByteArray lastFunction;
foreach (const QByteArray &line0, output.data().split('\n')) {
QByteArray line = line0.trimmed();
......@@ -4057,23 +4070,19 @@ void GdbEngine::handleFetchDisassemblerByCli(const GdbResponse &response)
if (pos1 < pos2 && pos2 < pos3) {
QByteArray function = line.mid(pos1, pos2 - pos1);
if (function != lastFunction) {
res.append("\nFunction: ");
res.append(function);
res.append('\n');
dlines.appendComment(QString());
dlines.appendComment(_("Function: ") + _(function));
lastFunction = function;
}
line.replace(pos1, pos2 - pos1, "");
}
res.append(line);
res.append('\n');
dlines.appendLine(DisassemblerLine(_(line)));
continue;
}
res.append(someSpace);
res.append(line);
res.append('\n');
dlines.appendComment(someSpace + _(line));
}
if (res.size() > 1)
ac.agent->setContents(_(res));
if (dlines.size())
ac.agent->setContents(dlines);
else
fetchDisassemblerByAddressCli(ac);
}
......
......@@ -59,6 +59,7 @@ class GdbMi;
class WatchData;
class DisassemblerAgentCookie;
class DisassemblerLines;
class AttachGdbAdapter;
class CoreGdbAdapter;
......@@ -404,7 +405,7 @@ private: ////////// View & Data Stuff //////////
void handleFetchDisassemblerByLine(const GdbResponse &response);
void handleFetchDisassemblerByAddress1(const GdbResponse &response);
void handleFetchDisassemblerByAddress0(const GdbResponse &response);
QString parseDisassembler(const GdbMi &lines);
DisassemblerLines parseDisassembler(const GdbMi &lines);
//
// Source file specific stuff
......
......@@ -446,9 +446,9 @@ void IPCEngineHost::rpcCallback(quint64 f, QByteArray payload)
QString da;
s >> pc;
s >> da;
DisassemblerViewAgent *view = m_frameToDisassemblerAgent.take(pc);
if (view)
view->setContents(da);
//DisassemblerViewAgent *view = m_frameToDisassemblerAgent.take(pc);
//if (view)
// view->setContents(da);
}
break;
case IPCEngineGuest::UpdateWatchData:
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment