OSDN Git Service

35f9e3a5712722ab33d6b9412ea1ce935f6aa810
[qt-creator-jp/qt-creator-jp.git] / src / libs / qtcreatorcdbext / qtcreatorcdbextension.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
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
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 **
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.
24 **
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.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
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"
41
42 #include <cstdio>
43 #include <sstream>
44 #include <list>
45 #include <iterator>
46
47 /*!
48     \group qtcreatorcdbext
49     \title Qt Creator CDB extension
50
51     \brief  QtCreatorCDB ext is an extension loaded into CDB.exe (see cdbengine.cpp).
52
53
54     It provides
55
56     \list
57     \o Notification about the state of the debugging session:
58     \list
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.
63     \endlist
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().
67 */
68
69 // Data struct and helpers for formatting help
70 struct CommandDescription {
71     const char *name;
72     const char *description;
73     const char *usage;
74 };
75
76 // Single line of usage: For reporting usage errors back as a single line
77 static std::string singleLineUsage(const CommandDescription &d)
78 {
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);
82     return rc;
83 }
84
85 // Format description of a command
86 std::ostream &operator<<(std::ostream &str, const CommandDescription &d)
87 {
88     str << "Command '" << d.name << "': " << d.description << '\n';
89     if (d.usage[0])
90         str << "Usage: " << d.name << ' ' << d.usage << '\n';
91     str << '\n';
92     return str;
93 }
94
95 enum Command {
96     CmdPid,
97     CmdExpandlocals,
98     CmdLocals,
99     CmdWatches,
100     CmdDumplocal,
101     CmdTypecast,
102     CmdAddsymbol,
103     CmdAssign,
104     CmdThreads,
105     CmdRegisters,
106     CmdModules,
107     CmdIdle,
108     CmdHelp,
109     CmdMemory,
110     CmdStack,
111     CmdShutdownex,
112     CmdAddWatch,
113     CmdWidgetAt,
114     CmdBreakPoints,
115     CmdTest
116 };
117
118 static const CommandDescription commandDescriptions[] = {
119 {"pid",
120  "Prints inferior process id and hooks up output callbacks.",
121  "[-t token]"},
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"},
126 {"locals",
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"
132  "-d debug output\n"
133  "-f debug_filter\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"},
142 {"watches",
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"
147  "-d debug output\n"
148  "-f debug_filter\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]"},
163 {"idle",
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"}
174 };
175
176 typedef std::vector<std::string> StringVector;
177 typedef std::list<std::string> StringList;
178
179 static inline bool isOption(const std::string &opt)
180 {
181     return opt.size() == 2 && opt.at(0) == '-' && opt != "--";
182 }
183
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)
189 {
190     enum State { WhiteSpace, WithinToken, WithinQuoted };
191
192     State state = WhiteSpace;
193     std::string current;
194     for (PCSTR p = args; *p; p++) {
195         char c = *p;
196         switch (state) {
197         case WhiteSpace:
198             switch (c) {
199             case ' ':
200                 break;
201             case '"':
202                 state = WithinQuoted;
203                 current.clear();
204                 break;
205             default:
206                 state = WithinToken;
207                 current.clear();
208                 current.push_back(c);
209                 break;
210             }
211             break;
212         case WithinToken:
213             switch (c) {
214             case ' ':
215                 state = WhiteSpace;
216                 *it = current;
217                 ++it;
218                 break;
219             default:
220                 current.push_back(c);
221                 break;
222             }
223             break;
224         case WithinQuoted:
225             switch (c) {
226             case '"':
227                 state = WhiteSpace;
228                 *it = current;
229                 ++it;
230                 break;
231             default:
232                 current.push_back(c);
233                 break;
234             }
235             break;
236         }
237     }
238     if (state == WithinToken) {
239         *it = current;
240         ++it;
241     }
242 }
243
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.
248
249 template<class StringContainer>
250 static inline StringContainer commandTokens(PCSTR args, int *token = 0)
251 {
252     typedef StringContainer::iterator ContainerIterator;
253
254     if (token)
255         *token = -1; // Handled as 'display' in engine, so that user can type commands
256     StringContainer tokens;
257     splitCommand(args, std::back_inserter(tokens));
258     // Check for token
259     ContainerIterator it = tokens.begin();
260     if (it != tokens.end() && *it == "-t" && ++it != tokens.end()) {
261         if (token)
262             std::istringstream(*it) >> *token;
263         tokens.erase(tokens.begin(), ++it);
264     }
265     return tokens;
266 }
267
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)
271 {
272     ExtensionContext::instance().hookCallbacks(client);
273
274     int token;
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);
279     } else {
280         ExtensionContext::instance().report('N', token, 0, "pid", "0");
281     }
282     return S_OK;
283 }
284
285 // Extension command 'expandlocals':
286 // Expand a comma-separated iname-list of local variables.
287
288 extern "C" HRESULT CALLBACK expandlocals(CIDebugClient *client, PCSTR args)
289 {
290     ExtensionCommandContext exc(client);
291     unsigned frame = 0;
292     SymbolGroup *symGroup = 0;
293     std::string errorMessage;
294
295     int token;
296     StringList tokens = commandTokens<StringList>(args, &token);
297     StringVector inames;
298     bool runComplexDumpers = false;
299     do {
300         if (!tokens.empty() && tokens.front() == "-c") {
301             runComplexDumpers = true;
302             tokens.pop_front();
303         }
304         if (tokens.empty() || !integerFromString(tokens.front(), &frame))  {
305             errorMessage = singleLineUsage(commandDescriptions[CmdExpandlocals]);
306             break;
307         }
308         tokens.pop_front();
309         if (tokens.empty()) {
310             errorMessage = singleLineUsage(commandDescriptions[CmdExpandlocals]);
311             break;
312         }
313         split(tokens.front(), ',', std::back_inserter(inames));
314     } while (false);
315
316     if (errorMessage.empty())
317         symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), frame, &errorMessage);
318
319     if (!symGroup) {
320         ExtensionContext::instance().report('N', token, 0, "expandlocals", errorMessage.c_str());
321         return S_OK;
322     }
323
324     const unsigned succeeded = runComplexDumpers ?
325         symGroup->expandListRunComplexDumpers(inames, SymbolGroupValueContext(exc.dataSpaces(), exc.symbols()), &errorMessage) :
326         symGroup->expandList(inames, &errorMessage);
327
328     ExtensionContext::instance().report('R', token, 0, "expandlocals", "%u/%u %s",
329                                         succeeded, unsigned(inames.size()), errorMessage.c_str());
330     return S_OK;
331 }
332
333 // Parameters to be shared between watch/locals dump commands
334 struct DumpCommandParameters
335 {
336     DumpCommandParameters() : debugOutput(0), verbose(0) {}
337
338     // Check option off front and remove if parsed
339     enum ParseOptionResult { Ok, Error, OtherOption, EndOfOptions };
340     ParseOptionResult parseOption(StringList *options);
341
342     unsigned debugOutput;
343     std::string debugFilter;
344     DumpParameters dumpParameters;
345     unsigned verbose;
346 };
347
348 DumpCommandParameters::ParseOptionResult DumpCommandParameters::parseOption(StringList *options)
349 {
350     // Parse all options and erase valid ones from the list
351     if (options->empty())
352         return DumpCommandParameters::EndOfOptions;
353
354     const std::string &opt = options->front();
355     if (!isOption(opt))
356         return DumpCommandParameters::EndOfOptions;
357     const char option = opt.at(1);
358     bool knownOption = true;
359     switch (option) {
360     case 'd':
361         debugOutput++;
362         break;
363     case 'h':
364         dumpParameters.dumpFlags |= DumpParameters::DumpHumanReadable;
365         break;
366     case 'c':
367         dumpParameters.dumpFlags |= DumpParameters::DumpComplexDumpers;
368         break;
369     case 'f':
370         if (options->size() < 2)
371             return Error;
372         options->pop_front();
373         debugFilter = options->front();
374         break;
375     case 'v':
376         verbose++;
377         break;
378     case 'T': // typeformats: 'hex'ed name = formatnumber,...'
379         if (options->size() < 2)
380             return Error;
381         options->pop_front();
382         if (options->front().empty())
383             return Error;
384         dumpParameters.typeFormats = DumpParameters::decodeFormatArgument(options->front());
385         break;
386     case 'I': // individual formats: 'hex'ed name = formatnumber,...'
387         if (options->size() < 2)
388             return Error;
389         options->pop_front();
390         dumpParameters.individualFormats = DumpParameters::decodeFormatArgument(options->front());
391         break;
392     default:
393         knownOption = false;
394         break;
395     } // case option
396     if (knownOption)
397         options->pop_front();
398     return knownOption ? Ok : OtherOption;
399 }
400
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)
404
405 static std::string commmandLocals(ExtensionCommandContext &commandExtCtx,PCSTR args, int *token, std::string *errorMessage)
406 {
407     typedef WatchesSymbolGroup::InameExpressionMap InameExpressionMap;
408     typedef InameExpressionMap::value_type InameExpressionMapEntry;
409
410     // Parse the command
411     ExtensionContext &extCtx = ExtensionContext::instance();
412     DumpCommandParameters parameters;
413     std::string iname;
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:
424             break;
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);
430             tokens.pop_front();
431             switch (option) {
432             case 'u':
433                 if (tokens.empty()) {
434                     *errorMessage = singleLineUsage(commandDescriptions[CmdLocals]);
435                     return std::string();
436                 }
437                 split(tokens.front(), ',', std::back_inserter(uninitializedInames));
438                 tokens.pop_front();
439                 break;
440             case 'e':
441                 if (tokens.empty()) {
442                     *errorMessage = singleLineUsage(commandDescriptions[CmdLocals]);
443                     return std::string();
444                 }
445                 split(tokens.front(), ',', std::back_inserter(expandedInames));
446                 tokens.pop_front();
447                 break;
448             case 'w':  { // Watcher iname exp
449                 if (tokens.size() < 2) {
450                     *errorMessage = singleLineUsage(commandDescriptions[CmdLocals]);
451                     return std::string();
452                 }
453                 const std::string iname = tokens.front();
454                 tokens.pop_front();
455                 const std::string expression = tokens.front();
456                 tokens.pop_front();
457                 watcherInameExpressionMap.insert(InameExpressionMapEntry(iname, expression));
458             }
459             break;
460             case 'W':
461                 watchSynchronization = true;
462                 break;
463             case 'D':
464                 discardSymbolGroup = true;
465                 break;
466             } // case option
467         }
468         break;
469         case DumpCommandParameters::EndOfOptions:
470             optionLeft = false;
471             break;
472         }
473     }
474     // Frame and iname
475     unsigned frame;
476     if (tokens.empty() || !integerFromString(tokens.front(), &frame)) {
477         *errorMessage = singleLineUsage(commandDescriptions[CmdLocals]);
478         return std::string();
479     }
480
481     tokens.pop_front();
482     if (!tokens.empty())
483         iname = tokens.front();
484
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);
489     if (!symGroup)
490         return std::string();
491
492     if (!uninitializedInames.empty())
493         symGroup->markUninitialized(uninitializedInames);
494
495     SymbolGroupValue::verbose = parameters.verbose;
496
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";
505         } else {
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();
510         }
511     }
512
513     // Pre-expand.
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);
519         } else {
520             symGroup->expandList(expandedInames, errorMessage);
521             if (watchesSymbolGroup)
522                 watchesSymbolGroup->expandList(expandedInames, errorMessage);
523         }
524     }
525
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);
532         }
533         return debugRc;
534     }
535
536     // Dump all: Join the 2 symbol groups '[local.x][watch.0]'->'[local.x,watch.0]'
537     if (iname.empty()) {
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;
545             }
546         }
547         return dumpRc;
548     }
549
550     return symGroup->dump(iname, dumpContext, parameters.dumpParameters, errorMessage);
551 }
552
553 extern "C" HRESULT CALLBACK locals(CIDebugClient *client, PCSTR args)
554 {
555     ExtensionCommandContext exc(client);
556     std::string errorMessage;
557     int token;
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());
562     } else {
563         ExtensionContext::instance().reportLong('R', token, "locals", output);
564     }
565     return S_OK;
566 }
567
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)
571
572 static std::string commmandWatches(ExtensionCommandContext &exc, PCSTR args, int *token, std::string *errorMessage)
573 {
574     // Parse the command
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:
581             break;
582         case DumpCommandParameters::Error:
583         case DumpCommandParameters::OtherOption:
584             *errorMessage = singleLineUsage(commandDescriptions[CmdWatches]);
585             return std::string();
586         case DumpCommandParameters::EndOfOptions:
587             optionLeft = false;
588             break;
589         }
590     }
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();
595     }
596
597     const SymbolGroupValueContext dumpContext(exc.dataSpaces(), exc.symbols());
598     SymbolGroup * const symGroup = ExtensionContext::instance().watchesSymbolGroup(exc.symbols(), errorMessage);
599     if (!symGroup)
600         return std::string();
601     SymbolGroupValue::verbose = parameters.verbose;
602
603     if (parameters.debugOutput)
604         return symGroup->debug(iname, parameters.debugFilter, parameters.debugOutput - 1);
605     return symGroup->dump(iname, dumpContext, parameters.dumpParameters, errorMessage);
606 }
607
608 extern "C" HRESULT CALLBACK watches(CIDebugClient *client, PCSTR args)
609 {
610     ExtensionCommandContext exc(client);
611     std::string errorMessage = "e";
612     int token = 0;
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());
617     } else {
618         ExtensionContext::instance().reportLong('R', token, "locals", output);
619     }
620     return S_OK;
621 }
622
623 // Extension command 'dumplocal':
624 // Dump a local variable using dumpers (testing command).
625
626 static std::string dumplocalHelper(ExtensionCommandContext &exc,PCSTR args, int *token, std::string *errorMessage)
627 {
628     // Parse the command
629     StringList tokens = commandTokens<StringList>(args, token);
630     // Frame and iname
631     unsigned frame;
632     if (tokens.empty() || integerFromString(tokens.front(), &frame)) {
633         *errorMessage = singleLineUsage(commandDescriptions[CmdDumplocal]);
634         return std::string();
635     }
636     tokens.pop_front();
637     if (tokens.empty()) {
638         *errorMessage = singleLineUsage(commandDescriptions[CmdDumplocal]);
639         return std::string();
640     }
641     const std::string iname = tokens.front();
642
643     SymbolGroup * const symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), frame, errorMessage);
644     if (!symGroup)
645         return std::string();
646
647     AbstractSymbolGroupNode *n = symGroup->find(iname);
648     if (!n || !n->asSymbolGroupNode()) {
649         *errorMessage = "No such iname " + iname;
650         return std::string();
651     }
652     std::wstring value;
653     if (!dumpSimpleType(n->asSymbolGroupNode(), SymbolGroupValueContext(exc.dataSpaces(), exc.symbols()), &value)) {
654         *errorMessage = "Cannot dump " + iname;
655         return std::string();
656     }
657     return wStringToString(value);
658 }
659
660 extern "C" HRESULT CALLBACK dumplocal(CIDebugClient *client, PCSTR  argsIn)
661 {
662     ExtensionCommandContext exc(client);
663     std::string errorMessage;
664     int token = 0;
665     const std::string value = dumplocalHelper(exc,argsIn, &token, &errorMessage);
666     if (value.empty()) {
667         ExtensionContext::instance().report('N', token, 0, "dumplocal", errorMessage.c_str());
668     } else {
669         ExtensionContext::instance().reportLong('R', token, "dumplocal", value);
670     }
671     return S_OK;
672 }
673
674 // Extension command 'typecast':
675 // Change the type of a symbol group entry (testing purposes)
676
677 extern "C" HRESULT CALLBACK typecast(CIDebugClient *client, PCSTR args)
678 {
679     ExtensionCommandContext exc(client);
680     unsigned frame = 0;
681     SymbolGroup *symGroup = 0;
682     std::string errorMessage;
683
684     int token;
685     const StringVector tokens = commandTokens<StringVector>(args, &token);
686     std::string iname;
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);
692     } else {
693         errorMessage = singleLineUsage(commandDescriptions[CmdTypecast]);
694     }
695     if (symGroup != 0 && symGroup->typeCast(iname, desiredType, &errorMessage)) {
696         ExtensionContext::instance().report('R', token, 0, "typecast", "OK");
697     } else {
698         ExtensionContext::instance().report('N', token, 0, "typecast", errorMessage.c_str());
699     }
700     return S_OK;
701 }
702
703 // Extension command 'addsymbol':
704 // Adds a symbol to a symbol group by name (testing purposes)
705
706 extern "C" HRESULT CALLBACK addsymbol(CIDebugClient *client, PCSTR args)
707 {
708     ExtensionCommandContext exc(client);
709     unsigned frame = 0;
710     SymbolGroup *symGroup = 0;
711     std::string errorMessage;
712
713     int token;
714     const StringVector tokens = commandTokens<StringVector>(args, &token);
715     std::string name;
716     std::string iname;
717     if (tokens.size() >= 2u && integerFromString(tokens.front(), &frame)) {
718         symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), frame, &errorMessage);
719         name = tokens.at(1);
720         if (tokens.size() >= 3)
721             iname = tokens.at(2);
722     } else {
723         errorMessage = singleLineUsage(commandDescriptions[CmdAddsymbol]);
724     }
725     if (symGroup != 0 && symGroup->addSymbol(std::string(), name, iname, &errorMessage)) {
726         ExtensionContext::instance().report('R', token, 0, "addsymbol", "OK");
727     } else {
728         ExtensionContext::instance().report('N', token, 0, "addsymbol", errorMessage.c_str());
729     }
730     return S_OK;
731 }
732
733 extern "C" HRESULT CALLBACK addwatch(CIDebugClient *client, PCSTR argsIn)
734 {
735     ExtensionCommandContext exc(client);
736
737     std::string errorMessage;
738     std::string watchExpression;
739     std::string iname;
740
741     int token = 0;
742     bool success = false;
743     do {
744         StringList tokens = commandTokens<StringList>(argsIn, &token);
745         if (tokens.size() != 2) {
746             errorMessage = singleLineUsage(commandDescriptions[CmdAddWatch]);
747             break;
748         }
749         iname = tokens.front();
750         tokens.pop_front();
751         watchExpression = tokens.front();
752
753         WatchesSymbolGroup *watchesSymGroup = ExtensionContext::instance().watchesSymbolGroup(exc.symbols(), &errorMessage);
754         if (!watchesSymGroup)
755             break;
756         success = watchesSymGroup->addWatch(exc.symbols(), iname, watchExpression, &errorMessage);
757     } while (false);
758
759     if (success) {
760         ExtensionContext::instance().report('R', token, 0, "addwatch", "Ok");
761     } else {
762         ExtensionContext::instance().report('N', token, 0, "addwatch", errorMessage.c_str());
763     }
764     return S_OK;
765 }
766
767 // Extension command 'assign':
768 // Assign locals by iname: 'assign locals.x=5'
769
770 extern "C" HRESULT CALLBACK assign(CIDebugClient *client, PCSTR argsIn)
771 {
772     ExtensionCommandContext exc(client);
773
774     std::string errorMessage;
775     bool success = false;
776
777     int token = 0;
778     do {
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]);
784             break;
785         }
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.";
792             break;
793         }
794         SymbolGroup *symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), currentFrame, &errorMessage);
795         if (!symGroup)
796             break;
797         success = symGroup->assign(iname, value, &errorMessage);
798     } while (false);
799
800     if (success) {
801         ExtensionContext::instance().report('R', token, 0, "assign", "Ok");
802     } else {
803         ExtensionContext::instance().report('N', token, 0, "assign", errorMessage.c_str());
804     }
805     return S_OK;
806 }
807
808 // Extension command 'threads':
809 // List all thread info in GDBMI syntax
810
811 extern "C" HRESULT CALLBACK threads(CIDebugClient *client, PCSTR  argsIn)
812 {
813     ExtensionCommandContext exc(client);
814     std::string errorMessage;
815
816     int token;
817     commandTokens<StringList>(argsIn, &token);
818
819     const std::string gdbmi = gdbmiThreadList(exc.systemObjects(),
820                                               exc.symbols(),
821                                               exc.control(),
822                                               exc.advanced(),
823                                               &errorMessage);
824     if (gdbmi.empty()) {
825         ExtensionContext::instance().report('N', token, 0, "threads", errorMessage.c_str());
826     } else {
827         ExtensionContext::instance().reportLong('R', token, "threads", gdbmi);
828     }
829     return S_OK;
830 }
831
832 // Extension command 'registers':
833 // List all registers in GDBMI syntax
834
835 extern "C" HRESULT CALLBACK registers(CIDebugClient *Client, PCSTR argsIn)
836 {
837     ExtensionCommandContext exc(Client);
838     std::string errorMessage;
839
840     int token;
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);
844     if (regs.empty()) {
845         ExtensionContext::instance().report('N', token, 0, "registers", errorMessage.c_str());
846     } else {
847         ExtensionContext::instance().reportLong('R', token, "registers", regs);
848     }
849     return S_OK;
850 }
851
852 // Extension command 'modules':
853 // List all modules in GDBMI syntax
854
855 extern "C" HRESULT CALLBACK modules(CIDebugClient *Client, PCSTR argsIn)
856 {
857     ExtensionCommandContext exc(Client);
858     std::string errorMessage;
859
860     int token;
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());
866     } else {
867         ExtensionContext::instance().reportLong('R', token, "modules", modules);
868     }
869     return S_OK;
870 }
871
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)
875 {
876     ExtensionContext::instance().notifyIdleCommand(client);
877     return S_OK;
878 }
879
880 // Extension command 'help':
881 // Display version
882
883 extern "C" HRESULT CALLBACK help(CIDebugClient *, PCSTR)
884 {
885     std::ostringstream str;
886     str << "### Qt Creator CDB extension built " << __DATE__ << "\n\n";
887
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());
892     return S_OK;
893 }
894
895 // Extension command 'memory':
896 // Display memory as base64
897
898 extern "C" HRESULT CALLBACK memory(CIDebugClient *Client, PCSTR argsIn)
899 {
900     ExtensionCommandContext exc(Client);
901     std::string errorMessage;
902     std::string memory;
903
904     int token;
905     ULONG64 address = 0;
906     ULONG length = 0;
907
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);
913     } else {
914         errorMessage = singleLineUsage(commandDescriptions[CmdMemory]);
915     }
916
917     if (memory.empty()) {
918         ExtensionContext::instance().report('N', token, 0, "memory", errorMessage.c_str());
919     } else {
920         ExtensionContext::instance().reportLong('R', token, "memory", memory);
921         if (!errorMessage.empty())
922             ExtensionContext::instance().report('W', token, 0, "memory", errorMessage.c_str());
923     }
924     return S_OK;
925 }
926
927 // Extension command 'stack'
928 // Report stack correctly as 'k' does not list instruction pointer
929 // correctly.
930 extern "C" HRESULT CALLBACK stack(CIDebugClient *Client, PCSTR argsIn)
931 {
932     ExtensionCommandContext exc(Client);
933     std::string errorMessage;
934
935     int token;
936     bool humanReadable = false;
937     unsigned maxFrames = ExtensionContext::maxStackFrames;
938
939     StringList tokens = commandTokens<StringList>(argsIn, &token);
940     if (!tokens.empty() && tokens.front() == "-h") {
941          humanReadable = true;
942          tokens.pop_front();
943     }
944     if (!tokens.empty())
945         integerFromString(tokens.front(), &maxFrames);
946
947     const std::string stack = gdbmiStack(exc.control(), exc.symbols(),
948                                          maxFrames, humanReadable, &errorMessage);
949
950     if (stack.empty()) {
951         ExtensionContext::instance().report('N', token, 0, "stack", errorMessage.c_str());
952     } else {
953         ExtensionContext::instance().reportLong('R', token, "stack", stack);
954     }
955     return S_OK;
956 }
957
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)
962 {
963     ExtensionContext::instance().unhookCallbacks();
964     return S_OK;
965 }
966
967 extern "C" HRESULT CALLBACK widgetat(CIDebugClient *client, PCSTR argsIn)
968 {
969     ExtensionCommandContext exc(client);
970     int token = 0;
971     std::string widgetAddress;
972     std::string errorMessage;
973
974     do {
975         int x = -1;
976         int y = -1;
977
978         const StringVector tokens = commandTokens<StringVector>(argsIn, &token);
979         if (tokens.size() != 2) {
980             errorMessage = singleLineUsage(commandDescriptions[CmdWidgetAt]);
981             break;
982         }
983         if (!integerFromString(tokens.front(), &x) || !integerFromString(tokens.at(1), &y)) {
984             errorMessage = singleLineUsage(commandDescriptions[CmdWidgetAt]);
985             break;
986         }
987         widgetAddress = widgetAt(SymbolGroupValueContext(exc.dataSpaces(), exc.symbols()),
988                                  x, y, &errorMessage);
989     } while (false);
990
991     if (widgetAddress.empty()) {
992         ExtensionContext::instance().report('N', token, 0, "widgetat", errorMessage.c_str());
993     } else {
994         ExtensionContext::instance().reportLong('R', token, "widgetat", widgetAddress);
995     }
996     return S_OK;
997 }
998
999 extern "C" HRESULT CALLBACK breakpoints(CIDebugClient *client, PCSTR argsIn)
1000 {
1001     ExtensionCommandContext exc(client);
1002     int token;
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)) {
1009         case 'h':
1010             humanReadable = true;
1011             break;
1012         case 'v':
1013             verbose++;
1014             break;
1015         }
1016         tokens.pop_front();
1017     }
1018     const std::string bp = gdbmiBreakpoints(exc.control(), exc.symbols(), humanReadable, verbose, &errorMessage);
1019     if (bp.empty()) {
1020         ExtensionContext::instance().report('N', token, 0, "breakpoints", errorMessage.c_str());
1021     } else {
1022         ExtensionContext::instance().reportLong('R', token, "breakpoints", bp);
1023     }
1024     return S_OK;
1025 }
1026
1027 extern "C" HRESULT CALLBACK test(CIDebugClient *client, PCSTR argsIn)
1028 {
1029     enum Mode { Invalid, TestType, TestFixWatchExpression };
1030     ExtensionCommandContext exc(client);
1031
1032     std::string testType;
1033     Mode mode = Invalid;
1034     int token = 0;
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);
1039         tokens.pop_front();
1040         switch (option) {
1041         case 'T':
1042             mode = TestType;
1043             if (!tokens.empty()) {
1044                 testType = tokens.front();
1045                 tokens.pop_front();
1046             }
1047             break;
1048         case 'w':
1049             mode = TestFixWatchExpression;
1050             if (!tokens.empty()) {
1051                 testType = tokens.front();
1052                 tokens.pop_front();
1053             }
1054             break;
1055         } // case option
1056     }  // for options
1057
1058     // Frame and iname
1059     if (mode == Invalid || testType.empty()) {
1060         ExtensionContext::instance().report('N', token, 0, "test", singleLineUsage(commandDescriptions[CmdTest]).c_str());
1061     } else {
1062         std::ostringstream str;
1063         switch (mode) {
1064         case Invalid:
1065             break;
1066         case TestType: {
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;
1073         }
1074             break;
1075         case TestFixWatchExpression:
1076             str << testType << " -> " << WatchesSymbolGroup::fixWatchExpression(exc.symbols(), testType);
1077             break;
1078         }
1079         ExtensionContext::instance().reportLong('R', token, "test", str.str());
1080     }
1081     return S_OK;
1082 }
1083
1084 // Hook for dumping Known Structs.
1085 // Shows up in 'dv' (appended) as well as IDebugSymbolGroup::GetValueText.
1086
1087 extern "C"  HRESULT CALLBACK KnownStructOutput(ULONG Flag, ULONG64 Address, PSTR StructName, PSTR Buffer, PULONG BufferSize)
1088 {
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));
1092         return S_OK;
1093     }
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.
1098         RECT rectangle;
1099         enum { BufSize = 1000 };
1100         char buf[BufSize];
1101         std::ostringstream str;
1102
1103         HWND hwnd;
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;
1118             return  S_FALSE;
1119         }
1120         strcpy(Buffer, rc.c_str());
1121     }
1122
1123     return S_OK;
1124 }