1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "extensioncontext.h"
35 #include "outputcallback.h"
36 #include "eventcallback.h"
37 #include "symbolgroup.h"
38 #include "symbolgroupvalue.h"
39 #include "stringutils.h"
40 #include "gdbmihelpers.h"
48 \group qtcreatorcdbext
49 \title Qt Creator CDB extension
51 \brief QtCreatorCDB ext is an extension loaded into CDB.exe (see cdbengine.cpp).
57 \o Notification about the state of the debugging session:
59 \o idle: (hooked with .idle_cmd) debuggee stopped
60 \o accessible: Debuggee stopped, cdb.exe accepts commands
61 \o inaccessible: Debuggee runs, no way to post commands
62 \o session active/inactive: Lost debuggee, terminating.
64 \o Hook up with output/event callbacks and produce formatted output
65 \o Provide some extension commands that produce output in a standardized (GDBMI)
66 format that ends up in handleExtensionMessage().
69 // Data struct and helpers for formatting help
70 struct CommandDescription {
72 const char *description;
76 // Single line of usage: For reporting usage errors back as a single line
77 static std::string singleLineUsage(const CommandDescription &d)
79 std::string rc = "Usage: ";
80 const char *endOfLine = strchr(d.usage, '\n');
81 rc += endOfLine ? std::string(d.usage, endOfLine - d.usage) : std::string(d.usage);
85 // Format description of a command
86 std::ostream &operator<<(std::ostream &str, const CommandDescription &d)
88 str << "Command '" << d.name << "': " << d.description << '\n';
90 str << "Usage: " << d.name << ' ' << d.usage << '\n';
118 static const CommandDescription commandDescriptions[] = {
120 "Prints inferior process id and hooks up output callbacks.",
122 {"expandlocals", "Expands local variables by iname in symbol group.",
123 "[-t token] [-c] <frame-number> <iname1-list>\n"
124 "iname1-list: Comma-separated list of inames\n"
125 "-c complex dumpers"},
127 "Prints local variables of symbol group in GDBMI or debug format",
128 "[-t token] [-v] [T formats] [-I formats] [-f debugfilter] [-c] [-h] [-d]\n[-e expand-list] [-u uninitialized-list]\n"
129 "[-W] [-w watch-iname watch-expression] <frame-number> [iname]\n"
130 "-h human-readable output\n"
131 "-v increase verboseness of dumping\n"
134 "-c complex dumpers\n"
135 "-e expand-list Comma-separated list of inames to be expanded beforehand\n"
136 "-u uninitialized-list Comma-separated list of uninitialized inames\n"
137 "-I formatmap map of 'hex-encoded-iname=typecode'\n"
138 "-T formatmap map of 'hex-encoded-type-name=typecode'\n"
139 "-D Discard existing symbol group\n"
140 "-W Synchronize watch items (-w)\n"
141 "-w iname expression Watch item"},
143 "Prints watches variables of symbol group in GDBMI or debug format",
144 "[-t token] [-v] [T formats] [-I formats] [-f debugfilter] [-c] [-h] [-d] <iname>\n"
145 "-h human-readable output\n"
146 "-v increase verboseness of dumping\n"
149 "-c complex dumpers\n"
150 "-I formatmap map of 'hex-encoded-iname=typecode'\n"
151 "-T formatmap map of 'hex-encoded-type-name=typecode'"},
152 {"dumplocal", "Dumps local variable using simple dumpers (testing command).",
153 "[-t token] <frame-number> <iname>"},
154 {"typecast","Performs a type cast on an unexpanded iname of symbol group.",
155 "[-t token] <frame-number> <iname> <desired-type>"},
156 {"addsymbol","Adds a symbol to symbol group (testing command).",
157 "[-t token] <frame-number> <name-expression> [optional-iname]"},
158 {"assign","Assigns a value to a variable in current symbol group.",
159 "[-t token] <iname=value>"},
160 {"threads","Lists threads in GDBMI format.","[-t token]"},
161 {"registers","Lists registers in GDBMI format","[-t token]"},
162 {"modules","Lists modules in GDBMI format.","[-t token]"},
164 "Reports stop reason in GDBMI format.\n"
165 "Intended to be used with .idle_cmd to obtain proper stop notification.",""},
166 {"help","Prints help.",""},
167 {"memory","Prints memory contents in Base64 encoding.","[-t token] <address> <length>"},
168 {"stack","Prints stack in GDBMI format.","[-t token] [max-frames]"},
169 {"shutdownex","Unhooks output callbacks.\nNeeds to be called explicitly only in case of remote debugging.",""},
170 {"addwatch","Add watch expression","<iname> <expression>"},
171 {"widgetat","Return address of widget at position","<x> <y>"},
172 {"breakpoints","List breakpoints with modules","[-h] [-v]"},
173 {"test","Testing command","-T type | -w watch-expression"}
176 typedef std::vector<std::string> StringVector;
177 typedef std::list<std::string> StringList;
179 static inline bool isOption(const std::string &opt)
181 return opt.size() == 2 && opt.at(0) == '-' && opt != "--";
184 // Helper for commandTokens() below:
185 // Simple splitting of command lines allowing for '"'-quoted tokens
186 // 'typecast local.i "class QString *"' -> ('typecast','local.i','class QString *')
187 template<class Inserter>
188 static inline void splitCommand(PCSTR args, Inserter it)
190 enum State { WhiteSpace, WithinToken, WithinQuoted };
192 State state = WhiteSpace;
194 for (PCSTR p = args; *p; p++) {
202 state = WithinQuoted;
208 current.push_back(c);
220 current.push_back(c);
232 current.push_back(c);
238 if (state == WithinToken) {
244 // Split & Parse the arguments of a command and extract the
245 // optional first integer token argument ('command -t <number> remaining arguments')
246 // Each command takes the 'token' argument and includes it in its output
247 // so that the calling CDB engine in Qt Creator can match the callback.
249 template<class StringContainer>
250 static inline StringContainer commandTokens(PCSTR args, int *token = 0)
252 typedef StringContainer::iterator ContainerIterator;
255 *token = -1; // Handled as 'display' in engine, so that user can type commands
256 StringContainer tokens;
257 splitCommand(args, std::back_inserter(tokens));
259 ContainerIterator it = tokens.begin();
260 if (it != tokens.end() && *it == "-t" && ++it != tokens.end()) {
262 std::istringstream(*it) >> *token;
263 tokens.erase(tokens.begin(), ++it);
268 // Extension command 'pid':
269 // Report back PID so that Qt Creator engine knows whom to DebugBreakProcess.
270 extern "C" HRESULT CALLBACK pid(CIDebugClient *client, PCSTR args)
272 ExtensionContext::instance().hookCallbacks(client);
275 commandTokens<StringList>(args, &token);
276 dprintf("Qt Creator CDB extension version 0.1 %d bit built %s.\n", sizeof(void *) > 4 ? 64 : 32, __DATE__);
277 if (const ULONG pid = currentProcessId(client)) {
278 ExtensionContext::instance().report('R', token, 0, "pid", "%u", pid);
280 ExtensionContext::instance().report('N', token, 0, "pid", "0");
285 // Extension command 'expandlocals':
286 // Expand a comma-separated iname-list of local variables.
288 extern "C" HRESULT CALLBACK expandlocals(CIDebugClient *client, PCSTR args)
290 ExtensionCommandContext exc(client);
292 SymbolGroup *symGroup = 0;
293 std::string errorMessage;
296 StringList tokens = commandTokens<StringList>(args, &token);
298 bool runComplexDumpers = false;
300 if (!tokens.empty() && tokens.front() == "-c") {
301 runComplexDumpers = true;
304 if (tokens.empty() || !integerFromString(tokens.front(), &frame)) {
305 errorMessage = singleLineUsage(commandDescriptions[CmdExpandlocals]);
309 if (tokens.empty()) {
310 errorMessage = singleLineUsage(commandDescriptions[CmdExpandlocals]);
313 split(tokens.front(), ',', std::back_inserter(inames));
316 if (errorMessage.empty())
317 symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), frame, &errorMessage);
320 ExtensionContext::instance().report('N', token, 0, "expandlocals", errorMessage.c_str());
324 const unsigned succeeded = runComplexDumpers ?
325 symGroup->expandListRunComplexDumpers(inames, SymbolGroupValueContext(exc.dataSpaces(), exc.symbols()), &errorMessage) :
326 symGroup->expandList(inames, &errorMessage);
328 ExtensionContext::instance().report('R', token, 0, "expandlocals", "%u/%u %s",
329 succeeded, unsigned(inames.size()), errorMessage.c_str());
333 // Parameters to be shared between watch/locals dump commands
334 struct DumpCommandParameters
336 DumpCommandParameters() : debugOutput(0), verbose(0) {}
338 // Check option off front and remove if parsed
339 enum ParseOptionResult { Ok, Error, OtherOption, EndOfOptions };
340 ParseOptionResult parseOption(StringList *options);
342 unsigned debugOutput;
343 std::string debugFilter;
344 DumpParameters dumpParameters;
348 DumpCommandParameters::ParseOptionResult DumpCommandParameters::parseOption(StringList *options)
350 // Parse all options and erase valid ones from the list
351 if (options->empty())
352 return DumpCommandParameters::EndOfOptions;
354 const std::string &opt = options->front();
356 return DumpCommandParameters::EndOfOptions;
357 const char option = opt.at(1);
358 bool knownOption = true;
364 dumpParameters.dumpFlags |= DumpParameters::DumpHumanReadable;
367 dumpParameters.dumpFlags |= DumpParameters::DumpComplexDumpers;
370 if (options->size() < 2)
372 options->pop_front();
373 debugFilter = options->front();
378 case 'T': // typeformats: 'hex'ed name = formatnumber,...'
379 if (options->size() < 2)
381 options->pop_front();
382 if (options->front().empty())
384 dumpParameters.typeFormats = DumpParameters::decodeFormatArgument(options->front());
386 case 'I': // individual formats: 'hex'ed name = formatnumber,...'
387 if (options->size() < 2)
389 options->pop_front();
390 dumpParameters.individualFormats = DumpParameters::decodeFormatArgument(options->front());
397 options->pop_front();
398 return knownOption ? Ok : OtherOption;
401 // Extension command 'locals':
402 // Display local variables of symbol group in GDBMI or debug output form.
403 // Takes an optional iname which is expanded before displaying (for updateWatchData)
405 static std::string commmandLocals(ExtensionCommandContext &commandExtCtx,PCSTR args, int *token, std::string *errorMessage)
407 typedef WatchesSymbolGroup::InameExpressionMap InameExpressionMap;
408 typedef InameExpressionMap::value_type InameExpressionMapEntry;
411 ExtensionContext &extCtx = ExtensionContext::instance();
412 DumpCommandParameters parameters;
414 StringList tokens = commandTokens<StringList>(args, token);
415 StringVector expandedInames;
416 StringVector uninitializedInames;
417 InameExpressionMap watcherInameExpressionMap;
418 bool watchSynchronization = false;
419 bool discardSymbolGroup = false;
420 // Parse away options
421 for (bool optionLeft = true; optionLeft && !tokens.empty(); ) {
422 switch (parameters.parseOption(&tokens)) {
423 case DumpCommandParameters::Ok:
425 case DumpCommandParameters::Error:
426 *errorMessage = singleLineUsage(commandDescriptions[CmdLocals]);
427 return std::string();
428 case DumpCommandParameters::OtherOption: {
429 const char option = tokens.front().at(1);
433 if (tokens.empty()) {
434 *errorMessage = singleLineUsage(commandDescriptions[CmdLocals]);
435 return std::string();
437 split(tokens.front(), ',', std::back_inserter(uninitializedInames));
441 if (tokens.empty()) {
442 *errorMessage = singleLineUsage(commandDescriptions[CmdLocals]);
443 return std::string();
445 split(tokens.front(), ',', std::back_inserter(expandedInames));
448 case 'w': { // Watcher iname exp
449 if (tokens.size() < 2) {
450 *errorMessage = singleLineUsage(commandDescriptions[CmdLocals]);
451 return std::string();
453 const std::string iname = tokens.front();
455 const std::string expression = tokens.front();
457 watcherInameExpressionMap.insert(InameExpressionMapEntry(iname, expression));
461 watchSynchronization = true;
464 discardSymbolGroup = true;
469 case DumpCommandParameters::EndOfOptions:
476 if (tokens.empty() || !integerFromString(tokens.front(), &frame)) {
477 *errorMessage = singleLineUsage(commandDescriptions[CmdLocals]);
478 return std::string();
483 iname = tokens.front();
485 const SymbolGroupValueContext dumpContext(commandExtCtx.dataSpaces(), commandExtCtx.symbols());
486 if (discardSymbolGroup)
487 extCtx.discardSymbolGroup();
488 SymbolGroup * const symGroup = extCtx.symbolGroup(commandExtCtx.symbols(), commandExtCtx.threadId(), frame, errorMessage);
490 return std::string();
492 if (!uninitializedInames.empty())
493 symGroup->markUninitialized(uninitializedInames);
495 SymbolGroupValue::verbose = parameters.verbose;
497 // Synchronize watches if desired.
498 WatchesSymbolGroup *watchesSymbolGroup = extCtx.watchesSymbolGroup();
499 if (watchSynchronization) {
500 if (watcherInameExpressionMap.empty()) { // No watches..kill group.
501 watchesSymbolGroup = 0;
502 extCtx.discardWatchesSymbolGroup();
503 if (SymbolGroupValue::verbose)
504 DebugPrint() << "Discarding watchers";
506 // Force group into existence
507 watchesSymbolGroup = extCtx.watchesSymbolGroup(commandExtCtx.symbols(), errorMessage);
508 if (!watchesSymbolGroup || !watchesSymbolGroup->synchronize(commandExtCtx.symbols(), watcherInameExpressionMap, errorMessage))
509 return std::string();
514 if (!expandedInames.empty()) {
515 if (parameters.dumpParameters.dumpFlags & DumpParameters::DumpComplexDumpers) {
516 symGroup->expandListRunComplexDumpers(expandedInames, dumpContext, errorMessage);
517 if (watchesSymbolGroup)
518 watchesSymbolGroup->expandListRunComplexDumpers(expandedInames, dumpContext, errorMessage);
520 symGroup->expandList(expandedInames, errorMessage);
521 if (watchesSymbolGroup)
522 watchesSymbolGroup->expandList(expandedInames, errorMessage);
526 // Debug output: Join the 2 symbol groups if no iname is given.
527 if (parameters.debugOutput) {
528 std::string debugRc = symGroup->debug(iname, parameters.debugFilter, parameters.debugOutput - 1);
529 if (iname.empty() && watchesSymbolGroup) {
530 debugRc.push_back('\n');
531 debugRc += watchesSymbolGroup->debug(iname, parameters.debugFilter, parameters.debugOutput - 1);
536 // Dump all: Join the 2 symbol groups '[local.x][watch.0]'->'[local.x,watch.0]'
538 std::string dumpRc = symGroup->dump(dumpContext, parameters.dumpParameters);
539 if (!dumpRc.empty() && watchesSymbolGroup) {
540 std::string watchesDump = watchesSymbolGroup->dump(dumpContext, parameters.dumpParameters);
541 if (!watchesDump.empty()) {
542 dumpRc.erase(dumpRc.size() - 1, 1); // Strip ']'
543 watchesDump[0] = ','; // '[' -> '.'
544 dumpRc += watchesDump;
550 return symGroup->dump(iname, dumpContext, parameters.dumpParameters, errorMessage);
553 extern "C" HRESULT CALLBACK locals(CIDebugClient *client, PCSTR args)
555 ExtensionCommandContext exc(client);
556 std::string errorMessage;
558 const std::string output = commmandLocals(exc, args, &token, &errorMessage);
559 SymbolGroupValue::verbose = 0;
560 if (output.empty()) {
561 ExtensionContext::instance().report('N', token, 0, "locals", errorMessage.c_str());
563 ExtensionContext::instance().reportLong('R', token, "locals", output);
568 // Extension command 'locals':
569 // Display local variables of symbol group in GDBMI or debug output form.
570 // Takes an optional iname which is expanded before displaying (for updateWatchData)
572 static std::string commmandWatches(ExtensionCommandContext &exc, PCSTR args, int *token, std::string *errorMessage)
575 DumpCommandParameters parameters;
576 StringList tokens = commandTokens<StringList>(args, token);
577 // Parse away options
578 for (bool optionLeft = true; optionLeft && !tokens.empty(); ) {
579 switch (parameters.parseOption(&tokens)) {
580 case DumpCommandParameters::Ok:
582 case DumpCommandParameters::Error:
583 case DumpCommandParameters::OtherOption:
584 *errorMessage = singleLineUsage(commandDescriptions[CmdWatches]);
585 return std::string();
586 case DumpCommandParameters::EndOfOptions:
591 const std::string iname = tokens.empty() ? std::string() : tokens.front();
592 if (iname.empty() && !parameters.debugOutput) {
593 *errorMessage = singleLineUsage(commandDescriptions[CmdWatches]);
594 return std::string();
597 const SymbolGroupValueContext dumpContext(exc.dataSpaces(), exc.symbols());
598 SymbolGroup * const symGroup = ExtensionContext::instance().watchesSymbolGroup(exc.symbols(), errorMessage);
600 return std::string();
601 SymbolGroupValue::verbose = parameters.verbose;
603 if (parameters.debugOutput)
604 return symGroup->debug(iname, parameters.debugFilter, parameters.debugOutput - 1);
605 return symGroup->dump(iname, dumpContext, parameters.dumpParameters, errorMessage);
608 extern "C" HRESULT CALLBACK watches(CIDebugClient *client, PCSTR args)
610 ExtensionCommandContext exc(client);
611 std::string errorMessage = "e";
613 const std::string output = commmandWatches(exc, args, &token, &errorMessage);
614 SymbolGroupValue::verbose = 0;
615 if (output.empty()) {
616 ExtensionContext::instance().report('N', token, 0, "locals", errorMessage.c_str());
618 ExtensionContext::instance().reportLong('R', token, "locals", output);
623 // Extension command 'dumplocal':
624 // Dump a local variable using dumpers (testing command).
626 static std::string dumplocalHelper(ExtensionCommandContext &exc,PCSTR args, int *token, std::string *errorMessage)
629 StringList tokens = commandTokens<StringList>(args, token);
632 if (tokens.empty() || integerFromString(tokens.front(), &frame)) {
633 *errorMessage = singleLineUsage(commandDescriptions[CmdDumplocal]);
634 return std::string();
637 if (tokens.empty()) {
638 *errorMessage = singleLineUsage(commandDescriptions[CmdDumplocal]);
639 return std::string();
641 const std::string iname = tokens.front();
643 SymbolGroup * const symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), frame, errorMessage);
645 return std::string();
647 AbstractSymbolGroupNode *n = symGroup->find(iname);
648 if (!n || !n->asSymbolGroupNode()) {
649 *errorMessage = "No such iname " + iname;
650 return std::string();
653 if (!dumpSimpleType(n->asSymbolGroupNode(), SymbolGroupValueContext(exc.dataSpaces(), exc.symbols()), &value)) {
654 *errorMessage = "Cannot dump " + iname;
655 return std::string();
657 return wStringToString(value);
660 extern "C" HRESULT CALLBACK dumplocal(CIDebugClient *client, PCSTR argsIn)
662 ExtensionCommandContext exc(client);
663 std::string errorMessage;
665 const std::string value = dumplocalHelper(exc,argsIn, &token, &errorMessage);
667 ExtensionContext::instance().report('N', token, 0, "dumplocal", errorMessage.c_str());
669 ExtensionContext::instance().reportLong('R', token, "dumplocal", value);
674 // Extension command 'typecast':
675 // Change the type of a symbol group entry (testing purposes)
677 extern "C" HRESULT CALLBACK typecast(CIDebugClient *client, PCSTR args)
679 ExtensionCommandContext exc(client);
681 SymbolGroup *symGroup = 0;
682 std::string errorMessage;
685 const StringVector tokens = commandTokens<StringVector>(args, &token);
687 std::string desiredType;
688 if (tokens.size() == 3u && integerFromString(tokens.front(), &frame)) {
689 symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), frame, &errorMessage);
690 iname = tokens.at(1);
691 desiredType = tokens.at(2);
693 errorMessage = singleLineUsage(commandDescriptions[CmdTypecast]);
695 if (symGroup != 0 && symGroup->typeCast(iname, desiredType, &errorMessage)) {
696 ExtensionContext::instance().report('R', token, 0, "typecast", "OK");
698 ExtensionContext::instance().report('N', token, 0, "typecast", errorMessage.c_str());
703 // Extension command 'addsymbol':
704 // Adds a symbol to a symbol group by name (testing purposes)
706 extern "C" HRESULT CALLBACK addsymbol(CIDebugClient *client, PCSTR args)
708 ExtensionCommandContext exc(client);
710 SymbolGroup *symGroup = 0;
711 std::string errorMessage;
714 const StringVector tokens = commandTokens<StringVector>(args, &token);
717 if (tokens.size() >= 2u && integerFromString(tokens.front(), &frame)) {
718 symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), frame, &errorMessage);
720 if (tokens.size() >= 3)
721 iname = tokens.at(2);
723 errorMessage = singleLineUsage(commandDescriptions[CmdAddsymbol]);
725 if (symGroup != 0 && symGroup->addSymbol(std::string(), name, iname, &errorMessage)) {
726 ExtensionContext::instance().report('R', token, 0, "addsymbol", "OK");
728 ExtensionContext::instance().report('N', token, 0, "addsymbol", errorMessage.c_str());
733 extern "C" HRESULT CALLBACK addwatch(CIDebugClient *client, PCSTR argsIn)
735 ExtensionCommandContext exc(client);
737 std::string errorMessage;
738 std::string watchExpression;
742 bool success = false;
744 StringList tokens = commandTokens<StringList>(argsIn, &token);
745 if (tokens.size() != 2) {
746 errorMessage = singleLineUsage(commandDescriptions[CmdAddWatch]);
749 iname = tokens.front();
751 watchExpression = tokens.front();
753 WatchesSymbolGroup *watchesSymGroup = ExtensionContext::instance().watchesSymbolGroup(exc.symbols(), &errorMessage);
754 if (!watchesSymGroup)
756 success = watchesSymGroup->addWatch(exc.symbols(), iname, watchExpression, &errorMessage);
760 ExtensionContext::instance().report('R', token, 0, "addwatch", "Ok");
762 ExtensionContext::instance().report('N', token, 0, "addwatch", errorMessage.c_str());
767 // Extension command 'assign':
768 // Assign locals by iname: 'assign locals.x=5'
770 extern "C" HRESULT CALLBACK assign(CIDebugClient *client, PCSTR argsIn)
772 ExtensionCommandContext exc(client);
774 std::string errorMessage;
775 bool success = false;
779 const StringList tokens = commandTokens<StringList>(argsIn, &token);
780 // Parse 'assign locals.x=5'
781 const std::string::size_type equalsPos = tokens.size() == 1 ? tokens.front().find('=') : std::string::npos;
782 if (equalsPos == std::string::npos) {
783 errorMessage = singleLineUsage(commandDescriptions[CmdAssign]);
786 const std::string iname = tokens.front().substr(0, equalsPos);
787 const std::string value = tokens.front().substr(equalsPos + 1, tokens.front().size() - equalsPos - 1);
788 // get the symbolgroup
789 const int currentFrame = ExtensionContext::instance().symbolGroupFrame();
790 if (currentFrame < 0) {
791 errorMessage = "No current frame.";
794 SymbolGroup *symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), currentFrame, &errorMessage);
797 success = symGroup->assign(iname, value, &errorMessage);
801 ExtensionContext::instance().report('R', token, 0, "assign", "Ok");
803 ExtensionContext::instance().report('N', token, 0, "assign", errorMessage.c_str());
808 // Extension command 'threads':
809 // List all thread info in GDBMI syntax
811 extern "C" HRESULT CALLBACK threads(CIDebugClient *client, PCSTR argsIn)
813 ExtensionCommandContext exc(client);
814 std::string errorMessage;
817 commandTokens<StringList>(argsIn, &token);
819 const std::string gdbmi = gdbmiThreadList(exc.systemObjects(),
825 ExtensionContext::instance().report('N', token, 0, "threads", errorMessage.c_str());
827 ExtensionContext::instance().reportLong('R', token, "threads", gdbmi);
832 // Extension command 'registers':
833 // List all registers in GDBMI syntax
835 extern "C" HRESULT CALLBACK registers(CIDebugClient *Client, PCSTR argsIn)
837 ExtensionCommandContext exc(Client);
838 std::string errorMessage;
841 const StringList tokens = commandTokens<StringList>(argsIn, &token);
842 const bool humanReadable = !tokens.empty() && tokens.front() == "-h";
843 const std::string regs = gdbmiRegisters(exc.registers(), exc.control(), humanReadable, IncludePseudoRegisters, &errorMessage);
845 ExtensionContext::instance().report('N', token, 0, "registers", errorMessage.c_str());
847 ExtensionContext::instance().reportLong('R', token, "registers", regs);
852 // Extension command 'modules':
853 // List all modules in GDBMI syntax
855 extern "C" HRESULT CALLBACK modules(CIDebugClient *Client, PCSTR argsIn)
857 ExtensionCommandContext exc(Client);
858 std::string errorMessage;
861 const StringList tokens = commandTokens<StringList>(argsIn, &token);
862 const bool humanReadable = !tokens.empty() && tokens.front() == "-h";
863 const std::string modules = gdbmiModules(exc.symbols(), humanReadable, &errorMessage);
864 if (modules.empty()) {
865 ExtensionContext::instance().report('N', token, 0, "modules", errorMessage.c_str());
867 ExtensionContext::instance().reportLong('R', token, "modules", modules);
872 // Report stop of debuggee to Creator. This is hooked up as .idle_cmd command
873 // by the Creator engine to reliably get notified about stops.
874 extern "C" HRESULT CALLBACK idle(CIDebugClient *client, PCSTR)
876 ExtensionContext::instance().notifyIdleCommand(client);
880 // Extension command 'help':
883 extern "C" HRESULT CALLBACK help(CIDebugClient *, PCSTR)
885 std::ostringstream str;
886 str << "### Qt Creator CDB extension built " << __DATE__ << "\n\n";
888 const size_t commandCount = sizeof(commandDescriptions)/sizeof(CommandDescription);
889 std::copy(commandDescriptions, commandDescriptions + commandCount,
890 std::ostream_iterator<CommandDescription>(str));
891 dprintf("%s\n", str.str().c_str());
895 // Extension command 'memory':
896 // Display memory as base64
898 extern "C" HRESULT CALLBACK memory(CIDebugClient *Client, PCSTR argsIn)
900 ExtensionCommandContext exc(Client);
901 std::string errorMessage;
908 const StringVector tokens = commandTokens<StringVector>(argsIn, &token);
909 if (tokens.size() == 2
910 && integerFromString(tokens.front(), &address)
911 && integerFromString(tokens.at(1), &length)) {
912 memory = memoryToBase64(exc.dataSpaces(), address, length, &errorMessage);
914 errorMessage = singleLineUsage(commandDescriptions[CmdMemory]);
917 if (memory.empty()) {
918 ExtensionContext::instance().report('N', token, 0, "memory", errorMessage.c_str());
920 ExtensionContext::instance().reportLong('R', token, "memory", memory);
921 if (!errorMessage.empty())
922 ExtensionContext::instance().report('W', token, 0, "memory", errorMessage.c_str());
927 // Extension command 'stack'
928 // Report stack correctly as 'k' does not list instruction pointer
930 extern "C" HRESULT CALLBACK stack(CIDebugClient *Client, PCSTR argsIn)
932 ExtensionCommandContext exc(Client);
933 std::string errorMessage;
936 bool humanReadable = false;
937 unsigned maxFrames = ExtensionContext::maxStackFrames;
939 StringList tokens = commandTokens<StringList>(argsIn, &token);
940 if (!tokens.empty() && tokens.front() == "-h") {
941 humanReadable = true;
945 integerFromString(tokens.front(), &maxFrames);
947 const std::string stack = gdbmiStack(exc.control(), exc.symbols(),
948 maxFrames, humanReadable, &errorMessage);
951 ExtensionContext::instance().report('N', token, 0, "stack", errorMessage.c_str());
953 ExtensionContext::instance().reportLong('R', token, "stack", stack);
958 // Extension command 'shutdownex' (shutdown is reserved):
959 // Unhook the output callbacks. This is normally done by the session
960 // inaccessible notification, however, this does not work for remote-controlled sessions.
961 extern "C" HRESULT CALLBACK shutdownex(CIDebugClient *, PCSTR)
963 ExtensionContext::instance().unhookCallbacks();
967 extern "C" HRESULT CALLBACK widgetat(CIDebugClient *client, PCSTR argsIn)
969 ExtensionCommandContext exc(client);
971 std::string widgetAddress;
972 std::string errorMessage;
978 const StringVector tokens = commandTokens<StringVector>(argsIn, &token);
979 if (tokens.size() != 2) {
980 errorMessage = singleLineUsage(commandDescriptions[CmdWidgetAt]);
983 if (!integerFromString(tokens.front(), &x) || !integerFromString(tokens.at(1), &y)) {
984 errorMessage = singleLineUsage(commandDescriptions[CmdWidgetAt]);
987 widgetAddress = widgetAt(SymbolGroupValueContext(exc.dataSpaces(), exc.symbols()),
988 x, y, &errorMessage);
991 if (widgetAddress.empty()) {
992 ExtensionContext::instance().report('N', token, 0, "widgetat", errorMessage.c_str());
994 ExtensionContext::instance().reportLong('R', token, "widgetat", widgetAddress);
999 extern "C" HRESULT CALLBACK breakpoints(CIDebugClient *client, PCSTR argsIn)
1001 ExtensionCommandContext exc(client);
1003 std::string errorMessage;
1004 bool humanReadable = false;
1005 unsigned verbose = 0;
1006 StringList tokens = commandTokens<StringList>(argsIn, &token);
1007 while (!tokens.empty() && tokens.front().size() == 2 && tokens.front().at(0) == '-') {
1008 switch (tokens.front().at(1)) {
1010 humanReadable = true;
1018 const std::string bp = gdbmiBreakpoints(exc.control(), exc.symbols(), humanReadable, verbose, &errorMessage);
1020 ExtensionContext::instance().report('N', token, 0, "breakpoints", errorMessage.c_str());
1022 ExtensionContext::instance().reportLong('R', token, "breakpoints", bp);
1027 extern "C" HRESULT CALLBACK test(CIDebugClient *client, PCSTR argsIn)
1029 enum Mode { Invalid, TestType, TestFixWatchExpression };
1030 ExtensionCommandContext exc(client);
1032 std::string testType;
1033 Mode mode = Invalid;
1035 StringList tokens = commandTokens<StringList>(argsIn, &token);
1036 // Parse away options
1037 while (!tokens.empty() && tokens.front().size() == 2 && tokens.front().at(0) == '-') {
1038 const char option = tokens.front().at(1);
1043 if (!tokens.empty()) {
1044 testType = tokens.front();
1049 mode = TestFixWatchExpression;
1050 if (!tokens.empty()) {
1051 testType = tokens.front();
1059 if (mode == Invalid || testType.empty()) {
1060 ExtensionContext::instance().report('N', token, 0, "test", singleLineUsage(commandDescriptions[CmdTest]).c_str());
1062 std::ostringstream str;
1067 const KnownType kt = knownType(testType, 0);
1068 const std::string fixed = SymbolGroupValue::stripConst(testType);
1069 const unsigned size = SymbolGroupValue::sizeOf(fixed.c_str());
1070 str << '"' << testType << "\" (" << fixed << ") " << kt << " [";
1071 formatKnownTypeFlags(str, kt);
1072 str << "] size=" << size;
1075 case TestFixWatchExpression:
1076 str << testType << " -> " << WatchesSymbolGroup::fixWatchExpression(exc.symbols(), testType);
1079 ExtensionContext::instance().reportLong('R', token, "test", str.str());
1084 // Hook for dumping Known Structs.
1085 // Shows up in 'dv' (appended) as well as IDebugSymbolGroup::GetValueText.
1087 extern "C" HRESULT CALLBACK KnownStructOutput(ULONG Flag, ULONG64 Address, PSTR StructName, PSTR Buffer, PULONG BufferSize)
1089 static const char knownTypesC[] = "HWND__\0"; // implicitly adds terminating 0
1090 if (Flag == DEBUG_KNOWN_STRUCT_GET_NAMES) {
1091 memcpy(Buffer, knownTypesC, sizeof(knownTypesC));
1094 // Usually 260 chars buf
1095 if (!strcmp(StructName, "HWND__")) {
1096 // Dump a HWND. This is usually passed around as 'HWND*' with an (opaque) pointer value.
1097 // CDB dereferences it and passes it on here, which is why we get it as address.
1099 enum { BufSize = 1000 };
1101 std::ostringstream str;
1104 memset(&rectangle, 0, sizeof(RECT));
1105 memset(&hwnd, 0, sizeof(HWND));
1106 // Coerce the address into a HWND (little endian)
1107 memcpy(&hwnd, &Address, SymbolGroupValue::pointerSize());
1108 // Dump geometry and class.
1109 if (GetWindowRect(hwnd, &rectangle) && GetClassNameA(hwnd, buf, BufSize))
1110 str << ' ' << rectangle.left << '+' << rectangle.top << '+'
1111 << (rectangle.right - rectangle.left) << 'x' << (rectangle.bottom - rectangle.top)
1112 << " '" << buf << '\'';
1113 // Check size for string + 1;
1114 const std::string rc = str.str();
1115 const ULONG requiredBufferLength = static_cast<ULONG>(rc.size()) + 1;
1116 if (requiredBufferLength >= *BufferSize) {
1117 *BufferSize = requiredBufferLength;
1120 strcpy(Buffer, rc.c_str());