1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
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.
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 ** If you are unsure which license is appropriate for your use, please
26 ** contact the sales department at http://qt.nokia.com/contact.
28 **************************************************************************/
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"
40 #include "shared/sharedlibraryinjector.h"
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>
50 enum { loadDebug = 0 };
51 enum { dumpDebug = 0 };
53 static const char *dumperModuleNameC = "gdbmacros";
54 static const char *qtCoreModuleNameC = "QtCore";
55 static const ULONG waitTimeOutMS = 30000;
56 static const char *dumperPrefixC = "dumper:";
58 /* Loading the dumpers is 2 step process:
59 * 1) The library must be loaded into the debuggee, for which there are
61 * - Injection loading using the SharedLibraryInjector which
62 * launches a remote thread in the debuggee which loads the
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:
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.
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.
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). */
97 // ------- Call load helpers
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)
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);
116 // Make a call to LoadLibraryA. First, reserve memory in debugger
117 // and copy name over.
119 if (!engine->createDebuggeeAscIIString(moduleName, &nameAddress, errorMessage))
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)
132 QTextStream str(&callCmd);
133 str.setIntegerBase(16);
134 str << ".call /s " << dummyFunc << " Kernel32!LoadLibraryA(0x" << nameAddress << ')';
137 qDebug() << "Calling" << callCmd;
139 if (!engine->executeDebuggerCommand(callCmd, errorMessage))
141 // Execute current thread. This will hit a breakpoint.
143 QTextStream(&goCmd) << '~' << threadId << " g";
144 if (!engine->executeDebuggerCommand(goCmd, errorMessage))
146 const HRESULT hr = engine->waitForEvent(waitTimeOutMS);
148 *errorMessage = CdbCore::msgComFailed("WaitForEvent", hr);
154 // Format a "go" in a thread
155 static inline QString goCommand(unsigned long threadId)
158 QTextStream(&rc) << '~' << threadId << " g";
162 // ---- Load messages
163 static inline QString msgMethod(bool injectOrCall)
165 return injectOrCall ?
166 QCoreApplication::translate("Debugger::Internal::CdbDumperHelper", "injection") :
167 QCoreApplication::translate("Debugger::Internal::CdbDumperHelper", "debugger call");
170 static QString msgLoading(const QString &library, bool injectOrCall)
172 return QCoreApplication::translate("Debugger::Internal::CdbDumperHelper",
173 "Loading the custom dumper library '%1' (%2) ...").
174 arg(library, msgMethod(injectOrCall));
177 static QString msgLoadFailed(const QString &library, bool injectOrCall, const QString &why)
179 return QCoreApplication::translate("Debugger::Internal::CdbDumperHelper",
180 "Loading of the custom dumper library '%1' (%2) failed: %3").
181 arg(library, msgMethod(injectOrCall), why);
184 static QString msgLoadSucceeded(const QString &library, bool injectOrCall)
186 return QCoreApplication::translate("Debugger::Internal::CdbDumperHelper",
187 "Loaded the custom dumper library '%1' (%2).").
188 arg(library, msgMethod(injectOrCall));
191 // Dumper initialization as a background thread.
192 // Befriends CdbDumperHelper and calls its methods
193 class CdbDumperInitThread : public QThread {
196 static inline bool ensureDumperInitialized(CdbDumperHelper &h, QString *errorMessage);
201 void logMessage(const QString &m, int channel);
202 void statusMessage(const QString &m, int timeOut);
205 explicit CdbDumperInitThread(CdbDumperHelper &h, QString *errorMessage);
207 CdbDumperHelper &m_helper;
209 QString *m_errorMessage;
212 CdbDumperInitThread::CdbDumperInitThread(CdbDumperHelper &h, QString *errorMessage) :
215 m_errorMessage(errorMessage)
219 bool CdbDumperInitThread::ensureDumperInitialized(CdbDumperHelper &h, QString *errorMessage)
223 case CdbDumperHelper::Disabled:
224 *errorMessage = QLatin1String("Internal error, attempt to call disabled dumper");
226 case CdbDumperHelper::Initialized:
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);
244 if (thread.isRunning())
245 eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
246 QApplication::restoreOverrideCursor();
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;
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);
258 qDebug() << Q_FUNC_INFO << '\n' << thread.m_ok;
262 void CdbDumperInitThread ::run()
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;
277 case CdbDumperHelper::CallLoadError:
278 *m_errorMessage = msgLoadFailed(m_helper.m_library, false, *m_errorMessage);
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
288 case CdbDumperHelper::Loaded: // Injection load succeeded, ideally
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);
296 // ------------------- CdbDumperHelper
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")),
305 m_coreEngine(coreEngine),
306 m_inBufferAddress(0),
308 m_outBufferAddress(0),
311 m_dumperCallThread(0),
312 m_goCommand(goCommand(m_dumperCallThread)),
313 m_fastSymbolResolution(true)
317 CdbDumperHelper::~CdbDumperHelper()
322 void CdbDumperHelper::disable()
325 qDebug() << Q_FUNC_INFO;
326 m_engine->showMessage(QCoreApplication::translate("Debugger::Internal::CdbDumperHelper", "Disabling dumpers due to debuggee crash..."));
330 void CdbDumperHelper::clearBuffer()
338 void CdbDumperHelper::reset(const QString &library, bool enabled)
341 qDebug() << Q_FUNC_INFO << '\n' << library << enabled;
343 m_state = enabled ? NotLoaded : Disabled;
344 m_dumpObjectSymbol = QLatin1String("qDumpObjectData440");
346 m_inBufferAddress = m_outBufferAddress = 0;
347 m_inBufferSize = m_outBufferSize = 0;
348 m_failedTypes.clear();
352 void CdbDumperHelper::moduleLoadHook(const QString &module, HANDLE debuggeeHandle)
355 qDebug() << "moduleLoadHook" << module << m_state << debuggeeHandle;
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;
371 m_state = InjectLoadFailed;
372 // Ok, try call loading...
373 m_engine->showMessage(msgLoadFailed(m_library, true, errorMessage));
378 // check if gdbmacros.dll loaded
379 if (module.contains(QLatin1String(dumperModuleNameC), Qt::CaseInsensitive)) {
381 m_engine->showMessage(msgLoadSucceeded(m_library, true));
387 // Try to load dumpers by triggering calls using the debugger
388 CdbDumperHelper::CallLoadResult CdbDumperHelper::initCallLoad(QString *errorMessage)
391 qDebug() << Q_FUNC_INFO;
392 // Do we have Qt and are we already loaded by accident?
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;
403 if (!debuggeeLoadLibrary(m_engine, m_coreEngine, m_dumperCallThread, m_library, errorMessage))
404 return CallLoadError;
408 // Retrieve address and optionally size of a symbol.
409 static inline bool getSymbolAddress(CIDebugSymbols *sg,
412 ULONG *size /* = 0*/,
413 QString *errorMessage)
416 HRESULT hr = sg->GetOffsetByNameWide(reinterpret_cast<PCWSTR>(name.utf16()), address);
418 *errorMessage = CdbCore::msgComFailed("GetOffsetByNameWide", hr);
421 // Get size. Even works for arrays
423 ULONG64 moduleAddress;
425 hr = sg->GetOffsetTypeId(*address, &type, &moduleAddress);
427 *errorMessage = CdbCore::msgComFailed("GetOffsetTypeId", hr);
430 hr = sg->GetTypeSize(moduleAddress, type, size);
432 *errorMessage = CdbCore::msgComFailed("GetTypeSize", hr);
439 bool CdbDumperHelper::initResolveSymbols(QString *errorMessage)
441 // Resolve the symbols we need.
442 // There is a 'qDumpInBuffer' in QtCore as well.
444 qDebug() << Q_FUNC_INFO;
445 const QString dumperModuleName = QLatin1String(dumperModuleNameC);
446 QString inBufferSymbol, outBufferSymbol;
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");
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");
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;
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);
470 m_buffer = new char[qMax(m_inBufferSize, m_outBufferSize)];
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';
479 // Call query protocol to retrieve known types and sizes
480 bool CdbDumperHelper::initKnownTypes(QString *errorMessage)
483 qDebug() << Q_FUNC_INFO;
484 const double dumperVersionRequired = 1.3;
487 QTextStream(&callCmd) << ".call " << m_dumpObjectSymbol << "(1,0,0,0,0,0,0,0)";
489 if (callDumper(callCmd, QByteArray(), &outData, false, errorMessage) != CallOk) {
492 if (!m_helper.parseQuery(outData)) {
493 *errorMessage = QString::fromLatin1("Unable to parse the dumper output: '%1'").arg(QString::fromAscii(output));
495 if (m_helper.dumperVersion() < dumperVersionRequired) {
496 *errorMessage = QtDumperHelper::msgDumperOutdated(dumperVersionRequired, m_helper.dumperVersion());
499 if (loadDebug || dumpDebug)
500 qDebug() << Q_FUNC_INFO << '\n' << m_helper.toString(true);
504 CdbDumperHelper::CallResult
505 CdbDumperHelper::callDumper(const QString &callCmd, const QByteArray &inBuffer, const char **outDataPtr,
506 bool ignoreAccessViolation, QString *errorMessage)
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);
514 // write input buffer
515 if (!inBuffer.isEmpty()) {
516 if (!m_coreEngine->writeToDebuggee(inBuffer, m_inBufferAddress, errorMessage))
519 if (!m_coreEngine->executeDebuggerCommand(callCmd, errorMessage)) {
520 // Clear the outstanding call in case we triggered a debug library assert with a message box
522 if (!m_coreEngine->executeDebuggerCommand(QLatin1String(".call /c"), &clearError)) {
523 *errorMessage += QString::fromLatin1("/Unable to clear call %1").arg(clearError);
525 return CallSyntaxError;
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;
536 goCmd = QLatin1Char('N');
537 if (!m_coreEngine->executeDebuggerCommand(goCmd, errorMessage))
539 HRESULT hr = m_coreEngine->waitForEvent(waitTimeOutMS);
541 *errorMessage = CdbCore::msgComFailed("WaitForEvent", hr);
544 const int newExceptionCount = exLogger->exceptionCount();
545 // no new exceptions? -> break
546 if (oldExceptionCount == newExceptionCount)
548 // If we are to ignore EXCEPTION_ACCESS_VIOLATION, check if anything
550 if (ignoreAccessViolation) {
551 const QList<ULONG> newExceptionCodes = exLogger->exceptionCodes().mid(oldExceptionCount);
552 if (newExceptionCodes.count(EXCEPTION_ACCESS_VIOLATION) == newExceptionCodes.size())
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);
562 const HRESULT hr = m_coreEngine->interfaces().debugDataSpaces->ReadVirtual(m_outBufferAddress, m_buffer, m_outBufferSize, 0);
564 *errorMessage = CdbCore::msgComFailed("ReadVirtual", hr);
567 // see QDumper implementation
568 const char result = m_buffer[0];
573 *errorMessage = QString::fromLatin1("Dumper call '%1' resulted in output overflow.").arg(callCmd);
576 *errorMessage = QString::fromLatin1("Dumper call '%1' failed.").arg(callCmd);
579 *errorMessage = QString::fromLatin1("Dumper call '%1' failed ('%2').").arg(callCmd).arg(QLatin1Char(result));
582 *outDataPtr = m_buffer + 1;
586 static inline QString msgDumpFailed(const WatchData &wd, const QString *why)
588 return QString::fromLatin1("Unable to dump '%1' (%2): %3").arg(QString::fromLatin1(wd.iname), wd.type, *why);
591 static inline QString msgNotHandled(const QString &type)
593 return QString::fromLatin1("The type '%1' is not handled.").arg(type);
596 CdbDumperHelper::DumpResult CdbDumperHelper::dumpType(const WatchData &wd, bool dumpChildren,
597 QList<WatchData> *result, QString *errorMessage)
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);
604 qDebug() << "<dumpType() state: " << m_state << wd.iname
605 << wd.type << " returns " << rc << *errorMessage << QTime::currentTime().toString();
609 CdbDumperHelper::DumpResult CdbDumperHelper::dumpTypeI(const WatchData &wd, bool dumpChildren,
610 QList<WatchData> *result, QString *errorMessage)
612 errorMessage->clear();
613 // Check failure cache and supported types
614 if (m_state == Disabled) {
615 *errorMessage =m_msgDisabled;
616 return DumpNotHandled;
619 *errorMessage =m_msgNotInScope;
620 return DumpNotHandled;
622 if (m_failedTypes.contains(wd.type)) {
623 *errorMessage = msgNotHandled(wd.type);
624 return DumpNotHandled;
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;
632 // Do we have a thread
633 if (m_dumperCallThread == InvalidDumperCallThread) {
634 *errorMessage = QString::fromLatin1("No thread to call.");
636 qDebug() << *errorMessage;
637 return DumpNotHandled;
640 // Delay initialization as much as possible
641 if (isIntOrFloatType(wd.type)) {
642 *errorMessage = QString::fromLatin1("Unhandled POD: " ) + wd.type;
643 return DumpNotHandled;
646 // Ensure types are parsed and known.
647 if (!CdbDumperInitThread::ensureDumperInitialized(*this, errorMessage)) {
648 *errorMessage = msgDumpFailed(wd, errorMessage);
649 m_engine->showMessage(*errorMessage, LogError);
654 const QtDumperHelper::TypeData td = m_helper.typeData(wd.type);
656 qDebug() << "dumpType" << wd.type << td;
657 if (td.type == QtDumperHelper::UnknownType) {
658 *errorMessage = msgNotHandled(wd.type);
659 return DumpNotHandled;
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);
668 const DumpExecuteResult der = executeDump(wd, td, dumpChildren, result, errorMessage);
669 if (der == DumpExecuteOk)
671 if (der == CallSyntaxError) {
672 m_failedTypes.push_back(wd.type);
674 qDebug() << "Caching failing type/expression evaluation failed for " << wd.type;
677 *errorMessage = msgDumpFailed(wd, errorMessage);
678 m_engine->showMessage(*errorMessage, LogWarning);
682 CdbDumperHelper::DumpExecuteResult
683 CdbDumperHelper::executeDump(const WatchData &wd,
684 const QtDumperHelper::TypeData& td, bool dumpChildren,
685 QList<WatchData> *result, QString *errorMessage)
688 QList<QByteArray> extraParameters;
689 // Build parameter list.
690 m_helper.evaluationParameters(wd, td, QtDumperHelper::CdbDebugger, &inBuffer, &extraParameters);
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);
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);
703 *errorMessage = eb.errorString();
704 return DumpExecuteCallFailed;
706 switch (callDumper(callCmd, inBuffer, &outputData, true, errorMessage)) {
708 return DumpExecuteCallFailed;
709 case CallSyntaxError:
710 return DumpExpressionFailed;
714 if (!QtDumperHelper::parseValue(outputData, result)) {
715 *errorMessage = QLatin1String("Parsing of value query output failed.");
716 return DumpExecuteCallFailed;
718 return DumpExecuteOk;
721 // Simplify some types for sizeof expressions
722 static void simplifySizeExpression(QByteArray *typeName)
724 typeName->replace("std::basic_string<char,std::char_traits<char>,std::allocator<char>>",
728 bool CdbDumperHelper::getTypeSize(const QByteArray &typeNameIn, int *size, QString *errorMessage)
731 qDebug() << Q_FUNC_INFO << typeNameIn;
733 QByteArray typeName = typeNameIn;
734 simplifySizeExpression(&typeName);
735 // "std::" types sometimes only work without namespace.
736 // If it fails, try again with stripped namespace
738 bool success = false;
740 if (runTypeSizeQuery(typeName, size, errorMessage)) {
744 if (!typeName.contains("std::"))
746 typeName.replace("std::", "");
747 errorMessage->clear();
748 if (!runTypeSizeQuery(typeName, size, errorMessage))
752 // Cache in dumper helper
754 m_helper.addSize(typeName, *size);
758 bool CdbDumperHelper::runTypeSizeQuery
759 (const QByteArray &typeName, int *size, QString *errorMessage)
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))
768 if (!CdbCore::debugValueToInteger(sizeValue, &size64)) {
769 *errorMessage = QLatin1String("Expression result is not an integer");
772 *size = static_cast<int>(size64);
776 unsigned long CdbDumperHelper::dumperCallThread()
778 return m_dumperCallThread;
781 void CdbDumperHelper::setDumperCallThread(unsigned long t)
783 if (m_dumperCallThread != t) {
784 m_dumperCallThread = t;
785 m_goCommand = goCommand(m_dumperCallThread);
789 const CdbCore::ComInterfaces *CdbDumperHelper::comInterfaces() const
791 return &m_coreEngine->interfaces();
794 } // namespace Internal
795 } // namespace Debugger
797 #include "cdbdumperhelper.moc"