From 7fbff9c3afb4619645eddcd4c3d7309a7d882279 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Mon, 22 Nov 2010 13:50:40 +0100 Subject: [PATCH] Debugger[New CDB]: Fix disassembly. Introduce GDBMI-based 'stack' extension command instead of the builtin 'k' as this does not print the correct instruction pointer. --- src/libs/qtcreatorcdbext/eventcallback.cpp | 7 -- src/libs/qtcreatorcdbext/gdbmihelpers.cpp | 61 ++++++++++++++- src/libs/qtcreatorcdbext/gdbmihelpers.h | 10 +++ src/libs/qtcreatorcdbext/qtcreatorcdbext.def | 1 + src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp | 31 ++++++++ src/plugins/debugger/cdb2/cdbengine2.cpp | 89 +++++++++++++++++----- src/plugins/debugger/cdb2/cdbengine2.h | 3 +- src/plugins/debugger/cdb2/cdbparsehelpers.cpp | 69 ----------------- src/plugins/debugger/cdb2/cdbparsehelpers.h | 3 - 9 files changed, 171 insertions(+), 103 deletions(-) diff --git a/src/libs/qtcreatorcdbext/eventcallback.cpp b/src/libs/qtcreatorcdbext/eventcallback.cpp index 285e40caa9..2eb7344a0a 100644 --- a/src/libs/qtcreatorcdbext/eventcallback.cpp +++ b/src/libs/qtcreatorcdbext/eventcallback.cpp @@ -33,7 +33,6 @@ #include "gdbmihelpers.h" static const char eventContextC[] = "event"; -static const char moduleContextC[] = "module"; // Special exception codes (see dbgwinutils.cpp). enum { winExceptionCppException = 0xe06d7363, @@ -239,9 +238,6 @@ STDMETHODIMP EventCallback::LoadModule( __in ULONG TimeDateStamp ) { - ExtensionContext::instance().report('E', 0, moduleContextC, "L:%s:%s:0x%llx:0x%llx\n", - ModuleName, ImageName, BaseOffset, ModuleSize); - return m_wrapped ? m_wrapped->LoadModule(ImageFileHandle, BaseOffset, ModuleSize, ModuleName, ImageName, CheckSum, TimeDateStamp) : S_OK; @@ -253,9 +249,6 @@ STDMETHODIMP EventCallback::UnloadModule( __in ULONG64 BaseOffset ) { - ExtensionContext::instance().report('U', 0, moduleContextC, "U:%s\n", - ImageBaseName); - return m_wrapped ? m_wrapped->UnloadModule(ImageBaseName, BaseOffset) : S_OK; } diff --git a/src/libs/qtcreatorcdbext/gdbmihelpers.cpp b/src/libs/qtcreatorcdbext/gdbmihelpers.cpp index aff59c3a0e..af6f59442c 100644 --- a/src/libs/qtcreatorcdbext/gdbmihelpers.cpp +++ b/src/libs/qtcreatorcdbext/gdbmihelpers.cpp @@ -48,8 +48,18 @@ void StackFrame::formatGDBMI(std::ostream &str, unsigned level) const { str << "frame={level=\"" << level << "\",addr=\"0x" << std::hex << address << std::dec << '"'; - if (!function.empty()) - str << ",func=\"" << gdbmiWStringFormat(function) << '"'; + if (!function.empty()) { + // Split into module/function + const std::wstring::size_type exclPos = function.find('!'); + if (exclPos == std::wstring::npos) { + str << ",func=\"" << gdbmiWStringFormat(function) << '"'; + } else { + const std::wstring module = function.substr(0, exclPos); + const std::wstring fn = function.substr(exclPos + 1, function.size() - exclPos - 1); + str << ",func=\"" << gdbmiWStringFormat(fn) + << "\",from=\"" << gdbmiWStringFormat(module) << '"'; + } + } if (!fullPathName.empty()) { // Creator/gdbmi expects 'clean paths' std::wstring cleanPath = fullPathName; replace(cleanPath, L'\\', L'/'); @@ -559,3 +569,50 @@ std::string memoryToBase64(CIDebugDataSpaces *ds, ULONG64 address, ULONG length, delete [] buffer; return str.str(); } + +// Format stack as GDBMI +static StackFrames getStackTrace(CIDebugControl *debugControl, + CIDebugSymbols *debugSymbols, + unsigned maxFrames, + std::string *errorMessage) +{ + + if (!maxFrames) + return StackFrames(); + DEBUG_STACK_FRAME *frames = new DEBUG_STACK_FRAME[maxFrames]; + ULONG frameCount = 0; + const HRESULT hr = debugControl->GetStackTrace(0, 0, 0, frames, maxFrames, &frameCount); + if (FAILED(hr)) { + delete [] frames; + *errorMessage = msgDebugEngineComFailed("GetStackTrace", hr); + } + StackFrames rc(frameCount, StackFrame()); + for (ULONG f = 0; f < frameCount; f++) + getFrame(debugSymbols, frames[f], &(rc[f])); + delete [] frames; + return rc; +} + +std::string gdbmiStack(CIDebugControl *debugControl, + CIDebugSymbols *debugSymbols, + unsigned maxFrames, + bool humanReadable, std::string *errorMessage) +{ + const StackFrames frames = getStackTrace(debugControl, debugSymbols, + maxFrames, errorMessage); + if (frames.empty() && maxFrames > 0) + return std::string(); + + std::ostringstream str; + str << '['; + const StackFrames::size_type size = frames.size(); + for (StackFrames::size_type i = 0; i < size; i++) { + if (i) + str << ','; + frames.at(i).formatGDBMI(str, (int)i); + if (humanReadable) + str << '\n'; + } + str << ']'; + return str.str(); +} diff --git a/src/libs/qtcreatorcdbext/gdbmihelpers.h b/src/libs/qtcreatorcdbext/gdbmihelpers.h index 73622668cc..19e1a59321 100644 --- a/src/libs/qtcreatorcdbext/gdbmihelpers.h +++ b/src/libs/qtcreatorcdbext/gdbmihelpers.h @@ -51,6 +51,8 @@ struct StackFrame ULONG line; }; +typedef std::vector StackFrames; + bool getFrame(unsigned n, StackFrame *f, std::string *errorMessage); bool getFrame(CIDebugSymbols *debugSymbols, CIDebugControl *debugControl, unsigned n, StackFrame *f, std::string *errorMessage); @@ -145,4 +147,12 @@ std::string gdbmiRegisters(CIDebugRegisters *regs, std::string memoryToBase64(CIDebugDataSpaces *ds, ULONG64 address, ULONG length, std::string *errorMessage); +// Stack helpers +StackFrames getStackTrace(CIDebugControl *debugControl, CIDebugSymbols *debugSymbols, + unsigned maxFrames, std::string *errorMessage); + +std::string gdbmiStack(CIDebugControl *debugControl, CIDebugSymbols *debugSymbols, + unsigned maxFrames, bool humanReadable, + std::string *errorMessage); + #endif // THREADLIST_H diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbext.def b/src/libs/qtcreatorcdbext/qtcreatorcdbext.def index 6f71c02b34..0187da13eb 100644 --- a/src/libs/qtcreatorcdbext/qtcreatorcdbext.def +++ b/src/libs/qtcreatorcdbext/qtcreatorcdbext.def @@ -13,4 +13,5 @@ idle help memory shutdownex +stack KnownStructOutput diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp index dce4db7035..387d386c6a 100644 --- a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp +++ b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp @@ -351,6 +351,37 @@ extern "C" HRESULT CALLBACK memory(CIDebugClient *Client, PCSTR argsIn) return S_OK; } +// Extension command 'stack' +// Report stack correctly as 'k' does not list instruction pointer +// correctly. +extern "C" HRESULT CALLBACK stack(CIDebugClient *Client, PCSTR argsIn) +{ + ExtensionCommandContext exc(Client); + std::string errorMessage; + + int token; + bool humanReadable = false; + unsigned maxFrames = 1000; + + StringList tokens = commandTokens(argsIn, &token); + if (!tokens.empty() && tokens.front() == "-h") { + humanReadable = true; + tokens.pop_front(); + } + if (!tokens.empty()) + integerFromString(tokens.front(), &maxFrames); + + const std::string stack = gdbmiStack(exc.control(), exc.symbols(), + maxFrames, humanReadable, &errorMessage); + + if (stack.empty()) { + ExtensionContext::instance().report('N', token, "stack", errorMessage.c_str()); + } else { + ExtensionContext::instance().report('R', token, "stack", stack.c_str()); + } + return S_OK; +} + // Extension command 'shutdownex' (shutdown is reserved): // Unhook the output callbacks. This is normally done by the session // inaccessible notification, however, this does not work for remote-controlled sessions. diff --git a/src/plugins/debugger/cdb2/cdbengine2.cpp b/src/plugins/debugger/cdb2/cdbengine2.cpp index 86d8a0e5e5..25d4d8055a 100644 --- a/src/plugins/debugger/cdb2/cdbengine2.cpp +++ b/src/plugins/debugger/cdb2/cdbengine2.cpp @@ -308,8 +308,22 @@ CdbEngine::~CdbEngine() void CdbEngine::operateByInstructionTriggered(bool operateByInstruction) { - // To be set next time session becomes accessible - m_operateByInstructionPending = operateByInstruction; + if (state() == InferiorStopOk) { + syncOperateByInstruction(operateByInstruction); + } else { + // To be set next time session becomes accessible + m_operateByInstructionPending = operateByInstruction; + } +} + +void CdbEngine::syncOperateByInstruction(bool operateByInstruction) +{ + if (m_operateByInstruction == operateByInstruction) + return; + QTC_ASSERT(m_accessible, return; ) + m_operateByInstruction = operateByInstruction; + postCommand(m_operateByInstruction ? QByteArray("l-t") : QByteArray("l+t"), 0); + postCommand(m_operateByInstruction ? QByteArray("l-s") : QByteArray("l+s"), 0); } void CdbEngine::setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos) @@ -1197,11 +1211,7 @@ void CdbEngine::handleSessionIdle(const QByteArray &message) stateName(state()), m_specialStopMode); // Switch source level debugging - if (m_operateByInstructionPending != m_operateByInstruction) { - m_operateByInstruction = m_operateByInstructionPending; - postCommand(m_operateByInstruction ? QByteArray("l-t") : QByteArray("l+t"), 0); - postCommand(m_operateByInstruction ? QByteArray("l-s") : QByteArray("l+s"), 0); - } + syncOperateByInstruction(m_operateByInstructionPending); const SpecialStopMode specialStopMode = m_specialStopMode; m_specialStopMode = NoSpecialStop; @@ -1704,21 +1714,58 @@ QString CdbEngine::normalizeFileName(const QString &f) return normalized; } -void CdbEngine::handleStackTrace(const CdbBuiltinCommandPtr &command) -{ - StackFrames frames; - const int current = parseCdbStackTrace(command->reply, &frames); - if (debug) - qDebug("handleStackTrace %d of %d", current, frames.size()); - const StackFrames::iterator end = frames.end(); - for (StackFrames::iterator it = frames.begin(); it != end; ++it) { - if (!it->file.isEmpty()) - it->file = QDir::cleanPath(normalizeFileName(it->file)); +// Parse frame from GDBMI. Duplicate of the gdb code, but that +// has more processing. +static StackFrames parseFrames(const QByteArray &data) +{ + GdbMi gdbmi; + gdbmi.fromString(data); + if (!gdbmi.isValid()) + return StackFrames(); + + StackFrames rc; + const int count = gdbmi.childCount(); + rc.reserve(count); + for (int i = 0; i < count; i++) { + const GdbMi &frameMi = gdbmi.childAt(i); + StackFrame frame; + frame.level = i; + const GdbMi fullName = frameMi.findChild("fullname"); + if (fullName.isValid()) { + frame.file = QFile::decodeName(fullName.data()); + frame.line = frameMi.findChild("line").data().toInt(); + } + frame.function = QLatin1String(frameMi.findChild("func").data()); + frame.from = QLatin1String(frameMi.findChild("from").data()); + frame.address = frameMi.findChild("addr").data().toULongLong(0, 16); + rc.push_back(frame); } + return rc; +} - stackHandler()->setFrames(frames); - activateFrame(current); - postCommandSequence(command->commandSequence); +void CdbEngine::handleStackTrace(const CdbExtensionCommandPtr &command) +{ + // Parse frames, find current. + if (command->success) { + int current = -1; + StackFrames frames = parseFrames(command->reply); + const int count = frames.size(); + for (int i = 0; i < count; i++) { + if (!frames.at(i).file.isEmpty()) { + frames[i].file = QDir::cleanPath(normalizeFileName(frames.at(i).file)); + if (current == -1) + current = i; + } + } + if (count && current == -1) // No usable frame, use assembly. + current = 0; + // Set + stackHandler()->setFrames(frames); + activateFrame(current); + postCommandSequence(command->commandSequence); + } else { + showMessage(command->errorMessage, LogError); + } } void CdbEngine::dummyHandler(const CdbBuiltinCommandPtr &command) @@ -1739,7 +1786,7 @@ void CdbEngine::postCommandSequence(unsigned mask) return; } if (mask & CommandListStack) { - postBuiltinCommand("k", 0, &CdbEngine::handleStackTrace, mask & ~CommandListStack); + postExtensionCommand("stack", QByteArray(), 0, &CdbEngine::handleStackTrace, mask & ~CommandListStack); return; } if (mask & CommandListRegisters) { diff --git a/src/plugins/debugger/cdb2/cdbengine2.h b/src/plugins/debugger/cdb2/cdbengine2.h index dd4e8e7f27..669805b825 100644 --- a/src/plugins/debugger/cdb2/cdbengine2.h +++ b/src/plugins/debugger/cdb2/cdbengine2.h @@ -152,10 +152,11 @@ private: inline void parseOutputLine(QByteArray line); inline bool isCdbProcessRunning() const { return m_process.state() != QProcess::NotRunning; } bool canInterruptInferior() const; + void syncOperateByInstruction(bool operateByInstruction); // Builtin commands void dummyHandler(const CdbBuiltinCommandPtr &); - void handleStackTrace(const CdbBuiltinCommandPtr &); + void handleStackTrace(const CdbExtensionCommandPtr &); void handleRegisters(const CdbBuiltinCommandPtr &); void handleDisassembler(const CdbBuiltinCommandPtr &); void handleJumpToLineAddressResolution(const CdbBuiltinCommandPtr &); diff --git a/src/plugins/debugger/cdb2/cdbparsehelpers.cpp b/src/plugins/debugger/cdb2/cdbparsehelpers.cpp index 14da284c21..c2a9bf3439 100644 --- a/src/plugins/debugger/cdb2/cdbparsehelpers.cpp +++ b/src/plugins/debugger/cdb2/cdbparsehelpers.cpp @@ -154,75 +154,6 @@ QVariant cdbIntegerValue(const QByteArray &t) return converted; } -/* Parse: 64bit: -\code -Child-SP RetAddr Call Site -00000000`0012a290 00000000`70deb844 QtCored4!QString::QString+0x18 [c:\qt\src\corelib\tools\qstring.h @ 729] -\endcode 32bit: -\code -ChildEBP RetAddr -0012cc68 6714d114 QtCored4!QString::QString+0xf [d:\dev\qt4.7-vs8\qt\src\corelib\tools\qstring.h @ 729] -\endcode */ - -static inline bool isHexDigit(char c) -{ - return std::isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); -} - -static inline bool parseStackFrame(QByteArray line, Debugger::Internal::StackFrame *frame) -{ - frame->clear(); - if (line.isEmpty() || line.startsWith("Child") || !isHexDigit(line.at(0))) - return false; - if (line.endsWith(']')) { - const int sourceFilePos = line.lastIndexOf('['); - const int sepPos = line.lastIndexOf(" @ "); - if (sourceFilePos != -1 && sepPos != -1) { - const QString fileName = QString::fromLocal8Bit(line.mid(sourceFilePos + 1, sepPos - sourceFilePos - 1)); - frame->file = QDir::cleanPath(fileName); - frame->line = line.mid(sepPos + 3, line.size() - sepPos - 4).toInt(); - line.truncate(sourceFilePos - 1); - } - } - // Split address tokens - const int retAddrPos = line.indexOf(' '); - const int symbolPos = retAddrPos != -1 ? line.indexOf(' ', retAddrPos + 1) : -1; - if (symbolPos == -1) - return false; - - // Remove offset off symbol - const int offsetPos = line.lastIndexOf("+0x"); - if (offsetPos != -1) - line.truncate(offsetPos); - - frame->address = cdbIntegerValue(line.mid(0, retAddrPos)).toULongLong(); - // Module!foo - frame->function = QString::fromAscii(line.mid(symbolPos)); - const int moduleSep = frame->function.indexOf(QLatin1Char('!')); - if (moduleSep != -1) { - frame->from = frame->function.left(moduleSep); - frame->function.remove(0, moduleSep + 1); - } - return true; -} - -int parseCdbStackTrace(const QList &in, QList *frames) -{ - frames->clear(); - Debugger::Internal::StackFrame frame; - frames->reserve(in.size()); - int level = 0; - int current = -1; - foreach(const QByteArray &line, in) - if (parseStackFrame(line, &frame)) { - frame.level = level++; - if (current == -1 && frame.isUsable()) - current = frames->size(); - frames->push_back(frame); - } - return current; -} - /* \code 0:002> ~ [Debugger-Id] Id: Suspends count thread environment block add state name 0 Id: 133c.1374 Suspend: 1 Teb: 000007ff`fffdd000 Unfrozen diff --git a/src/plugins/debugger/cdb2/cdbparsehelpers.h b/src/plugins/debugger/cdb2/cdbparsehelpers.h index ccaeffe2b1..6ee8b03d04 100644 --- a/src/plugins/debugger/cdb2/cdbparsehelpers.h +++ b/src/plugins/debugger/cdb2/cdbparsehelpers.h @@ -63,9 +63,6 @@ QByteArray fixCdbIntegerValue(QByteArray t, bool stripLeadingZeros = false, int // Convert a CDB integer value into quint64 or int64 QVariant cdbIntegerValue(const QByteArray &t); -// Parse stack frames and return current -int parseCdbStackTrace(const QList &in, QList *frames); - QString debugByteArray(const QByteArray &a); QString StringFromBase64EncodedUtf16(const QByteArray &a); -- 2.11.0