forked from qt-creator/qt-creator
Debugger[New CDB]: Add infrastructure for casting/adding symbols.
Some more value fixing/support double-quoted commands in extension. Functionality for printing command help.
This commit is contained in:
@@ -6,6 +6,8 @@ pid
|
|||||||
expandlocals
|
expandlocals
|
||||||
locals
|
locals
|
||||||
dumplocal
|
dumplocal
|
||||||
|
typecast
|
||||||
|
addsymbol
|
||||||
assign
|
assign
|
||||||
threads
|
threads
|
||||||
registers
|
registers
|
||||||
|
|||||||
@@ -49,14 +49,149 @@
|
|||||||
* - Hook up with output/event callbacks and produce formatted output
|
* - Hook up with output/event callbacks and produce formatted output
|
||||||
* - Provide some extension commands that produce output in a standardized (GDBMI)
|
* - Provide some extension commands that produce output in a standardized (GDBMI)
|
||||||
* format that ends up in handleExtensionMessage().
|
* format that ends up in handleExtensionMessage().
|
||||||
* + pid Return debuggee pid for interrupting.
|
*/
|
||||||
* + locals Print locals from SymbolGroup
|
|
||||||
* + expandLocals Expand locals in symbol group
|
// Data struct and helpers for formatting help
|
||||||
* + registers, modules, threads */
|
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] <frame-number> <iname1-list>\n"
|
||||||
|
"iname1-list: Comma-separated list of inames"},
|
||||||
|
{"locals",
|
||||||
|
"Prints local variables of symbol group in GDBMI or debug format",
|
||||||
|
"[-t token] [-h] [-d] [-e expand-list] [-u uninitialized-list]\n<frame-number> [iname]\n"
|
||||||
|
"-h human-readable ouput\n"
|
||||||
|
"-d debug output\n"
|
||||||
|
"-e expand-list Comma-separated list of inames to be expanded beforehand\n"
|
||||||
|
"-u uninitialized-list Comma-separated list of uninitialized inames"},
|
||||||
|
{"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::vector<std::string> StringVector;
|
||||||
typedef std::list<std::string> StringList;
|
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
|
// Split & Parse the arguments of a command and extract the
|
||||||
// optional first integer token argument ('command -t <number> remaining arguments')
|
// optional first integer token argument ('command -t <number> remaining arguments')
|
||||||
// Each command takes the 'token' argument and includes it in its output
|
// Each command takes the 'token' argument and includes it in its output
|
||||||
@@ -69,11 +204,8 @@ static inline StringContainer commandTokens(PCSTR args, int *token = 0)
|
|||||||
|
|
||||||
if (token)
|
if (token)
|
||||||
*token = -1; // Handled as 'display' in engine, so that user can type commands
|
*token = -1; // Handled as 'display' in engine, so that user can type commands
|
||||||
std::string cmd(args);
|
|
||||||
simplify(cmd);
|
|
||||||
StringContainer tokens;
|
StringContainer tokens;
|
||||||
split(cmd, ' ', std::back_inserter(tokens));
|
splitCommand(args, std::back_inserter(tokens));
|
||||||
|
|
||||||
// Check for token
|
// Check for token
|
||||||
ContainerIterator it = tokens.begin();
|
ContainerIterator it = tokens.begin();
|
||||||
if (it != tokens.end() && *it == "-t" && ++it != tokens.end()) {
|
if (it != tokens.end() && *it == "-t" && ++it != tokens.end()) {
|
||||||
@@ -114,13 +246,11 @@ extern "C" HRESULT CALLBACK expandlocals(CIDebugClient *client, PCSTR args)
|
|||||||
int token;
|
int token;
|
||||||
const StringVector tokens = commandTokens<StringVector>(args, &token);
|
const StringVector tokens = commandTokens<StringVector>(args, &token);
|
||||||
StringVector inames;
|
StringVector inames;
|
||||||
if (tokens.size() == 2u && sscanf(tokens.front().c_str(), "%u", &frame) == 1) {
|
if (tokens.size() == 2u && integerFromString(tokens.front(), &frame)) {
|
||||||
symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), frame, &errorMessage);
|
symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), frame, &errorMessage);
|
||||||
split(tokens.at(1), ',', std::back_inserter(inames));
|
split(tokens.at(1), ',', std::back_inserter(inames));
|
||||||
} else {
|
} else {
|
||||||
std::ostringstream str;
|
errorMessage = singleLineUsage(commandDescriptions[CmdExpandlocals]);
|
||||||
str << "Invalid parameter: '" << args << "' (usage expand <frame> iname1,iname2..).";
|
|
||||||
errorMessage = str.str();
|
|
||||||
}
|
}
|
||||||
if (symGroup) {
|
if (symGroup) {
|
||||||
const unsigned succeeded = symGroup->expandList(inames, &errorMessage);
|
const unsigned succeeded = symGroup->expandList(inames, &errorMessage);
|
||||||
@@ -132,17 +262,6 @@ extern "C" HRESULT CALLBACK expandlocals(CIDebugClient *client, PCSTR args)
|
|||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline std::string msgLocalsUsage(PCSTR args)
|
|
||||||
{
|
|
||||||
std::ostringstream str;
|
|
||||||
str << "Invalid parameter: '" << args
|
|
||||||
<< "'\nUsage: locals [-t token] [-h] [-d] [-e expandset] [-u uninitializedset] <frame> [iname]).\n"
|
|
||||||
"-h human-readable ouput\n"
|
|
||||||
"-d debug output\n-e expandset Comma-separated list of expanded inames\n"
|
|
||||||
"-u uninitializedset Comma-separated list of uninitialized inames\n";
|
|
||||||
return str.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extension command 'locals':
|
// Extension command 'locals':
|
||||||
// Display local variables of symbol group in GDBMI or debug output form.
|
// Display local variables of symbol group in GDBMI or debug output form.
|
||||||
// Takes an optional iname which is expanded before displaying (for updateWatchData)
|
// Takes an optional iname which is expanded before displaying (for updateWatchData)
|
||||||
@@ -170,7 +289,7 @@ static std::string commmandLocals(ExtensionCommandContext &exc,PCSTR args, int *
|
|||||||
break;
|
break;
|
||||||
case 'u':
|
case 'u':
|
||||||
if (tokens.empty()) {
|
if (tokens.empty()) {
|
||||||
*errorMessage = msgLocalsUsage(args);
|
*errorMessage = singleLineUsage(commandDescriptions[CmdLocals]);
|
||||||
return std::string();
|
return std::string();
|
||||||
}
|
}
|
||||||
split(tokens.front(), ',', std::back_inserter(uninitializedInames));
|
split(tokens.front(), ',', std::back_inserter(uninitializedInames));
|
||||||
@@ -178,7 +297,7 @@ static std::string commmandLocals(ExtensionCommandContext &exc,PCSTR args, int *
|
|||||||
break;
|
break;
|
||||||
case 'e':
|
case 'e':
|
||||||
if (tokens.empty()) {
|
if (tokens.empty()) {
|
||||||
*errorMessage = msgLocalsUsage(args);
|
*errorMessage = singleLineUsage(commandDescriptions[CmdLocals]);
|
||||||
return std::string();
|
return std::string();
|
||||||
}
|
}
|
||||||
split(tokens.front(), ',', std::back_inserter(expandedInames));
|
split(tokens.front(), ',', std::back_inserter(expandedInames));
|
||||||
@@ -188,8 +307,8 @@ static std::string commmandLocals(ExtensionCommandContext &exc,PCSTR args, int *
|
|||||||
}
|
}
|
||||||
// Frame and iname
|
// Frame and iname
|
||||||
unsigned frame;
|
unsigned frame;
|
||||||
if (tokens.empty() || sscanf(tokens.front().c_str(), "%u", &frame) != 1) {
|
if (tokens.empty() || !integerFromString(tokens.front(), &frame)) {
|
||||||
*errorMessage = msgLocalsUsage(args);
|
*errorMessage = singleLineUsage(commandDescriptions[CmdLocals]);
|
||||||
return std::string();
|
return std::string();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,21 +350,19 @@ extern "C" HRESULT CALLBACK locals(CIDebugClient *client, PCSTR args)
|
|||||||
// Extension command 'dumplocal':
|
// Extension command 'dumplocal':
|
||||||
// Dump a local variable using dumpers (testing command).
|
// Dump a local variable using dumpers (testing command).
|
||||||
|
|
||||||
const char dumpLocalUsageC[] = "Usage: dumplocal <frame> <iname>";
|
|
||||||
|
|
||||||
static std::string dumplocalHelper(ExtensionCommandContext &exc,PCSTR args, int *token, std::string *errorMessage)
|
static std::string dumplocalHelper(ExtensionCommandContext &exc,PCSTR args, int *token, std::string *errorMessage)
|
||||||
{
|
{
|
||||||
// Parse the command
|
// Parse the command
|
||||||
StringList tokens = commandTokens<StringList>(args, token);
|
StringList tokens = commandTokens<StringList>(args, token);
|
||||||
// Frame and iname
|
// Frame and iname
|
||||||
unsigned frame;
|
unsigned frame;
|
||||||
if (tokens.empty() || sscanf(tokens.front().c_str(), "%u", &frame) != 1) {
|
if (tokens.empty() || integerFromString(tokens.front(), &frame)) {
|
||||||
*errorMessage = dumpLocalUsageC;
|
*errorMessage = singleLineUsage(commandDescriptions[CmdDumplocal]);
|
||||||
return std::string();
|
return std::string();
|
||||||
}
|
}
|
||||||
tokens.pop_front();
|
tokens.pop_front();
|
||||||
if (tokens.empty()) {
|
if (tokens.empty()) {
|
||||||
*errorMessage = dumpLocalUsageC;
|
*errorMessage = singleLineUsage(commandDescriptions[CmdDumplocal]);
|
||||||
return std::string();
|
return std::string();
|
||||||
}
|
}
|
||||||
const std::string iname = tokens.front();
|
const std::string iname = tokens.front();
|
||||||
@@ -281,6 +398,65 @@ extern "C" HRESULT CALLBACK dumplocal(CIDebugClient *client, PCSTR argsIn)
|
|||||||
return S_OK;
|
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':
|
// Extension command 'assign':
|
||||||
// Assign locals by iname: 'assign locals.x=5'
|
// Assign locals by iname: 'assign locals.x=5'
|
||||||
|
|
||||||
@@ -297,7 +473,7 @@ extern "C" HRESULT CALLBACK assign(CIDebugClient *client, PCSTR argsIn)
|
|||||||
// Parse 'assign locals.x=5'
|
// Parse 'assign locals.x=5'
|
||||||
const std::string::size_type equalsPos = tokens.size() == 1 ? tokens.front().find('=') : std::string::npos;
|
const std::string::size_type equalsPos = tokens.size() == 1 ? tokens.front().find('=') : std::string::npos;
|
||||||
if (equalsPos == std::string::npos) {
|
if (equalsPos == std::string::npos) {
|
||||||
errorMessage = "Syntax error, expecting 'locals.x=5'.";
|
errorMessage = singleLineUsage(commandDescriptions[CmdAssign]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
const std::string iname = tokens.front().substr(0, equalsPos);
|
const std::string iname = tokens.front().substr(0, equalsPos);
|
||||||
@@ -399,7 +575,13 @@ extern "C" HRESULT CALLBACK idle(CIDebugClient *, PCSTR)
|
|||||||
|
|
||||||
extern "C" HRESULT CALLBACK help(CIDebugClient *, PCSTR)
|
extern "C" HRESULT CALLBACK help(CIDebugClient *, PCSTR)
|
||||||
{
|
{
|
||||||
dprintf("Qt Creator CDB extension built %s\n", __DATE__);
|
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;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -422,7 +604,7 @@ extern "C" HRESULT CALLBACK memory(CIDebugClient *Client, PCSTR argsIn)
|
|||||||
&& integerFromString(tokens.at(1), &length)) {
|
&& integerFromString(tokens.at(1), &length)) {
|
||||||
memory = memoryToBase64(exc.dataSpaces(), address, length, &errorMessage);
|
memory = memoryToBase64(exc.dataSpaces(), address, length, &errorMessage);
|
||||||
} else {
|
} else {
|
||||||
errorMessage = "Invalid parameters to memory command.";
|
errorMessage = singleLineUsage(commandDescriptions[CmdMemory]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (memory.empty()) {
|
if (memory.empty()) {
|
||||||
|
|||||||
@@ -468,7 +468,7 @@ static void fixValue(const std::string &type, std::wstring *value)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Fix long class names on std containers 'class std::tree<...>' -> 'class std::tree<>'
|
// Fix long class names on std containers 'class std::tree<...>' -> 'class std::tree<>'
|
||||||
if (value->compare(0, 6, L"class ") == 0) {
|
if (value->compare(0, 6, L"class ") == 0 || value->compare(0, 7, L"struct ") == 0) {
|
||||||
const std::string::size_type openTemplate = value->find(L'<');
|
const std::string::size_type openTemplate = value->find(L'<');
|
||||||
if (openTemplate != std::string::npos) {
|
if (openTemplate != std::string::npos) {
|
||||||
value->erase(openTemplate + 1, value->size() - openTemplate - 2);
|
value->erase(openTemplate + 1, value->size() - openTemplate - 2);
|
||||||
@@ -665,6 +665,17 @@ void SymbolGroupNode::debug(std::ostream &str, unsigned verbosity, unsigned dept
|
|||||||
str << '\n';
|
str << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline std::string msgCannotCast(const std::string &nodeName,
|
||||||
|
const std::string &fromType,
|
||||||
|
const std::string &toType,
|
||||||
|
const std::string &why)
|
||||||
|
{
|
||||||
|
std::ostringstream str;
|
||||||
|
str << "Cannot cast node '" << nodeName << "' from '" << fromType
|
||||||
|
<< "' to '" << toType << "': " << why;
|
||||||
|
return str.str();
|
||||||
|
}
|
||||||
|
|
||||||
// Expand!
|
// Expand!
|
||||||
bool SymbolGroupNode::expand(std::string *errorMessage)
|
bool SymbolGroupNode::expand(std::string *errorMessage)
|
||||||
{
|
{
|
||||||
@@ -704,6 +715,65 @@ bool SymbolGroupNode::expand(std::string *errorMessage)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SymbolGroupNode::typeCast(const std::string &desiredType, std::string *errorMessage)
|
||||||
|
{
|
||||||
|
const std::string fromType = type();
|
||||||
|
if (fromType == desiredType)
|
||||||
|
return true;
|
||||||
|
if (isExpanded()) {
|
||||||
|
*errorMessage = msgCannotCast(fullIName(), fromType, desiredType, "Already expanded");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
HRESULT hr = m_symbolGroup->debugSymbolGroup()->OutputAsType(m_index, desiredType.c_str());
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
*errorMessage = msgCannotCast(fullIName(), fromType, desiredType, msgDebugEngineComFailed("OutputAsType", hr));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
hr = m_symbolGroup->debugSymbolGroup()->GetSymbolParameters(m_index, 1, &m_parameters);
|
||||||
|
if (FAILED(hr)) { // Should never fail
|
||||||
|
*errorMessage = msgCannotCast(fullIName(), fromType, desiredType, msgDebugEngineComFailed("GetSymbolParameters", hr));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline std::string msgCannotAddSymbol(const std::string &name, const std::string &why)
|
||||||
|
{
|
||||||
|
std::ostringstream str;
|
||||||
|
str << "Cannot add symbol '" << name << "': " << why;
|
||||||
|
return str.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// For root nodes, only: Add a new symbol by name
|
||||||
|
bool SymbolGroupNode::addSymbolByName(const std::string &name,
|
||||||
|
const std::string &iname,
|
||||||
|
std::string *errorMessage)
|
||||||
|
{
|
||||||
|
ULONG index = DEBUG_ANY_ID; // Append
|
||||||
|
HRESULT hr = m_symbolGroup->debugSymbolGroup()->AddSymbol(name.c_str(), &index);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
*errorMessage = msgCannotAddSymbol(name, msgDebugEngineComFailed("AddSymbol", hr));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SymbolParameterVector parameters(1, DEBUG_SYMBOL_PARAMETERS());
|
||||||
|
hr = m_symbolGroup->debugSymbolGroup()->GetSymbolParameters(index, 1, &(*parameters.begin()));
|
||||||
|
if (FAILED(hr)) { // Should never fail
|
||||||
|
*errorMessage = msgCannotAddSymbol(name, msgDebugEngineComFailed("GetSymbolParameters", hr));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Paranoia: Check for cuckoo's eggs (which should not happen)
|
||||||
|
if (parameters.front().ParentSymbol != m_index) {
|
||||||
|
*errorMessage = msgCannotAddSymbol(name, "Parent id mismatch");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SymbolGroupNode *node = new SymbolGroupNode(m_symbolGroup, index,
|
||||||
|
name, iname.empty() ? name : iname,
|
||||||
|
this);
|
||||||
|
node->parseParameters(0, 0, parameters);
|
||||||
|
m_children.push_back(node);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static inline std::string msgNotFound(const std::string &nodeName)
|
static inline std::string msgNotFound(const std::string &nodeName)
|
||||||
{
|
{
|
||||||
std::ostringstream str;
|
std::ostringstream str;
|
||||||
@@ -834,6 +904,26 @@ bool SymbolGroup::expand(const std::string &nodeName, std::string *errorMessage)
|
|||||||
return node->expand(errorMessage);
|
return node->expand(errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cast an (unexpanded) node
|
||||||
|
bool SymbolGroup::typeCast(const std::string &iname, const std::string &desiredType, std::string *errorMessage)
|
||||||
|
{
|
||||||
|
SymbolGroupNode *node = find(iname);
|
||||||
|
if (!node) {
|
||||||
|
*errorMessage = msgNotFound(iname);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (node == m_root) {
|
||||||
|
*errorMessage = "Cannot cast root node";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return node->typeCast(desiredType, errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SymbolGroup::addSymbol(const std::string &name, const std::string &iname, std::string *errorMessage)
|
||||||
|
{
|
||||||
|
return m_root->addSymbolByName(name, iname, errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
// Mark uninitialized (top level only)
|
// Mark uninitialized (top level only)
|
||||||
void SymbolGroup::markUninitialized(const std::vector<std::string> &uniniNodes)
|
void SymbolGroup::markUninitialized(const std::vector<std::string> &uniniNodes)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -53,6 +53,13 @@ struct SymbolGroupValueContext;
|
|||||||
class SymbolGroupNode {
|
class SymbolGroupNode {
|
||||||
SymbolGroupNode(const SymbolGroupNode&);
|
SymbolGroupNode(const SymbolGroupNode&);
|
||||||
SymbolGroupNode& operator=(const SymbolGroupNode&);
|
SymbolGroupNode& operator=(const SymbolGroupNode&);
|
||||||
|
|
||||||
|
explicit SymbolGroupNode(SymbolGroup *symbolGroup,
|
||||||
|
ULONG index,
|
||||||
|
const std::string &name,
|
||||||
|
const std::string &iname,
|
||||||
|
SymbolGroupNode *parent = 0);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum Flags {
|
enum Flags {
|
||||||
Uninitialized = 0x1,
|
Uninitialized = 0x1,
|
||||||
@@ -67,12 +74,6 @@ public:
|
|||||||
typedef SymbolGroupNodePtrVector::iterator SymbolGroupNodePtrVectorIterator;
|
typedef SymbolGroupNodePtrVector::iterator SymbolGroupNodePtrVectorIterator;
|
||||||
typedef SymbolGroupNodePtrVector::const_iterator SymbolGroupNodePtrVectorConstIterator;
|
typedef SymbolGroupNodePtrVector::const_iterator SymbolGroupNodePtrVectorConstIterator;
|
||||||
|
|
||||||
explicit SymbolGroupNode(SymbolGroup *symbolGroup,
|
|
||||||
ULONG index,
|
|
||||||
const std::string &name,
|
|
||||||
const std::string &iname,
|
|
||||||
SymbolGroupNode *parent = 0);
|
|
||||||
|
|
||||||
~SymbolGroupNode() { removeChildren(); }
|
~SymbolGroupNode() { removeChildren(); }
|
||||||
|
|
||||||
void removeChildren();
|
void removeChildren();
|
||||||
@@ -81,6 +82,10 @@ public:
|
|||||||
const SymbolParameterVector &vec);
|
const SymbolParameterVector &vec);
|
||||||
|
|
||||||
static SymbolGroupNode *create(SymbolGroup *sg, const std::string &name, const SymbolParameterVector &vec);
|
static SymbolGroupNode *create(SymbolGroup *sg, const std::string &name, const SymbolParameterVector &vec);
|
||||||
|
// For root nodes, only: Add a new symbol by name
|
||||||
|
bool addSymbolByName(const std::string &name, // Expression like 'myarray[1]'
|
||||||
|
const std::string &iname, // Desired iname, defaults to name
|
||||||
|
std::string *errorMessage);
|
||||||
|
|
||||||
const std::string &name() const { return m_name; }
|
const std::string &name() const { return m_name; }
|
||||||
std::string fullIName() const;
|
std::string fullIName() const;
|
||||||
@@ -111,6 +116,8 @@ public:
|
|||||||
bool expand(std::string *errorMessage);
|
bool expand(std::string *errorMessage);
|
||||||
bool isExpanded() const { return !m_children.empty(); }
|
bool isExpanded() const { return !m_children.empty(); }
|
||||||
bool canExpand() const { return m_parameters.SubElements > 0; }
|
bool canExpand() const { return m_parameters.SubElements > 0; }
|
||||||
|
// Cast to a different type. Works only on unexpanded nodes
|
||||||
|
bool typeCast(const std::string &desiredType, std::string *errorMessage);
|
||||||
|
|
||||||
ULONG subElements() const { return m_parameters.SubElements; }
|
ULONG subElements() const { return m_parameters.SubElements; }
|
||||||
ULONG index() const { return m_index; }
|
ULONG index() const { return m_index; }
|
||||||
@@ -213,6 +220,12 @@ public:
|
|||||||
// Expand a single node "locals.A.B" requiring that "locals.A.B" is already visible
|
// Expand a single node "locals.A.B" requiring that "locals.A.B" is already visible
|
||||||
// (think mkdir without -p).
|
// (think mkdir without -p).
|
||||||
bool expand(const std::string &node, std::string *errorMessage);
|
bool expand(const std::string &node, std::string *errorMessage);
|
||||||
|
// Cast an (unexpanded) node
|
||||||
|
bool typeCast(const std::string &iname, const std::string &desiredType, std::string *errorMessage);
|
||||||
|
// Add a symbol by name expression
|
||||||
|
bool addSymbol(const std::string &name, // Expression like 'myarray[1]'
|
||||||
|
const std::string &iname, // Desired iname, defaults to name
|
||||||
|
std::string *errorMessage);
|
||||||
|
|
||||||
bool accept(SymbolGroupNodeVisitor &visitor) const;
|
bool accept(SymbolGroupNodeVisitor &visitor) const;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user