#include "threadshandler.h"
#include "registerhandler.h"
#include "bytearrayinputstream.h"
+#include "debuggeragents.h"
#include "gdb/gdbmi.h"
#ifdef Q_OS_WIN
# include "shared/dbgwinutils.h"
// 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'
namespace Internal {
class BreakpointData;
class BreakpointParameters;
+class DisassemblerLines;
class StackFrame;
struct ThreadData;
class Register;
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);
#include <QtCore/QDebug>
#include <QtCore/QMetaObject>
+#include <QtCore/QTimer>
#include <QtGui/QMessageBox>
#include <QtGui/QPlainTextEdit>
+#include <QtGui/QTextBlock>
#include <QtGui/QTextCursor>
#include <QtGui/QIcon>
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),
{
}
+
/*!
\class DisassemblerViewAgent
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);
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";
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
// 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
#define DEBUGGER_AGENTS_H
#include <QtCore/QObject>
+#include <QtCore/QHash>
#include <QtCore/QPointer>
+#include <QtCore/QVector>
namespace Core {
class IEditor;
namespace Internal {
class StackFrame;
+class DisassemblerViewAgent;
+class DisassemblerViewAgentPrivate;
class MemoryViewAgent : public QObject
{
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
{
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;
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;
};
//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));
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",
// {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()) {
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)
&& 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
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);
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();
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);
}
class WatchData;
class DisassemblerAgentCookie;
+class DisassemblerLines;
class AttachGdbAdapter;
class CoreGdbAdapter;
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
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: