From 285d216beddb9c4d8e65145e13e3e3203f4d615c Mon Sep 17 00:00:00 2001
From: Friedemann Kleint <Friedemann.Kleint@nokia.com>
Date: Wed, 2 Feb 2011 13:45:40 +0100
Subject: [PATCH] Debuggeri[CDB]: Add breakpoint command.

Report back breakpoints with modules.
---
 src/libs/qtcreatorcdbext/common.cpp           | 18 +++++
 src/libs/qtcreatorcdbext/common.h             |  1 +
 src/libs/qtcreatorcdbext/eventcallback.cpp    |  2 +-
 src/libs/qtcreatorcdbext/extensioncontext.cpp | 12 +++
 src/libs/qtcreatorcdbext/extensioncontext.h   |  1 +
 src/libs/qtcreatorcdbext/gdbmihelpers.cpp     | 81 +++++++++++++++++++
 src/libs/qtcreatorcdbext/gdbmihelpers.h       |  7 ++
 src/libs/qtcreatorcdbext/qtcreatorcdbext.def  |  1 +
 .../qtcreatorcdbext/qtcreatorcdbextension.cpp | 30 +++++++
 src/libs/qtcreatorcdbext/symbolgroupvalue.cpp | 16 +---
 10 files changed, 155 insertions(+), 14 deletions(-)

diff --git a/src/libs/qtcreatorcdbext/common.cpp b/src/libs/qtcreatorcdbext/common.cpp
index 18f2973a229..097264a4467 100644
--- a/src/libs/qtcreatorcdbext/common.cpp
+++ b/src/libs/qtcreatorcdbext/common.cpp
@@ -122,3 +122,21 @@ ULONG currentProcessId(CIDebugClient *client)
         return currentProcessId(sysObjects.data());
     return 0;
 }
+
+std::string moduleNameByOffset(CIDebugSymbols *symbols, ULONG64 offset)
+{
+    enum { BufSize = 512 };
+    ULONG index = 0;
+    ULONG64 base = 0;
+    // Convert module base address to module index
+    HRESULT hr = symbols->GetModuleByOffset(offset, 0, &index, &base);
+    if (FAILED(hr))
+        return std::string();
+    // Obtain module name
+    char buf[BufSize];
+    buf[0] = '\0';
+    hr = symbols->GetModuleNameString(DEBUG_MODNAME_MODULE, index, base, buf, BufSize, 0);
+    if (FAILED(hr))
+        return std::string();
+    return std::string(buf);
+}
diff --git a/src/libs/qtcreatorcdbext/common.h b/src/libs/qtcreatorcdbext/common.h
index f13a880a347..4d4075ab5b9 100644
--- a/src/libs/qtcreatorcdbext/common.h
+++ b/src/libs/qtcreatorcdbext/common.h
@@ -74,6 +74,7 @@ ULONG currentThreadId(IDebugSystemObjects *sysObjects);
 ULONG currentThreadId(CIDebugClient *client);
 ULONG currentProcessId(IDebugSystemObjects *sysObjects);
 ULONG currentProcessId(CIDebugClient *client);
+std::string moduleNameByOffset(CIDebugSymbols *symbols, ULONG64 offset);
 
 #ifdef QTC_TRACE
 #  define QTC_TRACE_IN dprintf(">%s\n", __FUNCTION__);
diff --git a/src/libs/qtcreatorcdbext/eventcallback.cpp b/src/libs/qtcreatorcdbext/eventcallback.cpp
index e2799a959eb..d04ad11a57d 100644
--- a/src/libs/qtcreatorcdbext/eventcallback.cpp
+++ b/src/libs/qtcreatorcdbext/eventcallback.cpp
@@ -172,7 +172,7 @@ STDMETHODIMP EventCallback::Exception(
 
     std::ostringstream str;
     formatGdbmiHash(str, parameters);
-    ExtensionContext::instance().setStopReason(parameters, "exception");
+    ExtensionContext::instance().setStopReason(parameters, ExtensionContext::breakPointStopReasonC);
     ExtensionContext::instance().report('E', 0, 0, "exception", "%s", str.str().c_str());
     return m_wrapped ? m_wrapped->Exception(Ex, FirstChance) : S_OK;
 }
