diff options
author | Friedemann Kleint <Friedemann.Kleint@nokia.com> | 2009-04-14 15:04:19 +0200 |
---|---|---|
committer | Friedemann Kleint <Friedemann.Kleint@nokia.com> | 2009-04-14 15:04:19 +0200 |
commit | d4ccc16f9fe5dcadeddf0a2d49cdeff409812deb (patch) | |
tree | f9b6b83334422c559914f50a6fe8673e7134d3d5 /src/plugins/debugger | |
parent | b95af44c0dbdc9ff367e841cbe262cdc10fc292e (diff) | |
download | qt-creator-d4ccc16f9fe5dcadeddf0a2d49cdeff409812deb.tar.gz |
Implement disassembler for cdb.
Filter debugging output correctly, some glitches. Extract
base class for debugging output to be able to intercept
debugging output for other purposes (such as disassembling).
Diffstat (limited to 'src/plugins/debugger')
-rw-r--r-- | src/plugins/debugger/breakwindow.cpp | 9 | ||||
-rw-r--r-- | src/plugins/debugger/cdb/cdbassembler.cpp | 153 | ||||
-rw-r--r-- | src/plugins/debugger/cdb/cdbassembler.h | 10 | ||||
-rw-r--r-- | src/plugins/debugger/cdb/cdbdebugengine.cpp | 35 | ||||
-rw-r--r-- | src/plugins/debugger/cdb/cdbdebugoutput.cpp | 77 | ||||
-rw-r--r-- | src/plugins/debugger/cdb/cdbdebugoutput.h | 45 | ||||
-rw-r--r-- | src/plugins/debugger/cdb/cdbstacktracecontext.cpp | 5 | ||||
-rw-r--r-- | src/plugins/debugger/cdb/cdbstacktracecontext.h | 4 | ||||
-rw-r--r-- | src/plugins/debugger/debuggermanager.cpp | 2 | ||||
-rw-r--r-- | src/plugins/debugger/disassemblerhandler.cpp | 8 | ||||
-rw-r--r-- | src/plugins/debugger/disassemblerhandler.h | 2 |
11 files changed, 312 insertions, 38 deletions
diff --git a/src/plugins/debugger/breakwindow.cpp b/src/plugins/debugger/breakwindow.cpp index a571fafae5..a93885abcb 100644 --- a/src/plugins/debugger/breakwindow.cpp +++ b/src/plugins/debugger/breakwindow.cpp @@ -84,16 +84,17 @@ void BreakWindow::resizeEvent(QResizeEvent *ev) void BreakWindow::contextMenuEvent(QContextMenuEvent *ev) { QMenu menu; - QModelIndex index = indexAt(ev->pos()); + const QModelIndex index = indexAt(ev->pos()); + const bool indexIsValid = index.isValid(); QAction *act0 = new QAction(tr("Delete breakpoint"), &menu); - act0->setEnabled(index.isValid()); + act0->setEnabled(indexIsValid); QAction *act1 = new QAction(tr("Adjust column widths to contents"), &menu); QAction *act2 = new QAction(tr("Always adjust column widths to contents"), &menu); act2->setCheckable(true); act2->setChecked(m_alwaysResizeColumnsToContents); QAction *act3 = new QAction(tr("Edit condition..."), &menu); - act0->setEnabled(index.isValid()); - QAction *act4 = new QAction(tr("Syncronize breakpoints"), &menu); + act3->setEnabled(indexIsValid); + QAction *act4 = new QAction(tr("Synchronize breakpoints"), &menu); menu.addAction(act0); menu.addAction(act3); diff --git a/src/plugins/debugger/cdb/cdbassembler.cpp b/src/plugins/debugger/cdb/cdbassembler.cpp index 3d01d6d54e..00c34415ea 100644 --- a/src/plugins/debugger/cdb/cdbassembler.cpp +++ b/src/plugins/debugger/cdb/cdbassembler.cpp @@ -28,15 +28,20 @@ **************************************************************************/ #include "cdbassembler.h" -#include "registerhandler.h" +#include "cdbdebugoutput.h" #include "cdbdebugengine_p.h" #include "cdbsymbolgroupcontext.h" +#include "disassemblerhandler.h" +#include "registerhandler.h" + #include <QtCore/QVector> namespace Debugger { namespace Internal { +typedef QList<DisassemblerLine> DisassemblerLineList; + bool getRegisters(IDebugControl4 *ctl, IDebugRegisters2 *ireg, QList<Register> *registers, @@ -79,5 +84,151 @@ bool getRegisters(IDebugControl4 *ctl, return true; } +// Output parser for disassembler lines. +// It uses the source file lines as symbol until it encounters +// a C++ symbol (function entered), from which then on +// it uses that symbol. +class DisassemblerOutputParser +{ +public: + explicit DisassemblerOutputParser(DisassemblerLineList *list); + + void parse(const QStringList &l); + +private: + enum ParseResult { ParseOk, ParseIgnore, ParseFailed }; + ParseResult parseDisassembled(const QString &in, DisassemblerLine* l); + + DisassemblerLineList *m_list; + QString m_sourceSymbol; + int m_sourceSymbolOffset; +}; + +DisassemblerOutputParser::DisassemblerOutputParser(DisassemblerLineList *list) : + m_list(list), + m_sourceSymbolOffset(0) +{ +} + +// Parse a disassembler line: +// module!class::foo: +// 004017cf cc int 3 +// 77 mainwindow.cpp 004018ff 8d4da8 lea ecx,[ebp-0x58] +DisassemblerOutputParser::ParseResult + DisassemblerOutputParser::parseDisassembled(const QString &in, DisassemblerLine* l) +{ + l->clear(); + + // Check if there is a source file + if (in.size() < 7) + return ParseIgnore; + const bool hasSourceFile = !in.at(6).isSpace(); + + // Sometimes, empty lines occur + const QString simplified = in.simplified(); + if (simplified.isEmpty()) + return ParseIgnore; + + QStringList tokens = simplified.split(QLatin1Char(' '), QString::SkipEmptyParts); + // Check for symbols as 'module!class::foo:' (start of function encountered) + if (tokens.size() == 1) { + QString symbol = tokens.front(); + if (symbol.endsWith(QLatin1Char(':')) && symbol.contains(QLatin1Char('!'))) { + symbol.truncate(symbol.size() - 1); + m_sourceSymbol = symbol; + m_sourceSymbolOffset = 0; + } + return ParseIgnore; + } + if (tokens.size() < 2) + return ParseIgnore; + // Symbol display: Do we know a symbol? + if (!m_sourceSymbol.isEmpty()) { + l->symbol = QString(QLatin1Char('<')); + l->symbol += m_sourceSymbol; + if (m_sourceSymbolOffset) { + l->symbol += QLatin1Char('+'); + l->symbol += QString::number(m_sourceSymbolOffset); + } + l->symbol += QLatin1Char('>'); + m_sourceSymbolOffset++; + } + // Read source file information: If we don't know a symbol yet, + // use the source file. + if (hasSourceFile) { + if (l->symbol.isEmpty()) { + l->symbol = tokens.at(1); + l->symbol += QLatin1Char('+'); + l->symbol += tokens.front(); + } + tokens.pop_front(); + tokens.pop_front(); + } + l->symbolDisplay = l->symbol; + // Get offset address and instruction + if (tokens.size() < 3) + return ParseFailed; + l->addressDisplay = l->address = tokens.front(); + tokens.pop_front(); + // The rest is effective address & instructions + if (tokens.size() > 1) + tokens.pop_front(); + l->mnemonic = tokens.join(QString(QLatin1Char(' '))); + return ParseOk; +} + +void DisassemblerOutputParser::parse(const QStringList &l) +{ + DisassemblerLine dLine; + foreach(const QString &line, l) { + switch (parseDisassembled(line, &dLine)) { + case ParseOk: + m_list->push_back(dLine); + break; + case ParseIgnore: + break; + case ParseFailed: + qWarning("Failed to parse '%s'\n", qPrintable(line)); + break; + } + } +} + +bool dissassemble(IDebugClient5 *client, + IDebugControl4 *ctl, + ULONG64 offset, + unsigned long beforeLines, + unsigned long afterLines, + QList<DisassemblerLine> *lines, + QString *errorMessage) +{ + if (debugCDB) + qDebug() << Q_FUNC_INFO << offset; + lines->clear(); + const ULONG flags = DEBUG_DISASM_MATCHING_SYMBOLS|DEBUG_DISASM_SOURCE_LINE_NUMBER|DEBUG_DISASM_SOURCE_FILE_NAME; + // Catch the output by temporarily setting another handler. + // We use the method that outputs to the output handler as it + // conveniently provides the 'beforeLines' context (stepping back + // in assembler code). We build a complete string first as line breaks + // may occur in-between messages. + StringOutputHandler stringHandler; + IDebugOutputCallbacksWide *oldHandler = CdbDebugOutputBase::getOutputCallback(client); + client->SetOutputCallbacksWide(&stringHandler); + // For some reason, we need to output to "all clients" + const HRESULT hr = ctl->OutputDisassemblyLines(DEBUG_OUTCTL_ALL_CLIENTS, + beforeLines, beforeLines + afterLines, + offset, flags, 0, 0, 0, 0); + client->SetOutputCallbacksWide(oldHandler); + + if (FAILED(hr)) { + *errorMessage= QString::fromLatin1("Unable to dissamble at 0x%1: %2"). + arg(QString::number(offset, 16), msgComFailed("OutputDisassemblyLines", hr)); + return false; + } + DisassemblerOutputParser parser(lines); + parser.parse(stringHandler.result().split(QLatin1Char('\n'))); + return true; +} + } } diff --git a/src/plugins/debugger/cdb/cdbassembler.h b/src/plugins/debugger/cdb/cdbassembler.h index e2386797e0..d065707db1 100644 --- a/src/plugins/debugger/cdb/cdbassembler.h +++ b/src/plugins/debugger/cdb/cdbassembler.h @@ -39,6 +39,8 @@ namespace Debugger { namespace Internal { +class DisassemblerLine; + // Utilities related to assembler code. class Register; @@ -47,6 +49,14 @@ bool getRegisters(IDebugControl4 *ctl, QList<Register> *registers, QString *errorMessage, int base = 10 /* 16 for hex, etc */); + +bool dissassemble(IDebugClient5 *client, + IDebugControl4 *ctl, + ULONG64 offset, + unsigned long beforeLines, + unsigned long afterLines, + QList<DisassemblerLine> *lines, + QString *errorMessage); } } diff --git a/src/plugins/debugger/cdb/cdbdebugengine.cpp b/src/plugins/debugger/cdb/cdbdebugengine.cpp index bdb692af94..77efd2d4d0 100644 --- a/src/plugins/debugger/cdb/cdbdebugengine.cpp +++ b/src/plugins/debugger/cdb/cdbdebugengine.cpp @@ -42,6 +42,7 @@ #include "watchhandler.h" #include "registerhandler.h" #include "moduleshandler.h" +#include "disassemblerhandler.h" #include "watchutils.h" #include <utils/qtcassert.h> @@ -56,6 +57,7 @@ #include <QtCore/QCoreApplication> #include <QtGui/QMessageBox> #include <QtGui/QMainWindow> +#include <QtGui/QApplication> #define DBGHELP_TRANSLATE_TCHAR #include <inc/Dbghelp.h> @@ -195,7 +197,6 @@ CdbDebugEnginePrivate::CdbDebugEnginePrivate(DebuggerManager *parent, CdbDebugEn m_breakEventMode(BreakEventHandle), m_watchTimer(-1), m_debugEventCallBack(engine), - m_debugOutputCallBack(engine), m_pDebugClient(0), m_pDebugControl(0), m_pDebugSystemObjects(0), @@ -225,7 +226,7 @@ bool CdbDebugEnginePrivate::init(QString *errorMessage) return false; } - m_pDebugClient->SetOutputCallbacks(&m_debugOutputCallBack); + m_pDebugClient->SetOutputCallbacksWide(&m_debugOutputCallBack); m_pDebugClient->SetEventCallbacks(&m_debugEventCallBack); hr = lib.debugCreate( __uuidof(IDebugControl4), reinterpret_cast<void**>(&m_pDebugControl)); @@ -317,6 +318,10 @@ CdbDebugEngine::CdbDebugEngine(DebuggerManager *parent) : m_d->m_debuggerManager, SLOT(showDebuggerOutput(QString,QString))); connect(&m_d->m_debugOutputCallBack, SIGNAL(debuggerInputPrompt(QString,QString)), m_d->m_debuggerManager, SLOT(showDebuggerInput(QString,QString))); + connect(&m_d->m_debugOutputCallBack, SIGNAL(debuggeeOutput(QString)), + m_d->m_debuggerManager, SLOT(showApplicationOutput(QString))); + connect(&m_d->m_debugOutputCallBack, SIGNAL(debuggeeInputPrompt(QString)), + m_d->m_debuggerManager, SLOT(showApplicationOutput(QString))); } CdbDebugEngine::~CdbDebugEngine() @@ -1040,7 +1045,31 @@ void CdbDebugEngine::saveSessionData() } void CdbDebugEngine::reloadDisassembler() -{ +{ + enum { ContextLines = 40 }; + // Do we have a top stack frame? + const ULONG64 offset = m_d->m_currentStackTrace ? m_d->m_currentStackTrace->instructionOffset() : ULONG64(0); + if (debugCDB) + qDebug() << Q_FUNC_INFO << offset; + + DisassemblerHandler *dh = m_d->m_debuggerManagerAccess->disassemblerHandler(); + if (offset) { + QList<DisassemblerLine> lines; + QString errorMessage; + QApplication::setOverrideCursor(Qt::WaitCursor); + const bool drc = dissassemble(m_d->m_pDebugClient, m_d->m_pDebugControl, offset, + ContextLines, ContextLines, &lines, &errorMessage); + QApplication::restoreOverrideCursor(); + if (drc) { + dh->setLines(lines); + if (lines.size() > ContextLines) + dh->setCurrentLine(ContextLines); + } else { + qWarning("reloadDisassembler: %s\n", qPrintable(errorMessage)); + } + } else { + dh->setLines(QList<DisassemblerLine>()); + } } void CdbDebugEngine::reloadModules() diff --git a/src/plugins/debugger/cdb/cdbdebugoutput.cpp b/src/plugins/debugger/cdb/cdbdebugoutput.cpp index f0df999d28..114c59c771 100644 --- a/src/plugins/debugger/cdb/cdbdebugoutput.cpp +++ b/src/plugins/debugger/cdb/cdbdebugoutput.cpp @@ -27,7 +27,6 @@ ** **************************************************************************/ - #include "cdbdebugoutput.h" #include "cdbdebugengine.h" #include "cdbdebugengine_p.h" @@ -38,12 +37,11 @@ namespace Debugger { namespace Internal { -CdbDebugOutput::CdbDebugOutput(CdbDebugEngine* engine) : - m_pEngine(engine) +CdbDebugOutputBase::CdbDebugOutputBase() { } -STDMETHODIMP CdbDebugOutput::QueryInterface( +STDMETHODIMP CdbDebugOutputBase::QueryInterface( THIS_ IN REFIID InterfaceId, OUT PVOID* Interface @@ -52,9 +50,9 @@ STDMETHODIMP CdbDebugOutput::QueryInterface( *Interface = NULL; if (IsEqualIID(InterfaceId, __uuidof(IUnknown)) || - IsEqualIID(InterfaceId, __uuidof(IDebugOutputCallbacks))) + IsEqualIID(InterfaceId, __uuidof(IDebugOutputCallbacksWide))) { - *Interface = (IDebugOutputCallbacks *)this; + *Interface = (IDebugOutputCallbacksWide*)this; AddRef(); return S_OK; } else { @@ -62,27 +60,43 @@ STDMETHODIMP CdbDebugOutput::QueryInterface( } } -STDMETHODIMP_(ULONG) CdbDebugOutput::AddRef(THIS) +STDMETHODIMP_(ULONG) CdbDebugOutputBase::AddRef(THIS) { // This class is designed to be static so // there's no true refcount. return 1; } -STDMETHODIMP_(ULONG) CdbDebugOutput::Release(THIS) +STDMETHODIMP_(ULONG) CdbDebugOutputBase::Release(THIS) { // This class is designed to be static so // there's no true refcount. return 0; } +STDMETHODIMP CdbDebugOutputBase::Output( + THIS_ + IN ULONG mask, + IN PCWSTR text + ) +{ + output(mask, QString::fromUtf16(text)); + return S_OK; +} + +IDebugOutputCallbacksWide *CdbDebugOutputBase::getOutputCallback(IDebugClient5 *client) +{ + IDebugOutputCallbacksWide *rc; + if (FAILED(client->GetOutputCallbacksWide(&rc))) + return 0; + return rc; +} + +// ------------------------- CdbDebugOutput + // Return a prefix for debugger messages static QString prefix(ULONG mask) { - if (mask & (DEBUG_OUTPUT_DEBUGGEE|DEBUG_OUTPUT_DEBUGGEE_PROMPT|DEBUG_OUTPUT_DEBUGGEE_PROMPT)) { - static const QString p = QLatin1String("target:"); - return p; - } if (mask & (DEBUG_OUTPUT_PROMPT_REGISTERS)) { static const QString p = QLatin1String("registers:"); return p; @@ -103,23 +117,42 @@ static QString prefix(ULONG mask) return commonPrefix; } -STDMETHODIMP CdbDebugOutput::Output( - THIS_ - IN ULONG mask, - IN PCSTR text - ) +enum OutputKind { DebuggerOutput, DebuggerPromptOutput, DebuggeeOutput, DebuggeePromptOutput }; + +static inline OutputKind outputKind(ULONG mask) +{ + if (mask & DEBUG_OUTPUT_DEBUGGEE) + return DebuggeeOutput; + if (mask & DEBUG_OUTPUT_DEBUGGEE_PROMPT) + return DebuggeePromptOutput; + if (mask & DEBUG_OUTPUT_PROMPT) + return DebuggerPromptOutput; + return DebuggerOutput; +} + +CdbDebugOutput::CdbDebugOutput() { - const QString msg = QString::fromLocal8Bit(text); +} +void CdbDebugOutput::output(ULONG mask, const QString &msg) +{ if (debugCDB > 1) qDebug() << Q_FUNC_INFO << "\n " << msg; - if (mask & (DEBUG_OUTPUT_PROMPT|DEBUG_OUTPUT_DEBUGGEE_PROMPT)) { + switch (outputKind(mask)) { + case DebuggerOutput: + debuggerOutput(prefix(mask), msg); + break; + case DebuggerPromptOutput: emit debuggerInputPrompt(prefix(mask), msg); - } else { - emit debuggerOutput(prefix(mask), msg); + break; + case DebuggeeOutput: + emit debuggeeOutput(msg); + break; + case DebuggeePromptOutput: + emit debuggeeInputPrompt(msg); + break; } - return S_OK; } } // namespace Internal diff --git a/src/plugins/debugger/cdb/cdbdebugoutput.h b/src/plugins/debugger/cdb/cdbdebugoutput.h index 827d85bbb2..f13fedac35 100644 --- a/src/plugins/debugger/cdb/cdbdebugoutput.h +++ b/src/plugins/debugger/cdb/cdbdebugoutput.h @@ -38,14 +38,12 @@ namespace Debugger { namespace Internal { -class CdbDebugEngine; +// CdbDebugOutputBase is a base class for output handlers +// that takes care of the Active X magic and conversion to QString. -class CdbDebugOutput : public QObject, public IDebugOutputCallbacks +class CdbDebugOutputBase : public IDebugOutputCallbacksWide { - Q_OBJECT public: - explicit CdbDebugOutput(CdbDebugEngine* engine); - // IUnknown. STDMETHOD(QueryInterface)( THIS_ @@ -63,15 +61,48 @@ public: STDMETHOD(Output)( THIS_ IN ULONG mask, - IN PCSTR text + IN PCWSTR text ); + // Helpers to retrieve the output callbacks IF + static IDebugOutputCallbacksWide *getOutputCallback(IDebugClient5 *client); + +protected: + CdbDebugOutputBase(); + virtual void output(ULONG mask, const QString &message) = 0; +}; + +// Standard CDB output handler +class CdbDebugOutput : public QObject, public CdbDebugOutputBase +{ + Q_OBJECT +public: + CdbDebugOutput(); + +protected: + virtual void output(ULONG mask, const QString &message); + signals: void debuggerOutput(const QString &prefix, const QString &message); void debuggerInputPrompt(const QString &prefix, const QString &message); + void debuggeeOutput(const QString &message); + void debuggeeInputPrompt(const QString &message); +}; + +// An output handler that adds lines to a string (to be +// used for cases in which linebreaks occur in-between calls +// to output). +class StringOutputHandler : public CdbDebugOutputBase +{ +public: + StringOutputHandler() {} + QString result() const { return m_result; } + +protected: + virtual void output(ULONG, const QString &message) { m_result += message; } private: - CdbDebugEngine* m_pEngine; + QString m_result; }; } // namespace Internal diff --git a/src/plugins/debugger/cdb/cdbstacktracecontext.cpp b/src/plugins/debugger/cdb/cdbstacktracecontext.cpp index 0500657dc4..7d207fbba6 100644 --- a/src/plugins/debugger/cdb/cdbstacktracecontext.cpp +++ b/src/plugins/debugger/cdb/cdbstacktracecontext.cpp @@ -40,7 +40,8 @@ namespace Internal { CdbStackTraceContext::CdbStackTraceContext(IDebugSystemObjects4* pDebugSystemObjects, IDebugSymbols3* pDebugSymbols) : m_pDebugSystemObjects(pDebugSystemObjects), - m_pDebugSymbols(pDebugSymbols) + m_pDebugSymbols(pDebugSymbols), + m_instructionOffset(0) { } @@ -95,6 +96,8 @@ bool CdbStackTraceContext::init(unsigned long frameCount, QString * /*errorMessa for (ULONG i=0; i < frameCount; ++i) { StackFrame frame(i); const ULONG64 instructionOffset = m_cdbFrames[i].InstructionOffset; + if (i == 0) + m_instructionOffset = instructionOffset; frame.address = QString::fromLatin1("0x%1").arg(instructionOffset, 0, 16); m_pDebugSymbols->GetNameByOffsetWide(instructionOffset, wszBuf, MAX_PATH, 0, 0); diff --git a/src/plugins/debugger/cdb/cdbstacktracecontext.h b/src/plugins/debugger/cdb/cdbstacktracecontext.h index bb8286859e..4f3be5c5fc 100644 --- a/src/plugins/debugger/cdb/cdbstacktracecontext.h +++ b/src/plugins/debugger/cdb/cdbstacktracecontext.h @@ -66,6 +66,9 @@ public: QList<StackFrame> frames() const { return m_frames; } inline int frameCount() const { return m_frames.size(); } + // Top-Level instruction offset for disassembler + ULONG64 instructionOffset() const { return m_instructionOffset; } + CdbSymbolGroupContext *symbolGroupContextAt(int index, QString *errorMessage); private: @@ -78,6 +81,7 @@ private: DEBUG_STACK_FRAME m_cdbFrames[maxFrames]; QVector <CdbSymbolGroupContext*> m_symbolContexts; QList<StackFrame> m_frames; + ULONG64 m_instructionOffset; }; } diff --git a/src/plugins/debugger/debuggermanager.cpp b/src/plugins/debugger/debuggermanager.cpp index cf2066c790..9597c128c9 100644 --- a/src/plugins/debugger/debuggermanager.cpp +++ b/src/plugins/debugger/debuggermanager.cpp @@ -213,6 +213,8 @@ void DebuggerManager::init() QAbstractItemView *disassemblerView = qobject_cast<QAbstractItemView *>(m_disassemblerWindow); disassemblerView->setModel(m_disassemblerHandler->model()); + connect(m_disassemblerWindow, SIGNAL(reloadDisassemblerRequested()), + this, SLOT(reloadDisassembler())); // Breakpoints m_breakHandler = new BreakHandler; diff --git a/src/plugins/debugger/disassemblerhandler.cpp b/src/plugins/debugger/disassemblerhandler.cpp index 9ef9efec9c..ee03f6cfd5 100644 --- a/src/plugins/debugger/disassemblerhandler.cpp +++ b/src/plugins/debugger/disassemblerhandler.cpp @@ -39,6 +39,14 @@ using namespace Debugger; using namespace Debugger::Internal; +void DisassemblerLine::clear() +{ + address.clear(); + symbol.clear(); + addressDisplay.clear(); + symbolDisplay.clear(); + mnemonic.clear(); +} ////////////////////////////////////////////////////////////////// // diff --git a/src/plugins/debugger/disassemblerhandler.h b/src/plugins/debugger/disassemblerhandler.h index 6c0a5b37ba..4b73e69ddb 100644 --- a/src/plugins/debugger/disassemblerhandler.h +++ b/src/plugins/debugger/disassemblerhandler.h @@ -41,6 +41,8 @@ namespace Internal { class DisassemblerLine { public: + void clear(); + QString address; QString symbol; QString addressDisplay; |