// 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; 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 << "{number=\"" << 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, dataSpaces, verbose, errorMessage)) return std::string(); str << '}'; if (humanReadable) str << '\n'; } str << ']'; return str.str(); }
bool evaluateExpression(CIDebugControl *control, const std::string expression, ULONG desiredType, DEBUG_VALUE *v, std::string *errorMessage) { // Ensure we are in C++ ULONG oldSyntax; HRESULT hr = control->GetExpressionSyntax(&oldSyntax); if (FAILED(hr)) { *errorMessage = msgEvaluateExpressionFailed(expression, msgDebugEngineComFailed("GetExpressionSyntax", hr)); return false; } if (oldSyntax != DEBUG_EXPR_CPLUSPLUS) { HRESULT hr = control->SetExpressionSyntax(DEBUG_EXPR_CPLUSPLUS); if (FAILED(hr)) { *errorMessage = msgEvaluateExpressionFailed(expression, msgDebugEngineComFailed("SetExpressionSyntax", hr)); return false; } } hr = control->Evaluate(expression.c_str(), desiredType, v, NULL); if (FAILED(hr)) { *errorMessage = msgEvaluateExpressionFailed(expression, msgDebugEngineComFailed("Evaluate", hr)); return false; } if (oldSyntax != DEBUG_EXPR_CPLUSPLUS) { HRESULT hr = control->SetExpressionSyntax(oldSyntax); if (FAILED(hr)) { *errorMessage = msgEvaluateExpressionFailed(expression, msgDebugEngineComFailed("SetExpressionSyntax", hr)); return false; } } return true; }
bool threadList(CIDebugSystemObjects *debugSystemObjects, CIDebugSymbols *debugSymbols, CIDebugControl *debugControl, CIDebugAdvanced *debugAdvanced, Threads* threads, ULONG *currentThreadId, std::string *errorMessage) { threads->clear(); ULONG threadCount; *currentThreadId = 0; // Get count HRESULT hr= debugSystemObjects->GetNumberThreads(&threadCount); if (FAILED(hr)) { *errorMessage= msgGetThreadsFailed(msgDebugEngineComFailed("GetNumberThreads", hr)); return false; } // Get index of current if (!threadCount) return true; hr = debugSystemObjects->GetCurrentThreadId(currentThreadId); if (FAILED(hr)) { *errorMessage= msgGetThreadsFailed(msgDebugEngineComFailed("GetCurrentThreadId", hr)); return false; } // Get Identifiers threads->reserve(threadCount); ULONG *ids = new ULONG[threadCount]; ULONG *systemIds = new ULONG[threadCount]; hr = debugSystemObjects->GetThreadIdsByIndex(0, threadCount, ids, systemIds); if (FAILED(hr)) { *errorMessage= msgGetThreadsFailed(msgDebugEngineComFailed("GetThreadIdsByIndex", hr)); return false; } // Create entries static WCHAR name[256]; for (ULONG i= 0; i < threadCount ; ++i) { const ULONG id = ids[i]; Thread thread(id, systemIds[i]); // Thread name ULONG bytesReceived = 0; hr = debugAdvanced->GetSystemObjectInformation(DEBUG_SYSOBJINFO_THREAD_NAME_WIDE, 0, id, name, sizeof(name), &bytesReceived); if (SUCCEEDED(hr) && bytesReceived) thread.name = name; threads->push_back(thread); } delete [] ids; delete [] systemIds; // Get frames and at all events, // restore current thread after switching frames. const bool framesOk = getThreadFrames(debugSymbols, debugSystemObjects, debugControl, threads, errorMessage); const bool restoreOk =setCurrentThread(debugSystemObjects, *currentThreadId, errorMessage); return framesOk && restoreOk; }
// Format stack as GDBMI static StackFrames getStackTrace(CIDebugControl *debugControl, CIDebugSymbols *debugSymbols, unsigned maxFrames, bool *incomplete, std::string *errorMessage) { if (maxFrames) { *incomplete = false; } else { *incomplete = true; return StackFrames(); } // Ask for one more frame to find out whether it is a complete listing. const unsigned askedFrames = maxFrames + 1; DEBUG_STACK_FRAME *frames = new DEBUG_STACK_FRAME[askedFrames]; ULONG frameCount = 0; const HRESULT hr = debugControl->GetStackTrace(0, 0, 0, frames, askedFrames, &frameCount); if (FAILED(hr)) { delete [] frames; *errorMessage = msgDebugEngineComFailed("GetStackTrace", hr); return StackFrames(); } if (askedFrames == frameCount) { --frameCount; *incomplete = true; } StackFrames rc(frameCount, StackFrame()); for (ULONG f = 0; f < frameCount; ++f) getFrame(debugSymbols, frames[f], &(rc[f])); delete [] frames; return rc; }
std::string memoryToBase64(CIDebugDataSpaces *ds, ULONG64 address, ULONG length, std::string *errorMessage) { unsigned char *buffer = new unsigned char[length]; std::fill(buffer, buffer + length, 0); ULONG received = 0; 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 (received < length) { std::ostringstream estr; estr << "Warning: Received only " << received << " bytes of " << length << " requested at " << address << '.'; *errorMessage = estr.str(); } std::ostringstream str; base64Encode(str, buffer, length); delete [] buffer; return str.str(); }
static bool gdbmiFormatBreakpoint(std::ostream &str, IDebugBreakpoint *bp, CIDebugSymbols *symbols /* = 0 */, CIDebugDataSpaces *dataSpaces /* = 0 */, unsigned verbose, std::string *errorMessage) { enum { BufSize = 512 }; ULONG flags = 0; ULONG id = 0; if (SUCCEEDED(bp->GetId(&id))) str << ",id=\"" << id << '"'; 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); formatGdbmiFlag(str, ",enabled", (flags & DEBUG_BREAKPOINT_ENABLED) != 0); if (verbose) { 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) { 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]; if (SUCCEEDED(bp->GetOffsetExpression(buf, BUFSIZ, 0))) str << ",expression=\"" << gdbmiStringFormat(buf) << '"'; } return true; }
static inline bool setCurrentThread(CIDebugSystemObjects *debugSystemObjects, ULONG id, std::string *errorMessage) { const HRESULT hr = debugSystemObjects->SetCurrentThreadId(id); if (FAILED(hr)) { *errorMessage = msgDebugEngineComFailed("SetCurrentThreadId", hr); return false; } return true; }
static inline bool getSymbolCount(CIDebugSymbolGroup *symbolGroup, ULONG *count, std::string *errorMessage) { const HRESULT hr = symbolGroup->GetNumberSymbols(count); if (FAILED(hr)) { *errorMessage = msgDebugEngineComFailed("GetNumberSymbols", hr); return false; } return true; }
bool ExtensionContext::call(const std::string &functionCall, std::wstring *output, std::string *errorMessage) { if (!m_creatorOutputCallback) { *errorMessage = "Attempt to issue a call with no output hooked."; return false; } // Set up arguments const std::string call = ".call " + functionCall; HRESULT hr = m_control->Execute(DEBUG_OUTCTL_ALL_CLIENTS, call.c_str(), DEBUG_EXECUTE_ECHO); if (FAILED(hr)) { *errorMessage = msgDebugEngineComFailed("Execute", hr); return 0; } // Execute in current thread. TODO: This must not crash, else we are in an inconsistent state // (need to call 'gh', etc.) hr = m_control->Execute(DEBUG_OUTCTL_ALL_CLIENTS, "~. g", DEBUG_EXECUTE_ECHO); if (FAILED(hr)) { *errorMessage = msgDebugEngineComFailed("Execute", hr); return 0; } // Wait until finished startRecordingOutput(); m_stateNotification = false; m_control->WaitForEvent(0, INFINITE); *output = stopRecordingOutput(); m_stateNotification = true; // Crude attempt at recovering from a crash: Issue 'gN' (go with exception not handled). const bool crashed = output->find(L"This exception may be expected and handled.") != std::string::npos; if (crashed) { m_stopReason.clear(); m_stateNotification = false; hr = m_control->Execute(DEBUG_OUTCTL_ALL_CLIENTS, "~. gN", DEBUG_EXECUTE_ECHO); m_control->WaitForEvent(0, INFINITE); m_stateNotification = true; *errorMessage = "A crash occurred while calling: " + functionCall; return false; } return true; }
Modules getModules(CIDebugSymbols *syms, std::string *errorMessage) { enum { BufSize = 1024 }; char nameBuf[BufSize]; char fileBuf[BufSize]; ULONG Loaded; ULONG Unloaded; HRESULT hr = syms->GetNumberModules(&Loaded, &Unloaded); if (FAILED(hr)) { *errorMessage = msgDebugEngineComFailed("GetNumberModules", hr); return Modules(); } const ULONG count = Loaded + Unloaded; Modules rc; rc.reserve(count); DEBUG_MODULE_PARAMETERS *parameters = new DEBUG_MODULE_PARAMETERS[count]; hr = syms->GetModuleParameters(count, NULL, 0, parameters); if (FAILED(hr)) { delete [] parameters; *errorMessage = msgDebugEngineComFailed("GetModuleParameters", hr); return Modules(); } for (ULONG m = 0; m < count; ++m) { Module module; module.base = parameters[m].Base; module.size = parameters[m].Size; module.deferred = parameters[m].Flags == DEBUG_SYMTYPE_DEFERRED; hr = syms->GetModuleNames(m, 0, fileBuf, BufSize, NULL, nameBuf, BufSize, NULL, NULL, NULL, NULL); if (FAILED(hr)) break; // Fail silently should unloaded modules not work. module.name = nameBuf; module.image = fileBuf; rc.push_back(module); } return rc; }
bool getFrame(CIDebugSymbols *debugSymbols, CIDebugControl *debugControl, unsigned n, StackFrame *f, std::string *errorMessage) { DEBUG_STACK_FRAME *frames = new DEBUG_STACK_FRAME[n + 1]; ULONG frameCount; const HRESULT hr = debugControl->GetStackTrace(0, 0, 0, frames, n + 1, &frameCount); if (FAILED(hr)) { delete [] frames; *errorMessage = msgDebugEngineComFailed("GetStackTrace", hr); return false; } getFrame(debugSymbols, frames[n], f); delete [] frames; return true; }
static bool gdbmiFormatBreakpoint(std::ostream &str, IDebugBreakpoint *bp, CIDebugSymbols *symbols /* = 0 */, unsigned verbose, std::string *errorMessage) { enum { BufSize = 512 }; ULONG64 offset = 0; ULONG flags = 0; ULONG id = 0; if (SUCCEEDED(bp->GetId(&id))) str << ",id=\"" << id << '"'; 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); formatGdbmiFlag(str, ",enabled", (flags & DEBUG_BREAKPOINT_ENABLED) != 0); if (verbose) { 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 if (verbose > 1) { char buf[BufSize]; if (SUCCEEDED(bp->GetOffsetExpression(buf, BUFSIZ, 0))) str << ",expression=\"" << gdbmiStringFormat(buf) << '"'; } return true; }
// 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; }
bool SymbolGroup::getSymbolParameters(CIDebugSymbolGroup *symbolGroup, unsigned long start, unsigned long count, SymbolParameterVector *vec, std::string *errorMessage) { if (!count) { vec->clear(); return true; } // Trim the count to the maximum count available. When expanding elements // and passing SubElements as count, SubElements might be an estimate that // is too large and triggers errors. ULONG totalCount; if (!getSymbolCount(symbolGroup, &totalCount, errorMessage)) return false; if (start >= totalCount) { std::ostringstream str; str << "SymbolGroup::getSymbolParameters: Start parameter " << start << " beyond total " << totalCount << '.'; *errorMessage = str.str(); return false; } if (start + count > totalCount) count = totalCount - start; // Get parameters. vec->resize(count); const HRESULT hr = symbolGroup->GetSymbolParameters(start, count, &(*vec->begin())); if (FAILED(hr)) { std::ostringstream str; str << "SymbolGroup::getSymbolParameters failed for index=" << start << ", count=" << count << ": " << msgDebugEngineComFailed("GetSymbolParameters", hr); *errorMessage = str.str(); return false; } return true; }
Registers getRegisters(CIDebugRegisters *regs, unsigned flags, std::string *errorMessage) { enum { bufSize= 128 }; WCHAR buf[bufSize]; ULONG registerCount = 0; HRESULT hr = regs->GetNumberRegisters(®isterCount); if (FAILED(hr)) { *errorMessage = msgDebugEngineComFailed("GetNumberRegisters", hr); return Registers(); } ULONG pseudoRegisterCount = 0; if (flags & IncludePseudoRegisters) { hr = regs->GetNumberPseudoRegisters(&pseudoRegisterCount); if (FAILED(hr)) { *errorMessage = msgDebugEngineComFailed("GetNumberPseudoRegisters", hr); return Registers(); } } Registers rc; rc.reserve(registerCount + pseudoRegisterCount); // Standard registers DEBUG_REGISTER_DESCRIPTION description; DEBUG_VALUE value; for (ULONG r = 0; r < registerCount; ++r) { hr = regs->GetDescriptionWide(r, buf, bufSize, NULL, &description); if (FAILED(hr)) { *errorMessage = msgDebugEngineComFailed("GetDescription", hr); return Registers(); } hr = regs->GetValue(r, &value); if (FAILED(hr)) { *errorMessage = msgDebugEngineComFailed("GetValue", hr); return Registers(); } const bool isSubRegister = (description.Flags & DEBUG_REGISTER_SUB_REGISTER); if (!isSubRegister || (flags & IncludeSubRegisters)) { Register reg; reg.name = buf; reg.description = registerDescription(description); reg.subRegister = isSubRegister; reg.value = value; rc.push_back(reg); } } // Pseudo for (ULONG r = 0; r < pseudoRegisterCount; ++r) { ULONG type; hr = regs->GetPseudoDescriptionWide(r, buf, bufSize, NULL, NULL, &type); if (FAILED(hr)) continue; // Fails for some pseudo registers hr = regs->GetValue(r, &value); if (FAILED(hr)) continue; // Fails for some pseudo registers Register reg; reg.pseudoRegister = true; reg.name = buf; reg.description = valueType(type); reg.value = value; rc.push_back(reg); } return rc; }