diff --git a/src/libs/qtcreatorcdbext/extensioncontext.cpp b/src/libs/qtcreatorcdbext/extensioncontext.cpp
index 8d5b07affd4..965aac037aa 100644
--- a/src/libs/qtcreatorcdbext/extensioncontext.cpp
+++ b/src/libs/qtcreatorcdbext/extensioncontext.cpp
@@ -45,6 +45,7 @@
 WINDBG_EXTENSION_APIS   ExtensionApis = {sizeof(WINDBG_EXTENSION_APIS), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 
 const char *ExtensionContext::stopReasonKeyC = "reason";
+const char *ExtensionContext::breakPointStopReasonC = "breakpoint";
 
 ExtensionContext::ExtensionContext() :
     m_hookedClient(0),
@@ -197,6 +198,17 @@ void ExtensionContext::notifyIdleCommand(CIDebugClient *client)
         } else {
             str << ",stack=" << stackInfo;
         }
+        // Report breakpoints
+        const StopReasonMap::const_iterator rit = stopReasons.find(stopReasonKeyC);
+        if (rit != stopReasons.end() && rit->second == breakPointStopReasonC) {
+            const std::string breakpoints = gdbmiBreakpoints(exc.control(), exc.symbols(),
+                                                             false, false, &errorMessage);
+            if (breakpoints.empty()) {
+                str << ",breakpointserror=" << gdbmiStringFormat(errorMessage);
+            } else {
+                str << ",breakpoints=" << breakpoints;
+            }
+        }
         str << '}';
         reportLong('E', 0, "session_idle", str.str());
     }
diff --git a/src/libs/qtcreatorcdbext/extensioncontext.h b/src/libs/qtcreatorcdbext/extensioncontext.h
index abeeb2cbe73..f844783a592 100644
--- a/src/libs/qtcreatorcdbext/extensioncontext.h
+++ b/src/libs/qtcreatorcdbext/extensioncontext.h
@@ -56,6 +56,7 @@ public:
 
     // Key used to report stop reason in StopReasonMap
     static const char *stopReasonKeyC;
+    static const char *breakPointStopReasonC;  // pre-defined stop reasons
     // Map of parameters reported with the next stop as GDBMI
     typedef std::map<std::string, std::string> StopReasonMap;
 
diff --git a/src/libs/qtcreatorcdbext/gdbmihelpers.cpp b/src/libs/qtcreatorcdbext/gdbmihelpers.cpp
index 6826df9b83a..e3c45f178ec 100644
--- a/src/libs/qtcreatorcdbext/gdbmihelpers.cpp
+++ b/src/libs/qtcreatorcdbext/gdbmihelpers.cpp
@@ -670,3 +670,84 @@ std::string widgetAt(const SymbolGroupValueContext &ctx, int x, int y, std::stri
         wOutput.erase(sepPos, 1);
     return wStringToString(wOutput);
 }
