OSDN Git Service

Debugger[New CDB]: Fix disassembly.
authorFriedemann Kleint <Friedemann.Kleint@nokia.com>
Mon, 22 Nov 2010 12:50:40 +0000 (13:50 +0100)
committerFriedemann Kleint <Friedemann.Kleint@nokia.com>
Mon, 22 Nov 2010 12:50:40 +0000 (13:50 +0100)
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
src/libs/qtcreatorcdbext/gdbmihelpers.cpp
src/libs/qtcreatorcdbext/gdbmihelpers.h
src/libs/qtcreatorcdbext/qtcreatorcdbext.def
src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp
src/plugins/debugger/cdb2/cdbengine2.cpp
src/plugins/debugger/cdb2/cdbengine2.h
src/plugins/debugger/cdb2/cdbparsehelpers.cpp
src/plugins/debugger/cdb2/cdbparsehelpers.h

index 285e40c..2eb7344 100644 (file)
@@ -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;
 }
 
index aff59c3..af6f594 100644 (file)
@@ -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();
+}
index 7362266..19e1a59 100644 (file)
@@ -51,6 +51,8 @@ struct StackFrame
     ULONG line;
 };
 
+typedef std::vector<StackFrame> 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
index dce4db7..387d386 100644 (file)
@@ -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<StringList>(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.
index 86d8a0e..25d4d80 100644 (file)
@@ -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) {
index dd4e8e7..669805b 100644 (file)
@@ -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 &);
index 14da284..c2a9bf3 100644 (file)
@@ -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<QByteArray> &in, QList<Debugger::Internal::StackFrame> *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: <hex pid> <hex tid> Suspends count thread environment block add state name
    0  Id: 133c.1374 Suspend: 1 Teb: 000007ff`fffdd000 Unfrozen
index ccaeffe..6ee8b03 100644 (file)
@@ -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<QByteArray> &in, QList<Debugger::Internal::StackFrame> *frames);
-
 QString debugByteArray(const QByteArray &a);
 QString StringFromBase64EncodedUtf16(const QByteArray &a);