diff options
-rw-r--r-- | src/libs/qtcreatorcdbext/eventcallback.cpp | 45 | ||||
-rw-r--r-- | src/libs/qtcreatorcdbext/extensioncontext.h | 2 | ||||
-rw-r--r-- | src/libs/qtcreatorcdbext/gdbmihelpers.cpp | 95 | ||||
-rw-r--r-- | src/libs/qtcreatorcdbext/gdbmihelpers.h | 11 | ||||
-rw-r--r-- | src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp | 3 |
5 files changed, 120 insertions, 36 deletions
diff --git a/src/libs/qtcreatorcdbext/eventcallback.cpp b/src/libs/qtcreatorcdbext/eventcallback.cpp index 1e98bd4d3f0..d7e25b4d00e 100644 --- a/src/libs/qtcreatorcdbext/eventcallback.cpp +++ b/src/libs/qtcreatorcdbext/eventcallback.cpp @@ -50,6 +50,8 @@ enum { winExceptionCppException = 0xe06d7363, winExceptionAppInitFailed = 0xc0000143 }; +typedef ExtensionContext::StopReasonMap StopReasonMap; + /*! \class IDebugEventCallbacks @@ -109,20 +111,41 @@ STDMETHODIMP EventCallback::GetInterestMask(THIS_ __out PULONG mask) return S_OK; } -STDMETHODIMP EventCallback::Breakpoint(THIS_ __in PDEBUG_BREAKPOINT b) +// Fill a map with parameters to be reported for a breakpoint stop. +static StopReasonMap breakPointStopReasonParameters(PDEBUG_BREAKPOINT b) { - // Breakpoint hit - Set the stop reason parameters on the extension context. - typedef ExtensionContext::StopReasonMap StopReasonMap; - typedef ExtensionContext::StopReasonMap::value_type StopReasonMapValue; + typedef StopReasonMap::value_type StopReasonMapValue; + StopReasonMap rc; ULONG uId = 0; - ULONG64 address = 0; - StopReasonMap stopReason; - if (SUCCEEDED(b->GetId(&uId))) - stopReason.insert(StopReasonMapValue(std::string("breakpointId"), toString(uId))); - if (SUCCEEDED(b->GetOffset(&address))) - stopReason.insert(StopReasonMapValue(std::string("breakpointAddress"), toString(address))); - ExtensionContext::instance().setStopReason(stopReason, ExtensionContext::breakPointStopReasonC); + if (FAILED(b->GetId(&uId))) + return rc; + rc.insert(StopReasonMapValue(std::string("breakpointId"), toString(uId))); + const std::pair<ULONG64, ULONG> memoryRange = breakPointMemoryRange(b); + if (!memoryRange.first) + return rc; + rc.insert(StopReasonMapValue(std::string("breakpointAddress"), toString(memoryRange.first))); + // Report the memory for data breakpoints allowing for watching for changed bits + // on the client side. + if (!memoryRange.second) + return rc; + // Try to grab a IDataSpace from somewhere to get the memory + if (CIDebugClient *client = ExtensionContext::instance().hookedClient()) { + IInterfacePointer<CIDebugDataSpaces> dataSpaces(client); + if (dataSpaces) { + const std::wstring memoryHex = memoryToHexW(dataSpaces.data(), memoryRange.first, memoryRange.second); + if (!memoryHex.empty()) + rc.insert(StopReasonMapValue("memory", wStringToString(memoryHex))); + } // dataSpaces + } // client + return rc; +} + +STDMETHODIMP EventCallback::Breakpoint(THIS_ __in PDEBUG_BREAKPOINT b) +{ + // Breakpoint hit - Set the stop reason parameters on the extension context. + ExtensionContext::instance().setStopReason(breakPointStopReasonParameters(b), + ExtensionContext::breakPointStopReasonC); return m_wrapped ? m_wrapped->Breakpoint(b) : S_OK; } diff --git a/src/libs/qtcreatorcdbext/extensioncontext.h b/src/libs/qtcreatorcdbext/extensioncontext.h index f844783a592..5b98ac374d7 100644 --- a/src/libs/qtcreatorcdbext/extensioncontext.h +++ b/src/libs/qtcreatorcdbext/extensioncontext.h @@ -107,6 +107,8 @@ public: // Execute a function call and record the output. bool call(const std::string &functionCall, std::wstring *output, std::string *errorMessage); + CIDebugClient *hookedClient() const { return m_hookedClient; } + private: bool isInitialized() const; diff --git a/src/libs/qtcreatorcdbext/gdbmihelpers.cpp b/src/libs/qtcreatorcdbext/gdbmihelpers.cpp index fc3b747e5d0..f08dc8f3075 100644 --- a/src/libs/qtcreatorcdbext/gdbmihelpers.cpp +++ b/src/libs/qtcreatorcdbext/gdbmihelpers.cpp @@ -550,7 +550,9 @@ std::string gdbmiRegisters(CIDebugRegisters *regs, return str.str(); } -std::string memoryToBase64(CIDebugDataSpaces *ds, ULONG64 address, ULONG length, std::string *errorMessage) +// Read memory and return allocated array +unsigned char *readMemory(CIDebugDataSpaces *ds, ULONG64 address, ULONG length, + std::string *errorMessage = 0) { unsigned char *buffer = new unsigned char[length]; std::fill(buffer, buffer + length, 0); @@ -558,22 +560,43 @@ std::string memoryToBase64(CIDebugDataSpaces *ds, ULONG64 address, ULONG length, const HRESULT hr = ds->ReadVirtual(address, buffer, length, &received); if (FAILED(hr)) { delete [] buffer; - std::ostringstream estr; - estr << "Cannot read " << length << " bytes from " << address << ": " - << msgDebugEngineComFailed("ReadVirtual", hr); - *errorMessage = estr.str(); - return std::string(); + if (errorMessage) { + std::ostringstream estr; + estr << "Cannot read " << length << " bytes from " << address << ": " + << msgDebugEngineComFailed("ReadVirtual", hr); + *errorMessage = estr.str(); + } + return 0; } - if (received < length) { + if (received < length && errorMessage) { std::ostringstream estr; estr << "Warning: Received only " << received << " bytes of " << length << " requested at " << address << '.'; *errorMessage = estr.str(); } + return buffer; +} - std::ostringstream str; - base64Encode(str, buffer, length); - delete [] buffer; - return str.str(); +std::string memoryToBase64(CIDebugDataSpaces *ds, ULONG64 address, ULONG length, + std::string *errorMessage /* = 0 */) +{ + if (const unsigned char *buffer = readMemory(ds, address, length, errorMessage)) { + std::ostringstream str; + base64Encode(str, buffer, length); + delete [] buffer; + return str.str(); + } + return std::string(); +} + +std::wstring memoryToHexW(CIDebugDataSpaces *ds, ULONG64 address, ULONG length, + std::string *errorMessage /* = 0 */) +{ + if (const unsigned char *buffer = readMemory(ds, address, length, errorMessage)) { + const std::wstring hex = dataToHexW(buffer, buffer + length); + delete [] buffer; + return hex; + } + return std::wstring(); } // Format stack as GDBMI @@ -676,13 +699,29 @@ static inline void formatGdbmiFlag(std::ostream &str, const char *name, bool v) str << name << "=\"" << (v ? "true" : "false") << '"'; } +std::pair<ULONG64, ULONG> breakPointMemoryRange(IDebugBreakpoint *bp) +{ + // Get address. Can fail for deferred breakpoints. + std::pair<ULONG64, ULONG> result = std::pair<ULONG64, ULONG>(0, 0); + if (FAILED(bp->GetOffset(&result.first)) || result.first == 0) + return result; + // Fill 'size' only for data breakpoints + ULONG breakType = DEBUG_BREAKPOINT_CODE; + ULONG cpuType = 0; + if (FAILED(bp->GetType(&breakType, &cpuType)) || breakType != DEBUG_BREAKPOINT_DATA) + return result; + ULONG accessType = 0; + bp->GetDataParameters(&result.second, &accessType); + return result; +} + static bool gdbmiFormatBreakpoint(std::ostream &str, IDebugBreakpoint *bp, CIDebugSymbols *symbols /* = 0 */, + CIDebugDataSpaces *dataSpaces /* = 0 */, unsigned verbose, std::string *errorMessage) { enum { BufSize = 512 }; - ULONG64 offset = 0; ULONG flags = 0; ULONG id = 0; if (SUCCEEDED(bp->GetId(&id))) @@ -706,15 +745,26 @@ static bool gdbmiFormatBreakpoint(std::ostream &str, 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 << '"'; - } - } + if (!deferred) { + const std::pair<ULONG64, ULONG> memoryRange = breakPointMemoryRange(bp); + if (memoryRange.first) { + str << ",address=\"" << std::hex << std::showbase << memoryRange.first + << std::dec << std::noshowbase << '"'; + // Resolve module to be specified in next run for speed-up. + if (symbols) { + const std::string module = moduleNameByOffset(symbols, memoryRange.first); + if (!module.empty()) + str << ",module=\"" << module << '"'; + } // symbols + // Report the memory of watchpoints for comparing bitfields + if (dataSpaces && memoryRange.second > 0) { + str << ",size=\"" << memoryRange.second << '"'; + const std::wstring memoryHex = memoryToHexW(dataSpaces, memoryRange.first, memoryRange.second); + if (!memoryHex.empty()) + str << ",memory=\"" << gdbmiWStringFormat(memoryHex) << '"'; + } + } // Got address + } // !deferred // Expression if (verbose > 1) { char buf[BufSize]; @@ -727,6 +777,7 @@ static bool gdbmiFormatBreakpoint(std::ostream &str, // Format breakpoints as GDBMI std::string gdbmiBreakpoints(CIDebugControl *ctrl, CIDebugSymbols *symbols /* = 0 */, + CIDebugDataSpaces *dataSpaces /* = 0 */, bool humanReadable, unsigned verbose, std::string *errorMessage) { ULONG breakPointCount = 0; @@ -747,7 +798,7 @@ std::string gdbmiBreakpoints(CIDebugControl *ctrl, *errorMessage = msgDebugEngineComFailed("GetBreakpointByIndex", hr); return std::string(); } - if (!gdbmiFormatBreakpoint(str, bp, symbols, verbose, errorMessage)) + if (!gdbmiFormatBreakpoint(str, bp, symbols, dataSpaces, verbose, errorMessage)) return std::string(); str << '}'; if (humanReadable) diff --git a/src/libs/qtcreatorcdbext/gdbmihelpers.h b/src/libs/qtcreatorcdbext/gdbmihelpers.h index 4b77cc74919..7df20ad42e3 100644 --- a/src/libs/qtcreatorcdbext/gdbmihelpers.h +++ b/src/libs/qtcreatorcdbext/gdbmihelpers.h @@ -113,9 +113,14 @@ Modules getModules(CIDebugSymbols *syms, std::string *errorMessage); // Format modules as GDBMI std::string gdbmiModules(CIDebugSymbols *syms, bool humanReadable, std::string *errorMessage); +// Helper for breakpoints. Return the memory range of a breakpoint +// as pair(address, size). size is !=0 for data breakpoints. +std::pair<ULONG64, ULONG> breakPointMemoryRange(IDebugBreakpoint *bp); + // Format breakpoints as GDBMI std::string gdbmiBreakpoints(CIDebugControl *ctrl, CIDebugSymbols *symbols /* = 0 */, + CIDebugDataSpaces *dataSpaces /* = 0 */, bool humanReadable, unsigned verbose, std::string *errorMessage); @@ -158,8 +163,10 @@ std::string gdbmiRegisters(CIDebugRegisters *regs, unsigned flags, std::string *errorMessage); -std::string memoryToBase64(CIDebugDataSpaces *ds, ULONG64 address, ULONG length, std::string *errorMessage); - +std::string memoryToBase64(CIDebugDataSpaces *ds, ULONG64 address, ULONG length, + std::string *errorMessage = 0); +std::wstring memoryToHexW(CIDebugDataSpaces *ds, ULONG64 address, ULONG length, + std::string *errorMessage = 0); // Stack helpers StackFrames getStackTrace(CIDebugControl *debugControl, CIDebugSymbols *debugSymbols, unsigned maxFrames, std::string *errorMessage); diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp index 16b27585776..ae8c565be66 100644 --- a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp +++ b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp @@ -1043,7 +1043,8 @@ extern "C" HRESULT CALLBACK breakpoints(CIDebugClient *client, PCSTR argsIn) } tokens.pop_front(); } - const std::string bp = gdbmiBreakpoints(exc.control(), exc.symbols(), humanReadable, verbose, &errorMessage); + const std::string bp = gdbmiBreakpoints(exc.control(), exc.symbols(), exc.dataSpaces(), + humanReadable, verbose, &errorMessage); if (bp.empty()) { ExtensionContext::instance().report('N', token, 0, "breakpoints", errorMessage.c_str()); } else { |