forked from qt-creator/qt-creator
Debuggeri[CDB]: Add breakpoint command.
Report back breakpoints with modules.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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__);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -21,4 +21,5 @@ test
|
||||
stack
|
||||
addwatch
|
||||
widgetat
|
||||
breakpoints
|
||||
KnownStructOutput
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user