forked from qt-creator/qt-creator
Introduce node hierarchy and move nodes to a separate file. Introduce reference nodes that point to additional symbols and symbols within the symbol tree (make deeply nested linked list elements visible as array elements). Properly name container elements as array elements 0..n. Fix pre-expansion of complex dumpers.
724 lines
25 KiB
C++
724 lines
25 KiB
C++
/**************************************************************************
|
|
**
|
|
** This file is part of Qt Creator
|
|
**
|
|
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
|
**
|
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
|
**
|
|
** Commercial Usage
|
|
**
|
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
|
** accordance with the Qt Commercial License Agreement provided with the
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
** a written agreement between you and Nokia.
|
|
**
|
|
** GNU Lesser General Public License Usage
|
|
**
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
** General Public License version 2.1 as published by the Free Software
|
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
|
** packaging of this file. Please review the following information to
|
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
**
|
|
** If you are unsure which license is appropriate for your use, please
|
|
** contact the sales department at http://qt.nokia.com/contact.
|
|
**
|
|
**************************************************************************/
|
|
|
|
#include "extensioncontext.h"
|
|
#include "outputcallback.h"
|
|
#include "eventcallback.h"
|
|
#include "symbolgroup.h"
|
|
#include "symbolgroupvalue.h"
|
|
#include "stringutils.h"
|
|
#include "gdbmihelpers.h"
|
|
|
|
#include <cstdio>
|
|
#include <sstream>
|
|
#include <list>
|
|
#include <iterator>
|
|
|
|
/* QtCreatorCDB ext is an extension loaded into CDB.exe (see cdbengine2.cpp):
|
|
* - Notification about the state of the debugging session:
|
|
* + idle: (hooked with .idle_cmd) debuggee stopped
|
|
* + accessible: Debuggee stopped, cdb.exe accepts commands
|
|
* + inaccessible: Debuggee runs, no way to post commands
|
|
* + session active/inactive: Lost debuggee, terminating.
|
|
* - Hook up with output/event callbacks and produce formatted output
|
|
* - Provide some extension commands that produce output in a standardized (GDBMI)
|
|
* format that ends up in handleExtensionMessage().
|
|
*/
|
|
|
|
// Data struct and helpers for formatting help
|
|
struct CommandDescription {
|
|
const char *name;
|
|
const char *description;
|
|
const char *usage;
|
|
};
|
|
|
|
// Single line of usage: For reporting usage errors back as a single line
|
|
static std::string singleLineUsage(const CommandDescription &d)
|
|
{
|
|
std::string rc = "Usage: ";
|
|
const char *endOfLine = strchr(d.usage, '\n');
|
|
rc += endOfLine ? std::string(d.usage, endOfLine - d.usage) : std::string(d.usage);
|
|
return rc;
|
|
}
|
|
|
|
// Format description of a command
|
|
std::ostream &operator<<(std::ostream &str, const CommandDescription &d)
|
|
{
|
|
str << "Command '" << d.name << "': " << d.description << '\n';
|
|
if (d.usage[0])
|
|
str << "Usage: " << d.name << ' ' << d.usage << '\n';
|
|
str << '\n';
|
|
return str;
|
|
}
|
|
|
|
enum Command {
|
|
CmdPid,
|
|
CmdExpandlocals,
|
|
CmdLocals,
|
|
CmdDumplocal,
|
|
CmdTypecast,
|
|
CmdAddsymbol,
|
|
CmdAssign,
|
|
CmdThreads,
|
|
CmdRegisters,
|
|
CmdModules,
|
|
CmdIdle,
|
|
CmdHelp,
|
|
CmdMemory,
|
|
CmdStack,
|
|
CmdShutdownex
|
|
};
|
|
|
|
static const CommandDescription commandDescriptions[] = {
|
|
{"pid",
|
|
"Prints inferior process id and hooks up output callbacks.",
|
|
"[-t token]"},
|
|
{"expandlocals", "Expands local variables by iname in symbol group.",
|
|
"[-t token] [-c] <frame-number> <iname1-list>\n"
|
|
"iname1-list: Comma-separated list of inames\n"
|
|
"-c complex dumpers"},
|
|
{"locals",
|
|
"Prints local variables of symbol group in GDBMI or debug format",
|
|
"[-t token] [T formats] [-I formats] [-c] [-h] [-d] [-e expand-list] [-u uninitialized-list]\n<frame-number> [iname]\n"
|
|
"-h human-readable ouput\n"
|
|
"-d debug output\n"
|
|
"-c complex dumpers\n"
|
|
"-e expand-list Comma-separated list of inames to be expanded beforehand\n"
|
|
"-u uninitialized-list Comma-separated list of uninitialized inames\n"
|
|
"-I formatmap map of 'hex-encoded-iname=typecode'\n"
|
|
"-T formatmap map of 'hex-encoded-type-name=typecode'"},
|
|
{"dumplocal", "Dumps local variable using simple dumpers (testing command).",
|
|
"[-t token] <frame-number> <iname>"},
|
|
{"typecast","Performs a type cast on an unexpanded iname of symbol group.",
|
|
"[-t token] <frame-number> <iname> <desired-type>"},
|
|
{"addsymbol","Adds a symbol to symbol group (testing command).",
|
|
"[-t token] <frame-number> <name-expression> [optional-iname]"},
|
|
{"assign","Assigns a value to a variable in current symbol group.",
|
|
"[-t token] <iname=value>"},
|
|
{"threads","Lists threads in GDBMI format.","[-t token]"},
|
|
{"registers","Lists registers in GDBMI format","[-t token]"},
|
|
{"modules","Lists modules in GDBMI format.","[-t token]"},
|
|
{"idle",
|
|
"Reports stop reason in GDBMI format.\n"
|
|
"Intended to be used with .idle_cmd to obtain proper stop notification.",""},
|
|
{"help","Prints help.",""},
|
|
{"memory","Prints memory contents in Base64 encoding.","[-t token] <address> <length>"},
|
|
{"stack","Prints stack in GDBMI format.","[-t token] [max-frames]"},
|
|
{"shutdownex","Unhooks output callbacks.\nNeeds to be called explicitly only in case of remote debugging.",""}
|
|
};
|
|
|
|
typedef std::vector<std::string> StringVector;
|
|
typedef std::list<std::string> StringList;
|
|
|
|
// Helper for commandTokens() below:
|
|
// Simple splitting of command lines allowing for '"'-quoted tokens
|
|
// 'typecast local.i "class QString *"' -> ('typecast','local.i','class QString *')
|
|
template<class Inserter>
|
|
static inline void splitCommand(PCSTR args, Inserter it)
|
|
{
|
|
enum State { WhiteSpace, WithinToken, WithinQuoted };
|
|
|
|
State state = WhiteSpace;
|
|
std::string current;
|
|
for (PCSTR p = args; *p; p++) {
|
|
char c = *p;
|
|
switch (state) {
|
|
case WhiteSpace:
|
|
switch (c) {
|
|
case ' ':
|
|
break;
|
|
case '"':
|
|
state = WithinQuoted;
|
|
current.clear();
|
|
break;
|
|
default:
|
|
state = WithinToken;
|
|
current.clear();
|
|
current.push_back(c);
|
|
break;
|
|
}
|
|
break;
|
|
case WithinToken:
|
|
switch (c) {
|
|
case ' ':
|
|
state = WhiteSpace;
|
|
*it = current;
|
|
++it;
|
|
break;
|
|
default:
|
|
current.push_back(c);
|
|
break;
|
|
}
|
|
break;
|
|
case WithinQuoted:
|
|
switch (c) {
|
|
case '"':
|
|
state = WhiteSpace;
|
|
*it = current;
|
|
++it;
|
|
break;
|
|
default:
|
|
current.push_back(c);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (state == WithinToken) {
|
|
*it = current;
|
|
++it;
|
|
}
|
|
}
|
|
|
|
// Split & Parse the arguments of a command and extract the
|
|
// optional first integer token argument ('command -t <number> remaining arguments')
|
|
// Each command takes the 'token' argument and includes it in its output
|
|
// so that the calling CDB engine in Qt Creator can match the callback.
|
|
|
|
template<class StringContainer>
|
|
static inline StringContainer commandTokens(PCSTR args, int *token = 0)
|
|
{
|
|
typedef StringContainer::iterator ContainerIterator;
|
|
|
|
if (token)
|
|
*token = -1; // Handled as 'display' in engine, so that user can type commands
|
|
StringContainer tokens;
|
|
splitCommand(args, std::back_inserter(tokens));
|
|
// Check for token
|
|
ContainerIterator it = tokens.begin();
|
|
if (it != tokens.end() && *it == "-t" && ++it != tokens.end()) {
|
|
if (token)
|
|
std::istringstream(*it) >> *token;
|
|
tokens.erase(tokens.begin(), ++it);
|
|
}
|
|
return tokens;
|
|
}
|
|
|
|
// Extension command 'pid':
|
|
// Report back PID so that Qt Creator engine knows whom to DebugBreakProcess.
|
|
extern "C" HRESULT CALLBACK pid(CIDebugClient *client, PCSTR args)
|
|
{
|
|
ExtensionContext::instance().hookCallbacks(client);
|
|
|
|
int token;
|
|
commandTokens<StringList>(args, &token);
|
|
|
|
if (const ULONG pid = currentProcessId(client)) {
|
|
ExtensionContext::instance().report('R', token, "pid", "%u", pid);
|
|
} else {
|
|
ExtensionContext::instance().report('N', token, "pid", "0");
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
// Extension command 'expandlocals':
|
|
// Expand a comma-separated iname-list of local variables.
|
|
|
|
extern "C" HRESULT CALLBACK expandlocals(CIDebugClient *client, PCSTR args)
|
|
{
|
|
ExtensionCommandContext exc(client);
|
|
unsigned frame = 0;
|
|
SymbolGroup *symGroup = 0;
|
|
std::string errorMessage;
|
|
|
|
int token;
|
|
StringList tokens = commandTokens<StringList>(args, &token);
|
|
StringVector inames;
|
|
bool runComplexDumpers = false;
|
|
do {
|
|
if (!tokens.empty() && tokens.front() == "-c") {
|
|
runComplexDumpers = true;
|
|
tokens.pop_front();
|
|
}
|
|
if (tokens.empty() || !integerFromString(tokens.front(), &frame)) {
|
|
errorMessage = singleLineUsage(commandDescriptions[CmdExpandlocals]);
|
|
break;
|
|
}
|
|
tokens.pop_front();
|
|
if (tokens.empty()) {
|
|
errorMessage = singleLineUsage(commandDescriptions[CmdExpandlocals]);
|
|
break;
|
|
}
|
|
split(tokens.front(), ',', std::back_inserter(inames));
|
|
} while (false);
|
|
|
|
if (errorMessage.empty())
|
|
symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), frame, &errorMessage);
|
|
|
|
if (!symGroup) {
|
|
ExtensionContext::instance().report('N', token, "expandlocals", errorMessage.c_str());
|
|
return S_OK;
|
|
}
|
|
|
|
const unsigned succeeded = runComplexDumpers ?
|
|
symGroup->expandListRunComplexDumpers(inames, SymbolGroupValueContext(exc.dataSpaces()), &errorMessage) :
|
|
symGroup->expandList(inames, &errorMessage);
|
|
|
|
ExtensionContext::instance().report('R', token, "expandlocals", "%u/%u %s",
|
|
succeeded, unsigned(inames.size()), errorMessage.c_str());
|
|
return S_OK;
|
|
}
|
|
|
|
// Extension command 'locals':
|
|
// Display local variables of symbol group in GDBMI or debug output form.
|
|
// Takes an optional iname which is expanded before displaying (for updateWatchData)
|
|
|
|
static std::string commmandLocals(ExtensionCommandContext &exc,PCSTR args, int *token, std::string *errorMessage)
|
|
{
|
|
// Parse the command
|
|
unsigned debugOutput = 0;
|
|
std::string iname;
|
|
StringList tokens = commandTokens<StringList>(args, token);
|
|
StringVector expandedInames;
|
|
StringVector uninitializedInames;
|
|
DumpParameters parameters;
|
|
// Parse away options
|
|
while (!tokens.empty() && tokens.front().size() == 2 && tokens.front().at(0) == '-') {
|
|
const char option = tokens.front().at(1);
|
|
tokens.pop_front();
|
|
switch (option) {
|
|
case 'd':
|
|
debugOutput++;
|
|
break;
|
|
case 'h':
|
|
parameters.dumpFlags |= DumpParameters::DumpHumanReadable;
|
|
break;
|
|
case 'c':
|
|
parameters.dumpFlags |= DumpParameters::DumpComplexDumpers;
|
|
break;
|
|
case 'u':
|
|
if (tokens.empty()) {
|
|
*errorMessage = singleLineUsage(commandDescriptions[CmdLocals]);
|
|
return std::string();
|
|
}
|
|
split(tokens.front(), ',', std::back_inserter(uninitializedInames));
|
|
tokens.pop_front();
|
|
break;
|
|
case 'e':
|
|
if (tokens.empty()) {
|
|
*errorMessage = singleLineUsage(commandDescriptions[CmdLocals]);
|
|
return std::string();
|
|
}
|
|
split(tokens.front(), ',', std::back_inserter(expandedInames));
|
|
tokens.pop_front();
|
|
break;
|
|
case 'T': // typeformats: 'hex'ed name = formatnumber,...'
|
|
if (tokens.empty()) {
|
|
*errorMessage = singleLineUsage(commandDescriptions[CmdLocals]);
|
|
return std::string();
|
|
}
|
|
parameters.typeFormats = DumpParameters::decodeFormatArgument(tokens.front());
|
|
tokens.pop_front();
|
|
break;
|
|
case 'I': // individual formats: 'hex'ed name = formatnumber,...'
|
|
if (tokens.empty()) {
|
|
*errorMessage = singleLineUsage(commandDescriptions[CmdLocals]);
|
|
return std::string();
|
|
}
|
|
parameters.individualFormats = DumpParameters::decodeFormatArgument(tokens.front());
|
|
tokens.pop_front();
|
|
break;
|
|
} // case option
|
|
} // for options
|
|
|
|
// Frame and iname
|
|
unsigned frame;
|
|
if (tokens.empty() || !integerFromString(tokens.front(), &frame)) {
|
|
*errorMessage = singleLineUsage(commandDescriptions[CmdLocals]);
|
|
return std::string();
|
|
}
|
|
|
|
tokens.pop_front();
|
|
if (!tokens.empty())
|
|
iname = tokens.front();
|
|
|
|
const SymbolGroupValueContext dumpContext(exc.dataSpaces());
|
|
SymbolGroup * const symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), frame, errorMessage);
|
|
if (!symGroup)
|
|
return std::string();
|
|
|
|
if (!uninitializedInames.empty())
|
|
symGroup->markUninitialized(uninitializedInames);
|
|
|
|
if (!expandedInames.empty()) {
|
|
if (parameters.dumpFlags & DumpParameters::DumpComplexDumpers) {
|
|
symGroup->expandListRunComplexDumpers(expandedInames, dumpContext, errorMessage);
|
|
} else {
|
|
symGroup->expandList(expandedInames, errorMessage);
|
|
}
|
|
}
|
|
|
|
if (debugOutput)
|
|
return symGroup->debug(iname, debugOutput - 1);
|
|
|
|
return iname.empty() ?
|
|
symGroup->dump(dumpContext, parameters) :
|
|
symGroup->dump(iname, dumpContext, parameters, errorMessage);
|
|
}
|
|
|
|
extern "C" HRESULT CALLBACK locals(CIDebugClient *client, PCSTR args)
|
|
{
|
|
ExtensionCommandContext exc(client);
|
|
std::string errorMessage;
|
|
int token;
|
|
const std::string output = commmandLocals(exc, args, &token, &errorMessage);
|
|
if (output.empty()) {
|
|
ExtensionContext::instance().report('N', token, "locals", errorMessage.c_str());
|
|
} else {
|
|
ExtensionContext::instance().report('R', token, "locals", "%s", output.c_str());
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
// Extension command 'dumplocal':
|
|
// Dump a local variable using dumpers (testing command).
|
|
|
|
static std::string dumplocalHelper(ExtensionCommandContext &exc,PCSTR args, int *token, std::string *errorMessage)
|
|
{
|
|
// Parse the command
|
|
StringList tokens = commandTokens<StringList>(args, token);
|
|
// Frame and iname
|
|
unsigned frame;
|
|
if (tokens.empty() || integerFromString(tokens.front(), &frame)) {
|
|
*errorMessage = singleLineUsage(commandDescriptions[CmdDumplocal]);
|
|
return std::string();
|
|
}
|
|
tokens.pop_front();
|
|
if (tokens.empty()) {
|
|
*errorMessage = singleLineUsage(commandDescriptions[CmdDumplocal]);
|
|
return std::string();
|
|
}
|
|
const std::string iname = tokens.front();
|
|
|
|
SymbolGroup * const symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), frame, errorMessage);
|
|
if (!symGroup)
|
|
return std::string();
|
|
|
|
AbstractSymbolGroupNode *n = symGroup->find(iname);
|
|
if (!n || !n->asSymbolGroupNode()) {
|
|
*errorMessage = "No such iname " + iname;
|
|
return std::string();
|
|
}
|
|
std::wstring value;
|
|
if (!dumpSimpleType(n->asSymbolGroupNode(), SymbolGroupValueContext(exc.dataSpaces()), &value)) {
|
|
*errorMessage = "Cannot dump " + iname;
|
|
return std::string();
|
|
}
|
|
return wStringToString(value);
|
|
}
|
|
|
|
extern "C" HRESULT CALLBACK dumplocal(CIDebugClient *client, PCSTR argsIn)
|
|
{
|
|
ExtensionCommandContext exc(client);
|
|
std::string errorMessage;
|
|
int token = 0;
|
|
const std::string value = dumplocalHelper(exc,argsIn, &token, &errorMessage);
|
|
if (value.empty()) {
|
|
ExtensionContext::instance().report('N', token, "dumplocal", errorMessage.c_str());
|
|
} else {
|
|
ExtensionContext::instance().report('R', token, "dumplocal", value.c_str());
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
// Extension command 'typecast':
|
|
// Change the type of a symbol group entry (testing purposes)
|
|
|
|
extern "C" HRESULT CALLBACK typecast(CIDebugClient *client, PCSTR args)
|
|
{
|
|
ExtensionCommandContext exc(client);
|
|
unsigned frame = 0;
|
|
SymbolGroup *symGroup = 0;
|
|
std::string errorMessage;
|
|
|
|
int token;
|
|
const StringVector tokens = commandTokens<StringVector>(args, &token);
|
|
std::string iname;
|
|
std::string desiredType;
|
|
if (tokens.size() == 3u && integerFromString(tokens.front(), &frame)) {
|
|
symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), frame, &errorMessage);
|
|
iname = tokens.at(1);
|
|
desiredType = tokens.at(2);
|
|
} else {
|
|
errorMessage = singleLineUsage(commandDescriptions[CmdTypecast]);
|
|
}
|
|
if (symGroup != 0 && symGroup->typeCast(iname, desiredType, &errorMessage)) {
|
|
ExtensionContext::instance().report('R', token, "typecast", "OK");
|
|
} else {
|
|
ExtensionContext::instance().report('N', token, "typecast", errorMessage.c_str());
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
// Extension command 'addsymbol':
|
|
// Adds a symbol to a symbol group by name (testing purposes)
|
|
|
|
extern "C" HRESULT CALLBACK addsymbol(CIDebugClient *client, PCSTR args)
|
|
{
|
|
ExtensionCommandContext exc(client);
|
|
unsigned frame = 0;
|
|
SymbolGroup *symGroup = 0;
|
|
std::string errorMessage;
|
|
|
|
int token;
|
|
const StringVector tokens = commandTokens<StringVector>(args, &token);
|
|
std::string name;
|
|
std::string iname;
|
|
if (tokens.size() >= 2u && integerFromString(tokens.front(), &frame)) {
|
|
symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), frame, &errorMessage);
|
|
name = tokens.at(1);
|
|
if (tokens.size() >= 3)
|
|
iname = tokens.at(2);
|
|
} else {
|
|
errorMessage = singleLineUsage(commandDescriptions[CmdAddsymbol]);
|
|
}
|
|
if (symGroup != 0 && symGroup->addSymbol(name, iname, &errorMessage)) {
|
|
ExtensionContext::instance().report('R', token, "addsymbol", "OK");
|
|
} else {
|
|
ExtensionContext::instance().report('N', token, "addsymbol", errorMessage.c_str());
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
// Extension command 'assign':
|
|
// Assign locals by iname: 'assign locals.x=5'
|
|
|
|
extern "C" HRESULT CALLBACK assign(CIDebugClient *client, PCSTR argsIn)
|
|
{
|
|
ExtensionCommandContext exc(client);
|
|
|
|
std::string errorMessage;
|
|
bool success = false;
|
|
|
|
int token = 0;
|
|
do {
|
|
const StringList tokens = commandTokens<StringList>(argsIn, &token);
|
|
// Parse 'assign locals.x=5'
|
|
const std::string::size_type equalsPos = tokens.size() == 1 ? tokens.front().find('=') : std::string::npos;
|
|
if (equalsPos == std::string::npos) {
|
|
errorMessage = singleLineUsage(commandDescriptions[CmdAssign]);
|
|
break;
|
|
}
|
|
const std::string iname = tokens.front().substr(0, equalsPos);
|
|
const std::string value = tokens.front().substr(equalsPos + 1, tokens.front().size() - equalsPos - 1);
|
|
// get the symbolgroup
|
|
const int currentFrame = ExtensionContext::instance().symbolGroupFrame();
|
|
if (currentFrame < 0) {
|
|
errorMessage = "No current frame.";
|
|
break;
|
|
}
|
|
SymbolGroup *symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), currentFrame, &errorMessage);
|
|
if (!symGroup)
|
|
break;
|
|
success = symGroup->assign(iname, value, &errorMessage);
|
|
} while (false);
|
|
|
|
if (success) {
|
|
ExtensionContext::instance().report('R', token, "assign", "Ok");
|
|
} else {
|
|
ExtensionContext::instance().report('N', token, "assign", errorMessage.c_str());
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
// Extension command 'threads':
|
|
// List all thread info in GDBMI syntax
|
|
|
|
extern "C" HRESULT CALLBACK threads(CIDebugClient *client, PCSTR argsIn)
|
|
{
|
|
ExtensionCommandContext exc(client);
|
|
std::string errorMessage;
|
|
|
|
int token;
|
|
commandTokens<StringList>(argsIn, &token);
|
|
|
|
const std::string gdbmi = gdbmiThreadList(exc.systemObjects(),
|
|
exc.symbols(),
|
|
exc.control(),
|
|
exc.advanced(),
|
|
&errorMessage);
|
|
if (gdbmi.empty()) {
|
|
ExtensionContext::instance().report('N', token, "threads", errorMessage.c_str());
|
|
} else {
|
|
ExtensionContext::instance().report('R', token, "threads", gdbmi.c_str());
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
// Extension command 'registers':
|
|
// List all registers in GDBMI syntax
|
|
|
|
extern "C" HRESULT CALLBACK registers(CIDebugClient *Client, PCSTR argsIn)
|
|
{
|
|
ExtensionCommandContext exc(Client);
|
|
std::string errorMessage;
|
|
|
|
int token;
|
|
const StringList tokens = commandTokens<StringList>(argsIn, &token);
|
|
const bool humanReadable = !tokens.empty() && tokens.front() == "-h";
|
|
const std::string regs = gdbmiRegisters(exc.registers(), exc.control(), humanReadable, IncludePseudoRegisters, &errorMessage);
|
|
if (regs.empty()) {
|
|
ExtensionContext::instance().report('N', token, "registers", errorMessage.c_str());
|
|
} else {
|
|
ExtensionContext::instance().report('R', token, "registers", regs.c_str());
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
// Extension command 'modules':
|
|
// List all modules in GDBMI syntax
|
|
|
|
extern "C" HRESULT CALLBACK modules(CIDebugClient *Client, PCSTR argsIn)
|
|
{
|
|
ExtensionCommandContext exc(Client);
|
|
std::string errorMessage;
|
|
|
|
int token;
|
|
const StringList tokens = commandTokens<StringList>(argsIn, &token);
|
|
const bool humanReadable = !tokens.empty() && tokens.front() == "-h";
|
|
const std::string modules = gdbmiModules(exc.symbols(), humanReadable, &errorMessage);
|
|
if (modules.empty()) {
|
|
ExtensionContext::instance().report('N', token, "modules", errorMessage.c_str());
|
|
} else {
|
|
ExtensionContext::instance().report('R', token, "modules", modules.c_str());
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
// Report stop of debuggee to Creator. This is hooked up as .idle_cmd command
|
|
// by the Creator engine to reliably get notified about stops.
|
|
extern "C" HRESULT CALLBACK idle(CIDebugClient *, PCSTR)
|
|
{
|
|
ExtensionContext::instance().notifyIdle();
|
|
return S_OK;
|
|
}
|
|
|
|
// Extension command 'help':
|
|
// Display version
|
|
|
|
extern "C" HRESULT CALLBACK help(CIDebugClient *, PCSTR)
|
|
{
|
|
std::ostringstream str;
|
|
str << "### Qt Creator CDB extension built " << __DATE__ << "\n\n";
|
|
|
|
const size_t commandCount = sizeof(commandDescriptions)/sizeof(CommandDescription);
|
|
std::copy(commandDescriptions, commandDescriptions + commandCount,
|
|
std::ostream_iterator<CommandDescription>(str));
|
|
dprintf("%s\n", str.str().c_str());
|
|
return S_OK;
|
|
}
|
|
|
|
// Extension command 'memory':
|
|
// Display memory as base64
|
|
|
|
extern "C" HRESULT CALLBACK memory(CIDebugClient *Client, PCSTR argsIn)
|
|
{
|
|
ExtensionCommandContext exc(Client);
|
|
std::string errorMessage;
|
|
std::string memory;
|
|
|
|
int token;
|
|
ULONG64 address = 0;
|
|
ULONG length = 0;
|
|
|
|
const StringVector tokens = commandTokens<StringVector>(argsIn, &token);
|
|
if (tokens.size() == 2
|
|
&& integerFromString(tokens.front(), &address)
|
|
&& integerFromString(tokens.at(1), &length)) {
|
|
memory = memoryToBase64(exc.dataSpaces(), address, length, &errorMessage);
|
|
} else {
|
|
errorMessage = singleLineUsage(commandDescriptions[CmdMemory]);
|
|
}
|
|
|
|
if (memory.empty()) {
|
|
ExtensionContext::instance().report('N', token, "memory", errorMessage.c_str());
|
|
} else {
|
|
ExtensionContext::instance().report('R', token, "memory", memory.c_str());
|
|
if (!errorMessage.empty())
|
|
ExtensionContext::instance().report('W', token, "memory", errorMessage.c_str());
|
|
}
|
|
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.
|
|
extern "C" HRESULT CALLBACK shutdownex(CIDebugClient *, PCSTR)
|
|
{
|
|
ExtensionContext::instance().unhookCallbacks();
|
|
return S_OK;
|
|
}
|
|
|
|
// Hook for dumping Known Structs. Not currently used.
|
|
// Shows up in 'dv' as well as IDebugSymbolGroup::GetValueText.
|
|
|
|
extern "C" HRESULT CALLBACK KnownStructOutput(ULONG Flag, ULONG64 Address, PSTR StructName, PSTR Buffer, PULONG /* BufferSize */)
|
|
{
|
|
if (Flag == DEBUG_KNOWN_STRUCT_GET_NAMES) {
|
|
memcpy(Buffer, "\0\0", 2);
|
|
return S_OK;
|
|
}
|
|
// Usually 260 chars buf
|
|
std::ostringstream str;
|
|
str << " KnownStructOutput 0x" << std::hex << Address << ' '<< StructName;
|
|
strcpy(Buffer, str.str().c_str());
|
|
return S_OK;
|
|
}
|