+
+static inline void formatGdbmiFlag(std::ostream &str, const char *name, bool v)
+{
+    str << name << "=\"" << (v ? "true" : "false") << '"';
+}
+
+static bool gdbmiFormatBreakpoint(std::ostream &str,
+                                  IDebugBreakpoint *bp,
+                                  CIDebugSymbols *symbols  /* = 0 */,
+                                  bool verbose, std::string *errorMessage)
+{
+    enum { BufSize = 512 };
+    ULONG64 offset = 0;
+    ULONG flags = 0;
+    HRESULT hr = bp->GetFlags(&flags);
+    if (FAILED(hr)) {
+        *errorMessage = msgDebugEngineComFailed("GetFlags", hr);
+        return false;
+    }
+    const bool deferred = (flags & DEBUG_BREAKPOINT_DEFERRED) != 0;
+    formatGdbmiFlag(str, ",deferred", deferred);
+    if (verbose) {
+        formatGdbmiFlag(str, ",enabled", (flags & DEBUG_BREAKPOINT_ENABLED) != 0);
+        formatGdbmiFlag(str, ",oneshot", (flags & DEBUG_BREAKPOINT_ONE_SHOT) != 0);
+        str << ",flags=\"" << flags << '"';
+        ULONG threadId = 0;
+        if (SUCCEEDED(bp->GetMatchThreadId(&threadId))) // Fails if none set
+            str << ",thread=\"" << threadId << '"';
+        ULONG passCount = 0;
+        if (SUCCEEDED(bp->GetPassCount(&passCount)))
+            str << ",passcount=\"" << passCount << '"';
+    }
+    // Offset: Fails for deferred ones
+    if (!deferred && SUCCEEDED(bp->GetOffset(&offset))) {
+        str << ",address=\"" << std::hex << std::showbase << offset
+            << std::dec << std::noshowbase << '"';
+        if (symbols) {
+            const std::string module = moduleNameByOffset(symbols, offset);
+            if (!module.empty())
+                str << ",module=\"" << module << '"';
+        }
+    }
+    // Expression
+    char buf[BufSize];
+    if (SUCCEEDED(bp->GetOffsetExpression(buf, BUFSIZ, 0)))
+        str << ",expression=\"" << gdbmiStringFormat(buf) << '"';
+    return true;
+}
+
+// Format breakpoints as GDBMI
+std::string gdbmiBreakpoints(CIDebugControl *ctrl,
+                             CIDebugSymbols *symbols /* = 0 */,
+                             bool humanReadable, bool verbose, std::string *errorMessage)
+{
+    ULONG breakPointCount = 0;
+    HRESULT hr = ctrl->GetNumberBreakpoints(&breakPointCount);
+    if (FAILED(hr)) {
+        *errorMessage = msgDebugEngineComFailed("GetNumberBreakpoints", hr);
+        return std::string();
+    }
+    std::ostringstream str;
+    str << '[';
+    if (humanReadable)
+        str << '\n';
+    for (ULONG i = 0; i < breakPointCount; i++) {
+        str << "{id=\"" << i << '"';
+        IDebugBreakpoint *bp = 0;
+        hr = ctrl->GetBreakpointByIndex(i, &bp);
+        if (FAILED(hr) || !bp) {
+            *errorMessage = msgDebugEngineComFailed("GetBreakpointByIndex", hr);
+            return std::string();
+        }
+        if (!gdbmiFormatBreakpoint(str, bp, symbols, verbose, errorMessage))
+            return std::string();
+        str << '}';
+        if (humanReadable)
+            str << '\n';
+    }
+    str << ']';
+    return str.str();
+}
diff --git a/src/libs/qtcreatorcdbext/gdbmihelpers.h b/src/libs/qtcreatorcdbext/gdbmihelpers.h
index 849a05faebb..54c475bd9e8 100644
--- a/src/libs/qtcreatorcdbext/gdbmihelpers.h
+++ b/src/libs/qtcreatorcdbext/gdbmihelpers.h
@@ -113,6 +113,13 @@ Modules getModules(CIDebugSymbols *syms, std::string *errorMessage);
 // Format modules as GDBMI
 std::string gdbmiModules(CIDebugSymbols *syms, bool humanReadable, std::string *errorMessage);
 
