OSDN Git Service

debugger: compile fix msvc. done by hjk.
[qt-creator-jp/qt-creator-jp.git] / src / plugins / debugger / cdb / cdbdumperhelper.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** Commercial Usage
10 **
11 ** Licensees holding valid Qt Commercial licenses may use this file in
12 ** accordance with the Qt Commercial License Agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and Nokia.
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 ** If you are unsure which license is appropriate for your use, please
26 ** contact the sales department at http://qt.nokia.com/contact.
27 **
28 **************************************************************************/
29
30 #include "cdbdumperhelper.h"
31 #include "cdbmodules.h"
32 #include "cdbengine.h"
33 #include "cdbengine_p.h"
34 #include "cdbdebugoutput.h"
35 #include "cdbdebugeventcallback.h"
36 #include "cdbsymbolgroupcontext.h"
37 #include "watchhandler.h"
38 #include "cdbexceptionutils.h"
39
40 #include "shared/sharedlibraryinjector.h"
41
42 #include <QtCore/QRegExp>
43 #include <QtCore/QCoreApplication>
44 #include <QtCore/QTextStream>
45 #include <QtCore/QTime>
46 #include <QtCore/QThread>
47 #include <QtCore/QEventLoop>
48 #include <QtGui/QApplication>
49
50 enum { loadDebug = 0 };
51 enum { dumpDebug = 0 };
52
53 static const char *dumperModuleNameC = "gdbmacros";
54 static const char *qtCoreModuleNameC = "QtCore";
55 static const ULONG waitTimeOutMS = 30000;
56 static const char *dumperPrefixC  = "dumper:";
57
58 /* Loading the dumpers is 2 step process:
59  * 1) The library must be loaded into the debuggee, for which there are
60  *    2 approaches:
61  *     - Injection loading using the SharedLibraryInjector which
62  *       launches a remote thread in the debuggee which loads the
63  *       library. Drawbacks:
64  *       * The remote thread must not starve.
65  *       * It is not possible to wait loading and loading occurs late,
66  *         after entering main()
67  *     - Debugger Call loading, which has the debuggee execute
68  *       a LoadLibrary call via debugger commands. Drawbacks:
69  *       * Slow
70  *       * Requires presence of a symbol of the same prototype as
71  *         LoadLibraryA as the original prototype is not sufficient.
72  * 2) Call a query function  (protocol 1 of the dumper) to obtain a list
73  *    of handled types and a map of known sizes.
74  *
75  * The class currently launches injection loading from the module
76  * load hook as soon as it sees a Qt module.
77  * The dumpType() function performs the rest of the [delayed] initialization.
78  * If the load has not finished, it attempts call loading and
79  * executes the initial query protocol.
80  *
81  * Note: The main technique here is having the debuggee call functions
82  * using the .call command (which takes a function with a known
83  * prototype and simple, integer parameters).
84  * This does not work from an IDebugEvent callback, as it will cause
85  * WaitForEvent() to fail with unknown errors.
86  * It mostly works from breakpoints, with the addditional restriction
87  * that complex functions only work from 'well-defined'  breakpoints
88  * (such as main()) and otherwise cause access violation exceptions
89  * (for example LoadLibrary).
90  * Exceptions occurring in the functions to be called must be handled
91  * by __try/__except (they show up in the debugger and must acknowledged
92  * by gN (go not handled).  */
93
94 namespace Debugger {
95 namespace Internal {
96
97 // ------- Call load helpers
98
99     // Load a library into the debuggee. Currently requires
100 // the QtCored4.pdb file to be present as we need "qstrdup"
101 // as dummy symbol. This is ok ATM since dumpers only
102 // make sense for Qt apps.
103 static bool debuggeeLoadLibrary(CdbEngine *cdbEngine,
104                                 CdbCore::CoreEngine *engine,
105                                 unsigned long threadId,
106                                 const QString &moduleName,
107                                 QString *errorMessage)
108 {
109     if (loadDebug > 1)
110         qDebug() << Q_FUNC_INFO << moduleName;
111     // Try to ignore the breakpoints, skip stray startup-complete trap exceptions
112     QSharedPointer<CdbExceptionLoggerEventCallback>
113         exLogger(new CdbExceptionLoggerEventCallback(LogWarning, true, cdbEngine));
114     CdbCore::EventCallbackRedirector eventRedir(engine, exLogger);
115     Q_UNUSED(eventRedir)
116     // Make a call to LoadLibraryA. First, reserve memory in debugger
117     // and copy name over.
118     ULONG64 nameAddress;
119     if (!engine->createDebuggeeAscIIString(moduleName, &nameAddress, errorMessage))
120         return false;
121     // We want to call "HMODULE LoadLibraryA(LPCTSTR lpFileName)"
122     // (void* LoadLibraryA(char*)). However, despite providing a symbol
123     // server, the debugger refuses to recognize it as a function.
124     // Call with a prototype of 'qstrdup', as it is the same
125     // Prepare call: Locate 'qstrdup' in the (potentially namespaced) corelib. For some
126     // reason, the symbol is present in QtGui as well without type information.
127     QString dummyFunc = QLatin1String("*qstrdup");
128     if (resolveSymbol(engine->interfaces().debugSymbols, QLatin1String("QtCore[d]*4!"), &dummyFunc, errorMessage) != ResolveSymbolOk)
129         return false;
130
131     QString callCmd; {
132         QTextStream str(&callCmd);
133         str.setIntegerBase(16);
134         str << ".call /s " << dummyFunc << " Kernel32!LoadLibraryA(0x" << nameAddress << ')';
135     }
136     if (loadDebug)
137         qDebug() << "Calling" << callCmd;
138
139     if (!engine->executeDebuggerCommand(callCmd, errorMessage))
140         return false;
141     // Execute current thread. This will hit a breakpoint.
142     QString goCmd;
143     QTextStream(&goCmd) << '~' << threadId << " g";
144     if (!engine->executeDebuggerCommand(goCmd, errorMessage))
145         return false;
146     const HRESULT hr = engine->waitForEvent(waitTimeOutMS);
147     if (FAILED(hr)) {
148         *errorMessage = CdbCore::msgComFailed("WaitForEvent", hr);
149         return false;
150     }
151     return true;
152 }
153
154 // Format a "go" in a thread
155 static inline QString goCommand(unsigned long threadId)
156 {
157     QString rc;
158     QTextStream(&rc) << '~' << threadId << " g";
159     return rc;
160 }
161
162 // ---- Load messages
163 static inline QString msgMethod(bool injectOrCall)
164 {
165     return injectOrCall ?
166             QCoreApplication::translate("Debugger::Internal::CdbDumperHelper", "injection") :
167             QCoreApplication::translate("Debugger::Internal::CdbDumperHelper", "debugger call");
168 }
169
170 static QString msgLoading(const QString &library, bool injectOrCall)
171 {
172     return QCoreApplication::translate("Debugger::Internal::CdbDumperHelper",
173                                        "Loading the custom dumper library '%1' (%2) ...").
174                                        arg(library, msgMethod(injectOrCall));
175 }
176
177 static QString msgLoadFailed(const QString &library, bool injectOrCall, const QString &why)
178 {
179     return QCoreApplication::translate("Debugger::Internal::CdbDumperHelper",
180                                        "Loading of the custom dumper library '%1' (%2) failed: %3").
181                                        arg(library, msgMethod(injectOrCall), why);
182 }
183
184 static QString msgLoadSucceeded(const QString &library, bool injectOrCall)
185 {
186         return QCoreApplication::translate("Debugger::Internal::CdbDumperHelper",
187                                        "Loaded the custom dumper library '%1' (%2).").
188                                        arg(library, msgMethod(injectOrCall));
189 }
190
191 // Dumper initialization as a background thread.
192 // Befriends CdbDumperHelper and calls its methods
193 class CdbDumperInitThread : public QThread {
194     Q_OBJECT
195 public:
196     static inline bool ensureDumperInitialized(CdbDumperHelper &h, QString *errorMessage);
197
198     virtual void run();
199
200 signals:
201     void logMessage(const QString &m, int channel);
202     void statusMessage(const QString &m, int timeOut);
203
204 private:
205     explicit CdbDumperInitThread(CdbDumperHelper &h, QString *errorMessage);
206
207     CdbDumperHelper &m_helper;
208     bool m_ok;
209     QString *m_errorMessage;
210 };
211
212 CdbDumperInitThread::CdbDumperInitThread(CdbDumperHelper &h, QString *errorMessage) :
213         m_helper(h),
214         m_ok(false),
215         m_errorMessage(errorMessage)
216 {
217 }
218
219 bool CdbDumperInitThread::ensureDumperInitialized(CdbDumperHelper &h, QString *errorMessage)
220 {
221     // Quick state check
222     switch (h.state()) {
223     case CdbDumperHelper::Disabled:
224         *errorMessage = QLatin1String("Internal error, attempt to call disabled dumper");
225         return false;
226     case CdbDumperHelper::Initialized:
227         return true;
228     default:
229         break;
230     }
231     // Need a thread to do initialization work. Typically
232     // takes several seconds depending on debuggee size.
233     QApplication::setOverrideCursor(Qt::BusyCursor);
234     CdbDumperInitThread thread(h, errorMessage);
235     connect(&thread, SIGNAL(statusMessage(QString,int)),
236             h.m_engine, SLOT(showStatusMessage(QString,int)),
237             Qt::QueuedConnection);
238     connect(&thread, SIGNAL(logMessage(QString,int)),
239             h.m_engine, SLOT(showMessage(QString,int)),
240             Qt::QueuedConnection);
241     QEventLoop eventLoop;
242     connect(&thread, SIGNAL(finished()), &eventLoop, SLOT(quit()), Qt::QueuedConnection);
243     thread.start();
244     if (thread.isRunning())
245         eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
246     QApplication::restoreOverrideCursor();
247     if (thread.m_ok) {
248         h.m_engine->showStatusMessage(QCoreApplication::translate("Debugger::Internal::CdbDumperHelper", "Stopped / Custom dumper library initialized."), messageTimeOut);
249         h.m_engine->showMessage(h.m_helper.toString());
250         h.m_state = CdbDumperHelper::Initialized;
251     } else {
252         h.m_state = CdbDumperHelper::Disabled; // No message here
253         *errorMessage = QCoreApplication::translate("Debugger::Internal::CdbDumperHelper", "The custom dumper library could not be initialized: %1").arg(*errorMessage);
254         h.m_engine->showStatusMessage(*errorMessage, messageTimeOut);
255         h.m_engine->showQtDumperLibraryWarning(*errorMessage);
256     }
257     if (loadDebug)
258         qDebug() << Q_FUNC_INFO << '\n' << thread.m_ok;
259     return thread.m_ok;
260 }
261
262 void CdbDumperInitThread ::run()
263 {
264     switch (m_helper.state()) {
265     // Injection load failed or disabled: Try a call load.
266     case CdbDumperHelper::NotLoaded:
267     case CdbDumperHelper::InjectLoading:
268     case CdbDumperHelper::InjectLoadFailed:
269         // Also shows up in the log window.
270         emit statusMessage(msgLoading(m_helper.m_library, false), -1);
271         switch (m_helper.initCallLoad(m_errorMessage)) {
272         case CdbDumperHelper::CallLoadOk:
273         case CdbDumperHelper::CallLoadAlreadyLoaded:
274             emit logMessage(msgLoadSucceeded(m_helper.m_library, false), LogMisc);
275             m_helper.m_state = CdbDumperHelper::Loaded;
276             break;
277         case CdbDumperHelper::CallLoadError:
278             *m_errorMessage = msgLoadFailed(m_helper.m_library, false, *m_errorMessage);
279             m_ok = false;
280             return;
281         case CdbDumperHelper::CallLoadNoQtApp:
282             emit logMessage(QCoreApplication::translate("Debugger::Internal::CdbDumperHelper", "The debuggee does not appear to be Qt application."), LogMisc);
283             m_helper.m_state = CdbDumperHelper::Disabled; // No message here
284             m_ok = true;
285             return;
286         }
287         break;
288     case CdbDumperHelper::Loaded: // Injection load succeeded, ideally
289         break;
290     }
291     // Perform remaining initialization
292     emit statusMessage(QCoreApplication::translate("Debugger::Internal::CdbDumperHelper", "Initializing dumpers..."), 60000);
293     m_ok = m_helper.initResolveSymbols(m_errorMessage) && m_helper.initKnownTypes(m_errorMessage);
294 }
295
296 // ------------------- CdbDumperHelper
297
298 CdbDumperHelper::CdbDumperHelper(CdbEngine *engine,
299                                  CdbCore::CoreEngine *coreEngine) :
300     m_tryInjectLoad(true),
301     m_msgDisabled(QLatin1String("Dumpers are disabled")),
302     m_msgNotInScope(QLatin1String("Data not in scope")),
303     m_state(NotLoaded),
304     m_engine(engine),
305     m_coreEngine(coreEngine),
306     m_inBufferAddress(0),
307     m_inBufferSize(0),
308     m_outBufferAddress(0),
309     m_outBufferSize(0),
310     m_buffer(0),
311     m_dumperCallThread(0),
312     m_goCommand(goCommand(m_dumperCallThread)),
313     m_fastSymbolResolution(true)
314 {
315 }
316
317 CdbDumperHelper::~CdbDumperHelper()
318 {
319     clearBuffer();
320 }
321
322 void CdbDumperHelper::disable()
323 {
324     if (loadDebug)
325         qDebug() << Q_FUNC_INFO;
326     m_engine->showMessage(QCoreApplication::translate("Debugger::Internal::CdbDumperHelper", "Disabling dumpers due to debuggee crash..."));
327     m_state = Disabled;
328 }
329
330 void CdbDumperHelper::clearBuffer()
331 {
332     if (m_buffer) {
333         delete [] m_buffer;
334         m_buffer = 0;
335     }
336 }
337
338 void CdbDumperHelper::reset(const QString &library, bool enabled)
339 {
340      if (loadDebug)
341         qDebug() << Q_FUNC_INFO << '\n' << library << enabled;
342     m_library = library;
343     m_state = enabled ? NotLoaded : Disabled;
344     m_dumpObjectSymbol = QLatin1String("qDumpObjectData440");
345     m_helper.clear();
346     m_inBufferAddress = m_outBufferAddress = 0;
347     m_inBufferSize = m_outBufferSize = 0;
348     m_failedTypes.clear();
349     clearBuffer();
350 }
351
352 void CdbDumperHelper::moduleLoadHook(const QString &module, HANDLE debuggeeHandle)
353 {
354     if (loadDebug > 1)
355         qDebug() << "moduleLoadHook" << module << m_state << debuggeeHandle;
356     switch (m_state) {
357     case Disabled:
358     case Initialized:
359         break;
360     case NotLoaded:
361         // Try an inject load as soon as a Qt lib is loaded.
362         // for the thread to finish as this would lock up.
363         if (m_tryInjectLoad && module.contains(QLatin1String("Qt"), Qt::CaseInsensitive)) {
364             // Also shows up in the log window.
365             m_engine->showMessage(msgLoading(m_library, true), StatusBar, messageTimeOut);
366             QString errorMessage;
367             SharedLibraryInjector sh(GetProcessId(debuggeeHandle));
368             if (sh.remoteInject(m_library, false, &errorMessage)) {
369                 m_state = InjectLoading;
370             } else {
371                 m_state = InjectLoadFailed;
372                 // Ok, try call loading...
373                 m_engine->showMessage(msgLoadFailed(m_library, true, errorMessage));
374             }
375         }
376         break;
377     case InjectLoading:
378         // check if gdbmacros.dll loaded
379         if (module.contains(QLatin1String(dumperModuleNameC), Qt::CaseInsensitive)) {
380             m_state = Loaded;
381             m_engine->showMessage(msgLoadSucceeded(m_library, true));
382         }
383         break;
384     }
385 }
386
387 // Try to load dumpers by triggering calls using the debugger
388 CdbDumperHelper::CallLoadResult CdbDumperHelper::initCallLoad(QString *errorMessage)
389 {
390     if (loadDebug)
391         qDebug() << Q_FUNC_INFO;
392     // Do we have Qt and are we already loaded by accident?
393     QStringList modules;
394     if (!getModuleNameList(m_coreEngine->interfaces().debugSymbols, &modules, errorMessage))
395         return CallLoadError;
396     // Are we already loaded by some accident?
397     if (!modules.filter(QLatin1String(dumperModuleNameC), Qt::CaseInsensitive).isEmpty())
398         return CallLoadAlreadyLoaded;
399     // Is that Qt application at all?
400     if (modules.filter(QLatin1String(qtCoreModuleNameC), Qt::CaseInsensitive).isEmpty())
401         return CallLoadNoQtApp;
402     // Try to load
403     if (!debuggeeLoadLibrary(m_engine, m_coreEngine, m_dumperCallThread, m_library, errorMessage))
404         return CallLoadError;
405     return CallLoadOk;
406 }
407
408 // Retrieve address and optionally size of a symbol.
409 static inline bool getSymbolAddress(CIDebugSymbols *sg,
410                                     const QString &name,
411                                     quint64 *address,
412                                     ULONG *size /* = 0*/,
413                                     QString *errorMessage)
414 {
415     // Get address
416     HRESULT hr = sg->GetOffsetByNameWide(reinterpret_cast<PCWSTR>(name.utf16()), address);
417     if (FAILED(hr)) {
418         *errorMessage = CdbCore::msgComFailed("GetOffsetByNameWide", hr);
419         return false;
420     }
421     // Get size. Even works for arrays
422     if (size) {
423         ULONG64 moduleAddress;
424         ULONG type;
425         hr = sg->GetOffsetTypeId(*address, &type, &moduleAddress);
426         if (FAILED(hr)) {
427             *errorMessage = CdbCore::msgComFailed("GetOffsetTypeId", hr);
428             return false;
429         }
430         hr = sg->GetTypeSize(moduleAddress, type, size);
431         if (FAILED(hr)) {
432             *errorMessage = CdbCore::msgComFailed("GetTypeSize", hr);
433             return false;
434         }
435     } // size desired
436     return true;
437 }
438
439 bool CdbDumperHelper::initResolveSymbols(QString *errorMessage)
440 {
441     // Resolve the symbols we need.
442     // There is a 'qDumpInBuffer' in QtCore as well.
443     if (loadDebug)
444         qDebug() << Q_FUNC_INFO;
445     const QString dumperModuleName = QLatin1String(dumperModuleNameC);
446     QString inBufferSymbol, outBufferSymbol;
447     bool rc;
448     if (m_fastSymbolResolution) {
449         // Symbols in the debugging helpers are never namespaced.
450         m_dumpObjectSymbol = dumperModuleName + QLatin1String("!qDumpObjectData440");
451         inBufferSymbol = dumperModuleName + QLatin1String("!qDumpInBuffer");
452         outBufferSymbol = dumperModuleName + QLatin1String("!qDumpOutBuffer");
453     } else {
454         // Classical approach of loading the dumper symbols. Takes some time though.
455         m_dumpObjectSymbol = QLatin1String("*qDumpObjectData440");
456         inBufferSymbol = QLatin1String("*qDumpInBuffer");
457         outBufferSymbol = QLatin1String("*qDumpOutBuffer");
458
459         rc = resolveSymbol(m_coreEngine->interfaces().debugSymbols, &m_dumpObjectSymbol, errorMessage) == ResolveSymbolOk
460                         && resolveSymbol(m_coreEngine->interfaces().debugSymbols, dumperModuleName, &inBufferSymbol, errorMessage) == ResolveSymbolOk
461                         && resolveSymbol(m_coreEngine->interfaces().debugSymbols, dumperModuleName, &outBufferSymbol, errorMessage) == ResolveSymbolOk;
462         if (!rc)
463             return false;
464     }
465     //  Determine buffer addresses, sizes and alloc buffer
466     rc = getSymbolAddress(m_coreEngine->interfaces().debugSymbols, inBufferSymbol, &m_inBufferAddress, &m_inBufferSize, errorMessage)
467          && getSymbolAddress(m_coreEngine->interfaces().debugSymbols, outBufferSymbol, &m_outBufferAddress, &m_outBufferSize, errorMessage);
468     if (!rc)
469         return false;
470     m_buffer = new char[qMax(m_inBufferSize, m_outBufferSize)];
471     if (loadDebug)
472         qDebug().nospace() << Q_FUNC_INFO << '\n' << rc << m_dumpObjectSymbol
473                 << " buffers at 0x" << QString::number(m_inBufferAddress, 16) << ','
474                 << m_inBufferSize << "; 0x"
475                 << QString::number(m_outBufferAddress, 16) << ',' << m_outBufferSize << '\n';
476     return true;
477 }
478
479 // Call query protocol to retrieve known types and sizes
480 bool CdbDumperHelper::initKnownTypes(QString *errorMessage)
481 {
482     if (loadDebug)
483         qDebug() << Q_FUNC_INFO;
484     const double dumperVersionRequired = 1.3;
485     QByteArray output;
486     QString callCmd;
487     QTextStream(&callCmd) << ".call " << m_dumpObjectSymbol << "(1,0,0,0,0,0,0,0)";
488     const char *outData;
489     if (callDumper(callCmd, QByteArray(), &outData, false, errorMessage) != CallOk) {
490         return false;
491     }
492     if (!m_helper.parseQuery(outData)) {
493      *errorMessage = QString::fromLatin1("Unable to parse the dumper output: '%1'").arg(QString::fromAscii(output));
494     }
495     if (m_helper.dumperVersion() < dumperVersionRequired) {
496         *errorMessage = QtDumperHelper::msgDumperOutdated(dumperVersionRequired, m_helper.dumperVersion());
497         return false;
498     }
499     if (loadDebug || dumpDebug)
500         qDebug() << Q_FUNC_INFO << '\n' << m_helper.toString(true);
501     return true;
502 }
503
504 CdbDumperHelper::CallResult
505     CdbDumperHelper::callDumper(const QString &callCmd, const QByteArray &inBuffer, const char **outDataPtr,
506                                 bool ignoreAccessViolation, QString *errorMessage)
507 {
508     *outDataPtr = 0;
509     // Skip stray startup-complete trap exceptions.
510     QSharedPointer<CdbExceptionLoggerEventCallback> exLogger(new
511 CdbExceptionLoggerEventCallback(LogWarning, true, m_engine));
512     CdbCore::EventCallbackRedirector eventRedir(m_coreEngine, exLogger);
513     Q_UNUSED(eventRedir)
514     // write input buffer
515     if (!inBuffer.isEmpty()) {
516         if (!m_coreEngine->writeToDebuggee(inBuffer, m_inBufferAddress, errorMessage))
517             return CallFailed;
518     }
519     if (!m_coreEngine->executeDebuggerCommand(callCmd, errorMessage)) {
520         // Clear the outstanding call in case we triggered a debug library assert with a message box
521         QString clearError;
522         if (!m_coreEngine->executeDebuggerCommand(QLatin1String(".call /c"), &clearError)) {
523             *errorMessage += QString::fromLatin1("/Unable to clear call %1").arg(clearError);
524         }
525         return CallSyntaxError;
526     }
527     // Set up call and a temporary breakpoint after it.
528     // Try to skip debuggee crash exceptions and dumper exceptions
529     // by using 'gN' (go not handled -> pass handling to dumper __try/__catch block)
530     for (int i = 0; i < 10; i++) {
531         const int oldExceptionCount = exLogger->exceptionCount();
532         // Go in current thread. If an exception occurs in loop 2,
533         // let the dumper handle it.
534         QString goCmd = m_goCommand;
535         if (i)
536             goCmd = QLatin1Char('N');
537         if (!m_coreEngine->executeDebuggerCommand(goCmd, errorMessage))
538             return CallFailed;
539         HRESULT hr = m_coreEngine->waitForEvent(waitTimeOutMS);
540         if (FAILED(hr)) {
541             *errorMessage = CdbCore::msgComFailed("WaitForEvent", hr);
542             return CallFailed;
543         }
544         const int newExceptionCount = exLogger->exceptionCount();
545         // no new exceptions? -> break
546         if (oldExceptionCount == newExceptionCount)
547             break;
548         // If we are to ignore EXCEPTION_ACCESS_VIOLATION, check if anything
549         // else occurred.
550         if (ignoreAccessViolation) {
551             const QList<ULONG> newExceptionCodes = exLogger->exceptionCodes().mid(oldExceptionCount);
552             if (newExceptionCodes.count(EXCEPTION_ACCESS_VIOLATION) == newExceptionCodes.size())
553                 break;
554         }
555     }
556     if (exLogger->exceptionCount()) {
557         const QString exMsgs = exLogger->exceptionMessages().join(QString(QLatin1Char(',')));
558         *errorMessage = QString::fromLatin1("Exceptions occurred during the dumper call: %1").arg(exMsgs);
559         return CallFailed;
560     }
561     // Read output
562     const HRESULT hr = m_coreEngine->interfaces().debugDataSpaces->ReadVirtual(m_outBufferAddress, m_buffer, m_outBufferSize, 0);
563     if (FAILED(hr)) {
564         *errorMessage = CdbCore::msgComFailed("ReadVirtual", hr);
565         return CallFailed;
566     }
567     // see QDumper implementation
568     const char result = m_buffer[0];
569     switch (result) {
570     case 't':
571         break;
572     case '+':
573         *errorMessage = QString::fromLatin1("Dumper call '%1' resulted in output overflow.").arg(callCmd);
574         return CallFailed;
575     case 'f':
576         *errorMessage = QString::fromLatin1("Dumper call '%1' failed.").arg(callCmd);
577         return CallFailed;
578     default:
579         *errorMessage = QString::fromLatin1("Dumper call '%1' failed ('%2').").arg(callCmd).arg(QLatin1Char(result));
580         return CallFailed;
581     }
582     *outDataPtr = m_buffer + 1;
583     return CallOk;
584 }
585
586 static inline QString msgDumpFailed(const WatchData &wd, const QString *why)
587 {
588     return QString::fromLatin1("Unable to dump '%1' (%2): %3").arg(QString::fromLatin1(wd.iname), wd.type, *why);
589 }
590
591 static inline QString msgNotHandled(const QString &type)
592 {
593     return QString::fromLatin1("The type '%1' is not handled.").arg(type);
594 }
595
596 CdbDumperHelper::DumpResult CdbDumperHelper::dumpType(const WatchData &wd, bool dumpChildren,
597                                                       QList<WatchData> *result, QString *errorMessage)
598 {
599     if (dumpDebug || debugCDBExecution)
600         qDebug() << ">dumpType() thread: " << m_dumperCallThread << " state: " << m_state
601                  << wd.iname << wd.type << QTime::currentTime().toString();
602     const CdbDumperHelper::DumpResult rc = dumpTypeI(wd, dumpChildren, result, errorMessage);
603     if (dumpDebug)
604         qDebug() << "<dumpType() state: " << m_state << wd.iname
605                 << wd.type << " returns " << rc << *errorMessage << QTime::currentTime().toString();
606     return rc;
607 }
608
609 CdbDumperHelper::DumpResult CdbDumperHelper::dumpTypeI(const WatchData &wd, bool dumpChildren,
610                                                       QList<WatchData> *result, QString *errorMessage)
611 {
612     errorMessage->clear();
613     // Check failure cache and supported types
614     if (m_state == Disabled) {
615         *errorMessage =m_msgDisabled;
616         return DumpNotHandled;
617     }
618     if (wd.error) {
619         *errorMessage =m_msgNotInScope;
620         return DumpNotHandled;
621     }
622     if (m_failedTypes.contains(wd.type)) {
623         *errorMessage = msgNotHandled(wd.type);
624         return DumpNotHandled;
625     }
626     if (wd.addr.isEmpty()) {
627         *errorMessage = QString::fromLatin1("Address is missing for '%1' (%2).")
628             .arg(QString::fromUtf8(wd.exp)).arg(QString::fromUtf8(wd.type));
629         return DumpNotHandled;
630     }
631
632     // Do we have a thread
633     if (m_dumperCallThread == InvalidDumperCallThread) {
634         *errorMessage = QString::fromLatin1("No thread to call.");
635         if (loadDebug)
636             qDebug() << *errorMessage;
637         return DumpNotHandled;
638     }
639
640     // Delay initialization as much as possible
641     if (isIntOrFloatType(wd.type)) {
642         *errorMessage = QString::fromLatin1("Unhandled POD: " ) + wd.type;
643         return DumpNotHandled;
644     }
645
646     // Ensure types are parsed and known.
647     if (!CdbDumperInitThread::ensureDumperInitialized(*this, errorMessage)) {
648         *errorMessage = msgDumpFailed(wd, errorMessage);
649         m_engine->showMessage(*errorMessage, LogError);
650         return DumpError;
651     }
652
653     // Known type?
654     const QtDumperHelper::TypeData td = m_helper.typeData(wd.type);
655     if (loadDebug)
656         qDebug() << "dumpType" << wd.type << td;
657     if (td.type == QtDumperHelper::UnknownType) {
658         *errorMessage = msgNotHandled(wd.type);
659         return DumpNotHandled;
660     }
661
662     // Now evaluate
663     const QString message = QCoreApplication::translate("Debugger::Internal::CdbDumperHelper",
664                                                         "Querying dumpers for '%1'/'%2' (%3)").
665                                                         arg(QString::fromLatin1(wd.iname), wd.exp, wd.type);
666     m_engine->showMessage(message);
667
668     const DumpExecuteResult der = executeDump(wd, td, dumpChildren, result, errorMessage);
669     if (der == DumpExecuteOk)
670         return DumpOk;
671     if (der == CallSyntaxError) {
672         m_failedTypes.push_back(wd.type);
673         if (dumpDebug)
674             qDebug() << "Caching failing type/expression evaluation failed for " << wd.type;
675        }
676     // log error
677     *errorMessage = msgDumpFailed(wd, errorMessage);
678     m_engine->showMessage(*errorMessage, LogWarning);
679     return DumpError;
680 }
681
682 CdbDumperHelper::DumpExecuteResult
683     CdbDumperHelper::executeDump(const WatchData &wd,
684                                 const QtDumperHelper::TypeData& td, bool dumpChildren,
685                                 QList<WatchData> *result, QString *errorMessage)
686 {
687     QByteArray inBuffer;
688     QList<QByteArray> extraParameters;
689     // Build parameter list.
690     m_helper.evaluationParameters(wd, td, QtDumperHelper::CdbDebugger, &inBuffer, &extraParameters);
691     QString callCmd;
692     QTextStream str(&callCmd);
693     str << ".call " << m_dumpObjectSymbol << "(2,0," << wd.addr << ',' << (dumpChildren ? 1 : 0);
694     foreach(const QByteArray &e, extraParameters)
695         str << ',' << QString::fromUtf8(e);
696     str << ')';
697     if (dumpDebug)
698         qDebug() << "Query: " << wd.toString() << "\nwith: " << callCmd << '\n';
699     const char *outputData;
700     // Completely ignore EXCEPTION_ACCESS_VIOLATION crashes in the dumpers.
701     ExceptionBlocker eb(m_coreEngine->interfaces().debugControl, EXCEPTION_ACCESS_VIOLATION, ExceptionBlocker::IgnoreException);
702     if (!eb) {
703         *errorMessage = eb.errorString();
704         return DumpExecuteCallFailed;
705     }
706     switch (callDumper(callCmd, inBuffer, &outputData, true, errorMessage)) {
707     case CallFailed:
708         return DumpExecuteCallFailed;
709     case CallSyntaxError:
710         return DumpExpressionFailed;
711     case CallOk:
712         break;
713     }
714     if (!QtDumperHelper::parseValue(outputData, result)) {
715         *errorMessage = QLatin1String("Parsing of value query output failed.");
716         return DumpExecuteCallFailed;
717     }
718     return DumpExecuteOk;
719 }
720
721 // Simplify some types for sizeof expressions
722 static void simplifySizeExpression(QByteArray *typeName)
723 {
724     typeName->replace("std::basic_string<char,std::char_traits<char>,std::allocator<char>>",
725                       "std::string");
726 }
727
728 bool CdbDumperHelper::getTypeSize(const QByteArray &typeNameIn, int *size, QString *errorMessage)
729 {
730     if (loadDebug > 1)
731         qDebug() << Q_FUNC_INFO << typeNameIn;
732     // Look up cache
733     QByteArray typeName = typeNameIn;
734     simplifySizeExpression(&typeName);
735     // "std::" types sometimes only work without namespace.
736     // If it fails, try again with stripped namespace
737     *size = 0;
738     bool success = false;
739     do {
740         if (runTypeSizeQuery(typeName, size, errorMessage)) {
741             success = true;
742             break;
743         }
744         if (!typeName.contains("std::"))
745             break;
746         typeName.replace("std::", "");
747         errorMessage->clear();
748         if (!runTypeSizeQuery(typeName, size, errorMessage))
749             break;
750         success = true;
751     } while (false);
752     // Cache in dumper helper
753     if (success)
754         m_helper.addSize(typeName, *size);
755     return success;
756 }
757
758 bool CdbDumperHelper::runTypeSizeQuery
759         (const QByteArray &typeName, int *size, QString *errorMessage)
760 {
761     // Retrieve by C++ expression. If we knew the module, we could make use
762     // of the TypeId query mechanism provided by the IDebugSymbolGroup.
763     DEBUG_VALUE sizeValue;
764     QByteArray expression = "sizeof(" + typeName + ')';
765     if (!m_coreEngine->evaluateExpression(expression, &sizeValue, errorMessage))
766         return false;
767     qint64 size64;
768     if (!CdbCore::debugValueToInteger(sizeValue, &size64)) {
769         *errorMessage = QLatin1String("Expression result is not an integer");
770         return false;
771     }
772     *size = static_cast<int>(size64);
773     return true;
774 }
775
776 unsigned long CdbDumperHelper::dumperCallThread()
777 {
778     return m_dumperCallThread;
779 }
780
781 void CdbDumperHelper::setDumperCallThread(unsigned long t)
782 {
783     if (m_dumperCallThread != t) {
784         m_dumperCallThread = t;
785         m_goCommand = goCommand(m_dumperCallThread);
786     }
787 }
788
789 const CdbCore::ComInterfaces *CdbDumperHelper::comInterfaces() const
790 {
791     return &m_coreEngine->interfaces();
792 }
793
794 } // namespace Internal
795 } // namespace Debugger
796
797 #include "cdbdumperhelper.moc"