+// Format breakpoints as GDBMI
+std::string gdbmiBreakpoints(CIDebugControl *ctrl,
+                             CIDebugSymbols *symbols /* = 0 */,
+                             bool humanReadable,
+                             bool verbose,
+                             std::string *errorMessage);
+
 /* Helpers for registers */
 struct Register
 {
diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbext.def b/src/libs/qtcreatorcdbext/qtcreatorcdbext.def
index bc91011c9b5..ab4cf4ea5c8 100644
--- a/src/libs/qtcreatorcdbext/qtcreatorcdbext.def
+++ b/src/libs/qtcreatorcdbext/qtcreatorcdbext.def
@@ -21,4 +21,5 @@ test
 stack
 addwatch
 widgetat
+breakpoints
 KnownStructOutput
diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp
index 45dccd50d90..59d6a3c2f2e 100644
--- a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp
+++ b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp
@@ -101,6 +101,7 @@ enum Command {
     CmdShutdownex,
     CmdAddWatch,
     CmdWidgetAt,
+    CmdBreakPoints,
     CmdTest
 };
 
@@ -158,6 +159,7 @@ static const CommandDescription commandDescriptions[] = {
 {"shutdownex","Unhooks output callbacks.\nNeeds to be called explicitly only in case of remote debugging.",""},
 {"addwatch","Add watch expression","<iname> <expression>"},
 {"widgetat","Return address of widget at position","<x> <y>"},
+{"breakpoints","List breakpoints with modules","[-h] [-v]"},
 {"test","Testing command","-T type | -w watch-expression"}
 };
 
@@ -984,6 +986,34 @@ extern "C" HRESULT CALLBACK widgetat(CIDebugClient *client, PCSTR argsIn)
     return S_OK;
 }
 
+extern "C" HRESULT CALLBACK breakpoints(CIDebugClient *client, PCSTR argsIn)
+{
+    ExtensionCommandContext exc(client);
+    int token;
+    std::string errorMessage;
+    bool humanReadable = false;
+    bool verbose = false;
+    StringList tokens = commandTokens<StringList>(argsIn, &token);
+    while (!tokens.empty() && tokens.front().size() == 2 && tokens.front().at(0) == '-') {
+        switch (tokens.front().at(1)) {
+        case 'h':
+            humanReadable = true;
+            break;
+        case 'v':
+            verbose = true;
+            break;
+        }
+        tokens.pop_front();
+    }
+    const std::string bp = gdbmiBreakpoints(exc.control(), exc.symbols(), humanReadable, verbose, &errorMessage);
+    if (bp.empty()) {
+        ExtensionContext::instance().report('N', token, 0, "breakpoints", errorMessage.c_str());
+    } else {
+        ExtensionContext::instance().reportLong('R', token, "breakpoints", bp);
+    }
+    return S_OK;
+}
+
 extern "C" HRESULT CALLBACK test(CIDebugClient *client, PCSTR argsIn)
 {
     enum Mode { Invalid, TestType, TestFixWatchExpression };
diff --git a/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp b/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp
index 3654e42a43c..660cc837d1f 100644
--- a/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp
+++ b/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp
@@ -600,20 +600,10 @@ std::string SymbolGroupValue::resolveType(const std::string &typeIn,
     const ULONG typeSize = Ioctl(IG_GET_TYPE_SIZE, &symParameters, symParameters.size);
     if (!typeSize || !symParameters.ModBase) // Failed?
         return stripped;
-    ULONG index = 0;
-    ULONG64 base = 0;
-    // Convert module base address to module index
-    HRESULT hr = ctx.symbols->GetModuleByOffset(symParameters.ModBase, 0, &index, &base);
-    if (FAILED(hr))
+    const std::string module = moduleNameByOffset(ctx.symbols, symParameters.ModBase);
+    if (module.empty())
         return stripped;
-    // Obtain module name
-    char buf[BufSize];
-    buf[0] = '\0';
-    hr = ctx.symbols->GetModuleNameString(DEBUG_MODNAME_MODULE, index, base, buf, BufSize, 0);
-    if (FAILED(hr))
-        return stripped;
-
-    std::string rc = buf;
+    std::string rc = module;
     rc.push_back('!');
     rc += stripped;
     return rc;
-- 
GitLab