2009-04-20 16:40:50 +02:00
/**************************************************************************
* *
* * This file is part of Qt Creator
* *
* * Copyright ( c ) 2009 Nokia Corporation and / or its subsidiary ( - ies ) .
* *
2009-06-16 17:26:44 +02:00
* * Contact : Nokia Corporation ( qt - info @ nokia . com )
2009-04-20 16:40:50 +02:00
* *
* * Commercial Usage
* *
* * Licensees holding valid Qt Commercial licenses may use this file in
* * accordance with the Qt Commercial License Agreement provided with the
* * Software or , alternatively , in accordance with the terms contained in
* * a written agreement between you and Nokia .
* *
* * GNU Lesser General Public License Usage
* *
* * Alternatively , this file may be used under the terms of the GNU Lesser
* * General Public License version 2.1 as published by the Free Software
* * Foundation and appearing in the file LICENSE . LGPL included in the
* * packaging of this file . Please review the following information to
* * ensure the GNU Lesser General Public License version 2.1 requirements
* * will be met : http : //www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
* *
* * If you are unsure which license is appropriate for your use , please
2009-06-16 17:26:44 +02:00
* * contact the sales department at http : //www.qtsoftware.com/contact.
2009-04-20 16:40:50 +02:00
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include "cdbdumperhelper.h"
# include "cdbmodules.h"
# include "cdbdebugengine_p.h"
# include "cdbdebugoutput.h"
# include "cdbdebugeventcallback.h"
2009-04-29 14:15:09 +02:00
# include "cdbsymbolgroupcontext.h"
# include "watchhandler.h"
2009-05-22 14:31:37 +02:00
# include "cdbexceptionutils.h"
2009-04-20 16:40:50 +02:00
2009-05-19 15:45:58 +02:00
# include "shared/sharedlibraryinjector.h"
2009-05-05 09:52:24 +02:00
2009-04-20 16:40:50 +02:00
# include <QtCore/QRegExp>
2009-04-22 17:28:26 +02:00
# include <QtCore/QCoreApplication>
# include <QtCore/QTextStream>
2009-04-20 16:40:50 +02:00
enum { loadDebug = 0 } ;
2009-04-22 17:28:26 +02:00
static const char * dumperModuleNameC = " gdbmacros " ;
static const char * qtCoreModuleNameC = " QtCore " ;
static const ULONG waitTimeOutMS = 30000 ;
2009-04-29 14:15:09 +02:00
static const char * dumperPrefixC = " dumper: " ;
2009-05-05 09:52:24 +02:00
/* Loading the dumpers is 2 step process:
* 1 ) The library must be loaded into the debuggee , for which there are
* 2 approaches :
* - Injection loading using the SharedLibraryInjector which
* launches a remote thread in the debuggee which loads the
* library . Drawbacks :
* * The remote thread must not starve .
* * It is not possible to wait loading and loading occurs late ,
* after entering main ( )
* - Debugger Call loading , which has the debuggee execute
* a LoadLibrary call via debugger commands . Drawbacks :
* * Slow
* * Requires presence of a symbol of the same prototype as
* LoadLibraryA as the original prototype is not sufficient .
* 2 ) Call a query function ( protocol 1 of the dumper ) to obtain a list
* of handled types and a map of known sizes .
*
* The class currently launches injection loading from the module
* load hook as soon as it sees a Qt module .
* The dumpType ( ) function performs the rest of the [ delayed ] initialization .
* If the load has not finished , it attempts call loading and
* executes the initial query protocol .
*
* Note : The main technique here is having the debuggee call functions
* using the . call command ( which takes a function with a known
* prototype and simple , integer parameters ) .
* This does not work from an IDebugEvent callback , as it will cause
* WaitForEvent ( ) to fail with unknown errors .
* It mostly works from breakpoints , with the addditional restriction
* that complex functions only work from ' well - defined ' breakpoints
* ( such as main ( ) ) and otherwise cause access violation exceptions
* ( for example LoadLibrary ) .
* Exceptions occuring in the functions to be called must be handled
* by __try / __except ( they show up in the debugger and must acknowledged
* by gN ( go not handled ) . */
2009-04-20 16:40:50 +02:00
namespace Debugger {
namespace Internal {
2009-05-05 09:52:24 +02:00
// ------- Call load helpers
2009-04-20 16:40:50 +02:00
// Alloc memory in debuggee using the ".dvalloc" command as
// there seems to be no API for it.
2009-04-29 14:15:09 +02:00
static bool allocDebuggeeMemory ( CdbComInterfaces * cif ,
2009-04-20 16:40:50 +02:00
int size , ULONG64 * address , QString * errorMessage )
{
* address = 0 ;
const QString allocCmd = QLatin1String ( " .dvalloc " ) + QString : : number ( size ) ;
StringOutputHandler stringHandler ;
2009-04-29 14:15:09 +02:00
OutputRedirector redir ( cif - > debugClient , & stringHandler ) ;
if ( ! CdbDebugEnginePrivate : : executeDebuggerCommand ( cif - > debugControl , allocCmd , errorMessage ) )
2009-04-20 16:40:50 +02:00
return false ;
2009-04-22 17:28:26 +02:00
// "Allocated 1000 bytes starting at 003a0000" .. hopefully never localized
2009-04-20 16:40:50 +02:00
bool ok = false ;
const QString output = stringHandler . result ( ) ;
const int lastBlank = output . lastIndexOf ( QLatin1Char ( ' ' ) ) ;
if ( lastBlank ! = - 1 ) {
const qint64 addri = output . mid ( lastBlank + 1 ) . toLongLong ( & ok , 16 ) ;
if ( ok )
* address = addri ;
}
2009-04-29 14:15:09 +02:00
if ( loadDebug > 1 )
2009-04-22 17:28:26 +02:00
qDebug ( ) < < Q_FUNC_INFO < < ' \n ' < < output < < * address < < ok ;
2009-04-20 16:40:50 +02:00
if ( ! ok ) {
* errorMessage = QString : : fromLatin1 ( " Failed to parse output '%1' " ) . arg ( output ) ;
return false ;
}
return true ;
}
// Alloc an AscII string in debuggee
2009-04-29 14:15:09 +02:00
static bool createDebuggeeAscIIString ( CdbComInterfaces * cif ,
const QString & s ,
ULONG64 * address ,
QString * errorMessage )
2009-04-20 16:40:50 +02:00
{
QByteArray sAsciiData = s . toLocal8Bit ( ) ;
sAsciiData + = ' \0 ' ;
2009-04-29 14:15:09 +02:00
if ( ! allocDebuggeeMemory ( cif , sAsciiData . size ( ) , address , errorMessage ) )
2009-04-20 16:40:50 +02:00
return false ;
2009-04-29 14:15:09 +02:00
const HRESULT hr = cif - > debugDataSpaces - > WriteVirtual ( * address , sAsciiData . data ( ) , sAsciiData . size ( ) , 0 ) ;
2009-04-20 16:40:50 +02:00
if ( FAILED ( hr ) ) {
* errorMessage = msgComFailed ( " WriteVirtual " , hr ) ;
return false ;
}
return true ;
}
// Load a library into the debuggee. Currently requires
// the QtCored4.pdb file to be present as we need "qstrdup"
// as dummy symbol. This is ok ATM since dumpers only
// make sense for Qt apps.
2009-04-29 14:15:09 +02:00
static bool debuggeeLoadLibrary ( IDebuggerManagerAccessForEngines * access ,
CdbComInterfaces * cif ,
const QString & moduleName ,
QString * errorMessage )
2009-04-20 16:40:50 +02:00
{
2009-04-29 14:15:09 +02:00
if ( loadDebug > 1 )
2009-04-20 16:40:50 +02:00
qDebug ( ) < < Q_FUNC_INFO < < moduleName ;
// Try to ignore the breakpoints
2009-04-29 14:15:09 +02:00
CdbExceptionLoggerEventCallback exLogger ( QLatin1String ( dumperPrefixC ) , access ) ;
EventCallbackRedirector eventRedir ( cif - > debugClient , & exLogger ) ;
2009-04-20 16:40:50 +02:00
// Make a call to LoadLibraryA. First, reserve memory in debugger
// and copy name over.
ULONG64 nameAddress ;
2009-04-29 14:15:09 +02:00
if ( ! createDebuggeeAscIIString ( cif , moduleName , & nameAddress , errorMessage ) )
2009-04-20 16:40:50 +02:00
return false ;
// We want to call "HMODULE LoadLibraryA(LPCTSTR lpFileName)"
// (void* LoadLibraryA(char*)). However, despite providing a symbol
// server, the debugger refuses to recognize it as a function.
// Set up the call stack with a function of same signature (qstrdup)
// and change the call register to LoadLibraryA() before executing "g".
2009-04-22 17:28:26 +02:00
// Prepare call: Locate 'qstrdup' in the (potentially namespaced) corelib. For some
// reason, the symbol is present in QtGui as well without type information.
QString dummyFunc = QLatin1String ( " *qstrdup " ) ;
2009-04-29 14:15:09 +02:00
if ( resolveSymbol ( cif - > debugSymbols , QLatin1String ( " QtCore[d]*4! " ) , & dummyFunc , errorMessage ) ! = ResolveSymbolOk )
2009-04-20 16:40:50 +02:00
return false ;
QString callCmd = QLatin1String ( " .call " ) ;
callCmd + = dummyFunc ;
callCmd + = QLatin1String ( " (0x " ) ;
callCmd + = QString : : number ( nameAddress , 16 ) ;
callCmd + = QLatin1Char ( ' ) ' ) ;
2009-04-29 14:15:09 +02:00
if ( ! CdbDebugEnginePrivate : : executeDebuggerCommand ( cif - > debugControl , callCmd , errorMessage ) )
2009-04-20 16:40:50 +02:00
return false ;
2009-04-29 14:15:09 +02:00
if ( ! CdbDebugEnginePrivate : : executeDebuggerCommand ( cif - > debugControl , QLatin1String ( " r eip=Kernel32!LoadLibraryA " ) , errorMessage ) )
2009-04-20 16:40:50 +02:00
return false ;
2009-04-22 17:28:26 +02:00
// This will hit a breakpoint.
2009-04-29 14:15:09 +02:00
if ( ! CdbDebugEnginePrivate : : executeDebuggerCommand ( cif - > debugControl , QString ( QLatin1Char ( ' g ' ) ) , errorMessage ) )
2009-04-22 17:28:26 +02:00
return false ;
2009-04-29 14:15:09 +02:00
const HRESULT hr = cif - > debugControl - > WaitForEvent ( 0 , waitTimeOutMS ) ;
2009-04-22 17:28:26 +02:00
if ( FAILED ( hr ) ) {
* errorMessage = msgComFailed ( " WaitForEvent " , hr ) ;
2009-04-20 16:40:50 +02:00
return false ;
2009-04-22 17:28:26 +02:00
}
2009-04-20 16:40:50 +02:00
return true ;
}
2009-05-05 09:52:24 +02:00
// ---- Load messages
static inline QString msgMethod ( bool injectOrCall )
{
return injectOrCall ?
2009-05-07 15:34:52 +02:00
QCoreApplication : : translate ( " Debugger::Internal::CdbDumperHelper " , " injection " ) :
QCoreApplication : : translate ( " Debugger::Internal::CdbDumperHelper " , " debugger call " ) ;
2009-05-05 09:52:24 +02:00
}
static QString msgLoading ( const QString & library , bool injectOrCall )
{
2009-05-07 15:34:52 +02:00
return QCoreApplication : : translate ( " Debugger::Internal::CdbDumperHelper " ,
2009-05-05 09:52:24 +02:00
" Loading the custom dumper library '%1' (%2) ... " ) .
arg ( library , msgMethod ( injectOrCall ) ) ;
}
static QString msgLoadFailed ( const QString & library , bool injectOrCall , const QString & why )
{
2009-05-07 15:34:52 +02:00
return QCoreApplication : : translate ( " Debugger::Internal::CdbDumperHelper " ,
2009-05-05 09:52:24 +02:00
" Loading of the custom dumper library '%1' (%2) failed: %3 " ) .
arg ( library , msgMethod ( injectOrCall ) , why ) ;
}
static QString msgLoadSucceeded ( const QString & library , bool injectOrCall )
{
2009-05-07 15:34:52 +02:00
return QCoreApplication : : translate ( " Debugger::Internal::CdbDumperHelper " ,
2009-05-05 09:52:24 +02:00
" Loaded the custom dumper library '%1' (%2). " ) .
arg ( library , msgMethod ( injectOrCall ) ) ;
}
2009-04-22 17:28:26 +02:00
// ------------------- CdbDumperHelper
2009-05-05 09:52:24 +02:00
CdbDumperHelper : : CdbDumperHelper ( DebuggerManager * manager ,
2009-04-29 14:15:09 +02:00
CdbComInterfaces * cif ) :
2009-05-05 09:52:24 +02:00
m_tryInjectLoad ( true ) ,
2009-04-29 14:15:09 +02:00
m_messagePrefix ( QLatin1String ( dumperPrefixC ) ) ,
2009-04-20 16:40:50 +02:00
m_state ( NotLoaded ) ,
2009-05-05 09:52:24 +02:00
m_manager ( manager ) ,
m_access ( manager ) ,
2009-04-22 17:28:26 +02:00
m_cif ( cif ) ,
m_inBufferAddress ( 0 ) ,
m_inBufferSize ( 0 ) ,
m_outBufferAddress ( 0 ) ,
m_outBufferSize ( 0 ) ,
m_buffer ( 0 )
2009-04-20 16:40:50 +02:00
{
}
2009-04-22 17:28:26 +02:00
CdbDumperHelper : : ~ CdbDumperHelper ( )
{
clearBuffer ( ) ;
}
2009-05-05 09:52:24 +02:00
void CdbDumperHelper : : disable ( )
{
if ( loadDebug )
qDebug ( ) < < Q_FUNC_INFO ;
2009-05-07 15:34:52 +02:00
m_access - > showDebuggerOutput ( m_messagePrefix , QCoreApplication : : translate ( " Debugger::Internal::CdbDumperHelper " , " Disabling dumpers due to debuggee crash... " ) ) ;
2009-05-05 09:52:24 +02:00
m_state = Disabled ;
}
2009-04-22 17:28:26 +02:00
void CdbDumperHelper : : clearBuffer ( )
{
if ( m_buffer ) {
delete [ ] m_buffer ;
m_buffer = 0 ;
}
}
2009-04-20 16:40:50 +02:00
void CdbDumperHelper : : reset ( const QString & library , bool enabled )
{
2009-05-05 09:52:24 +02:00
if ( loadDebug )
qDebug ( ) < < Q_FUNC_INFO < < ' \n ' < < library < < enabled ;
2009-04-20 16:40:50 +02:00
m_library = library ;
m_state = enabled ? NotLoaded : Disabled ;
m_dumpObjectSymbol = QLatin1String ( " qDumpObjectData440 " ) ;
2009-04-29 14:15:09 +02:00
m_helper . clear ( ) ;
2009-04-22 17:28:26 +02:00
m_inBufferAddress = m_outBufferAddress = 0 ;
m_inBufferSize = m_outBufferSize = 0 ;
2009-04-29 14:15:09 +02:00
m_failedTypes . clear ( ) ;
2009-04-22 17:28:26 +02:00
clearBuffer ( ) ;
2009-04-20 16:40:50 +02:00
}
2009-05-05 09:52:24 +02:00
void CdbDumperHelper : : moduleLoadHook ( const QString & module , HANDLE debuggeeHandle )
2009-04-20 16:40:50 +02:00
{
2009-05-05 09:52:24 +02:00
if ( loadDebug > 1 )
qDebug ( ) < < " moduleLoadHook " < < module < < m_state < < debuggeeHandle ;
switch ( m_state ) {
case Disabled :
case Initialized :
break ;
case NotLoaded :
// Try an inject load as soon as a Qt lib is loaded.
// for the thread to finish as this would lock up.
if ( m_tryInjectLoad & & module . contains ( QLatin1String ( " Qt " ) , Qt : : CaseInsensitive ) ) {
// Also shows up in the log window.
m_manager - > showStatusMessage ( msgLoading ( m_library , true ) , 10000 ) ;
QString errorMessage ;
SharedLibraryInjector sh ( GetProcessId ( debuggeeHandle ) ) ;
if ( sh . remoteInject ( m_library , false , & errorMessage ) ) {
m_state = InjectLoading ;
} else {
m_state = InjectLoadFailed ;
// Ok, try call loading...
m_access - > showDebuggerOutput ( m_messagePrefix , msgLoadFailed ( m_library , true , errorMessage ) ) ;
2009-04-20 16:40:50 +02:00
}
}
break ;
2009-05-05 09:52:24 +02:00
case InjectLoading :
// check if gdbmacros.dll loaded
if ( module . contains ( QLatin1String ( dumperModuleNameC ) , Qt : : CaseInsensitive ) ) {
m_state = Loaded ;
m_access - > showDebuggerOutput ( m_messagePrefix , msgLoadSucceeded ( m_library , true ) ) ;
}
2009-04-20 16:40:50 +02:00
break ;
2009-05-05 09:52:24 +02:00
}
}
// Try to load dumpers by triggering calls using the debugger
CdbDumperHelper : : CallLoadResult CdbDumperHelper : : initCallLoad ( QString * errorMessage )
{
2009-04-22 17:28:26 +02:00
if ( loadDebug )
2009-05-05 09:52:24 +02:00
qDebug ( ) < < Q_FUNC_INFO ;
// Do we have Qt and are we already loaded by accident?
QStringList modules ;
if ( ! getModuleNameList ( m_cif - > debugSymbols , & modules , errorMessage ) )
return CallLoadError ;
// Are we already loaded by some accident?
if ( ! modules . filter ( QLatin1String ( dumperModuleNameC ) , Qt : : CaseInsensitive ) . isEmpty ( ) )
return CallLoadAlreadyLoaded ;
// Is that Qt application at all?
if ( modules . filter ( QLatin1String ( qtCoreModuleNameC ) , Qt : : CaseInsensitive ) . isEmpty ( ) )
return CallLoadNoQtApp ;
// Try to load
if ( ! debuggeeLoadLibrary ( m_access , m_cif , m_library , errorMessage ) )
return CallLoadError ;
return CallLoadOk ;
}
bool CdbDumperHelper : : ensureInitialized ( QString * errorMessage )
{
if ( loadDebug )
qDebug ( ) < < Q_FUNC_INFO < < ' \n ' < < m_state ;
switch ( m_state ) {
case Disabled :
* errorMessage = QLatin1String ( " Internal error, attempt to call disabled dumper " ) ;
return false ;
case Initialized :
return true ;
// Injection load failed or disabled: Try a call load.
case NotLoaded :
case InjectLoading :
case InjectLoadFailed :
// Also shows up in the log window.
m_manager - > showStatusMessage ( msgLoading ( m_library , false ) , 10000 ) ;
switch ( initCallLoad ( errorMessage ) ) {
case CallLoadOk :
case CallLoadAlreadyLoaded :
m_access - > showDebuggerOutput ( m_messagePrefix , msgLoadSucceeded ( m_library , false ) ) ;
m_state = Loaded ;
break ;
case CallLoadError :
* errorMessage = msgLoadFailed ( m_library , false , * errorMessage ) ;
m_access - > showDebuggerOutput ( m_messagePrefix , * errorMessage ) ;
m_access - > showQtDumperLibraryWarning ( * errorMessage ) ;
return false ;
case CallLoadNoQtApp :
2009-05-07 15:34:52 +02:00
m_access - > showDebuggerOutput ( m_messagePrefix , QCoreApplication : : translate ( " Debugger::Internal::CdbDumperHelper " , " The debuggee does not appear to be Qt application. " ) ) ;
2009-05-06 14:26:20 +02:00
m_state = Disabled ; // No message here
2009-05-05 09:52:24 +02:00
return true ;
}
break ;
case Loaded : // Injection load succeeded, ideally
break ;
}
// Perform remaining initialization
2009-05-07 15:34:52 +02:00
m_manager - > showStatusMessage ( QCoreApplication : : translate ( " Debugger::Internal::CdbDumperHelper " , " Initializing dumpers... " ) , 10000 ) ;
2009-05-05 09:52:24 +02:00
const bool ok = initResolveSymbols ( errorMessage ) & & initKnownTypes ( errorMessage ) ;
if ( ok ) {
2009-05-07 15:34:52 +02:00
m_access - > showDebuggerOutput ( m_messagePrefix , QCoreApplication : : translate ( " Debugger::Internal::CdbDumperHelper " , " Custom dumper library initialized. " ) ) ;
2009-05-05 09:52:24 +02:00
m_access - > showDebuggerOutput ( m_messagePrefix , m_helper . toString ( ) ) ;
m_state = Initialized ;
} else {
2009-05-06 14:26:20 +02:00
m_state = Disabled ; // No message here
2009-05-07 15:34:52 +02:00
* errorMessage = QCoreApplication : : translate ( " Debugger::Internal::CdbDumperHelper " , " The custom dumper library could not be initialized: %1 " ) . arg ( * errorMessage ) ;
2009-05-05 09:52:24 +02:00
m_access - > showDebuggerOutput ( m_messagePrefix , * errorMessage ) ;
m_access - > showQtDumperLibraryWarning ( * errorMessage ) ;
}
return ok ;
2009-04-22 17:28:26 +02:00
}
// Retrieve address and optionally size of a symbol.
static inline bool getSymbolAddress ( CIDebugSymbols * sg ,
const QString & name ,
quint64 * address ,
ULONG * size /* = 0*/ ,
QString * errorMessage )
{
// Get address
2009-05-30 23:03:43 +09:00
HRESULT hr = sg - > GetOffsetByNameWide ( reinterpret_cast < PCWSTR > ( name . utf16 ( ) ) , address ) ;
2009-04-22 17:28:26 +02:00
if ( FAILED ( hr ) ) {
* errorMessage = msgComFailed ( " GetOffsetByNameWide " , hr ) ;
return false ;
}
// Get size. Even works for arrays
if ( size ) {
ULONG64 moduleAddress ;
ULONG type ;
hr = sg - > GetOffsetTypeId ( * address , & type , & moduleAddress ) ;
if ( FAILED ( hr ) ) {
* errorMessage = msgComFailed ( " GetOffsetTypeId " , hr ) ;
return false ;
}
hr = sg - > GetTypeSize ( moduleAddress , type , size ) ;
if ( FAILED ( hr ) ) {
* errorMessage = msgComFailed ( " GetTypeSize " , hr ) ;
return false ;
}
} // size desired
return true ;
2009-04-20 16:40:50 +02:00
}
2009-05-05 09:52:24 +02:00
bool CdbDumperHelper : : initResolveSymbols ( QString * errorMessage )
2009-04-20 16:40:50 +02:00
{
2009-04-22 17:28:26 +02:00
// Resolve the symbols we need (potentially namespaced).
// There is a 'qDumpInBuffer' in QtCore as well.
m_dumpObjectSymbol = QLatin1String ( " *qDumpObjectData440 " ) ;
QString inBufferSymbol = QLatin1String ( " *qDumpInBuffer " ) ;
QString outBufferSymbol = QLatin1String ( " *qDumpOutBuffer " ) ;
const QString dumperModuleName = QLatin1String ( dumperModuleNameC ) ;
bool rc = resolveSymbol ( m_cif - > debugSymbols , & m_dumpObjectSymbol , errorMessage ) = = ResolveSymbolOk
& & resolveSymbol ( m_cif - > debugSymbols , dumperModuleName , & inBufferSymbol , errorMessage ) = = ResolveSymbolOk
& & resolveSymbol ( m_cif - > debugSymbols , dumperModuleName , & outBufferSymbol , errorMessage ) = = ResolveSymbolOk ;
if ( ! rc )
return false ;
// Determine buffer addresses, sizes and alloc buffer
rc = getSymbolAddress ( m_cif - > debugSymbols , inBufferSymbol , & m_inBufferAddress , & m_inBufferSize , errorMessage )
& & getSymbolAddress ( m_cif - > debugSymbols , outBufferSymbol , & m_outBufferAddress , & m_outBufferSize , errorMessage ) ;
if ( ! rc )
return false ;
m_buffer = new char [ qMax ( m_inBufferSize , m_outBufferSize ) ] ;
2009-04-20 16:40:50 +02:00
if ( loadDebug )
2009-04-22 17:28:26 +02:00
qDebug ( ) . nospace ( ) < < Q_FUNC_INFO < < ' \n ' < < rc < < m_dumpObjectSymbol
< < " buffers at 0x " < < QString : : number ( m_inBufferAddress , 16 ) < < ' , '
< < m_inBufferSize < < " ; 0x "
< < QString : : number ( m_outBufferAddress , 16 ) < < ' , ' < < m_outBufferSize < < ' \n ' ;
return true ;
}
2009-05-05 09:52:24 +02:00
// Call query protocol to retrieve known types and sizes
bool CdbDumperHelper : : initKnownTypes ( QString * errorMessage )
2009-04-22 17:28:26 +02:00
{
QByteArray output ;
2009-04-29 14:15:09 +02:00
QString callCmd ;
QTextStream ( & callCmd ) < < " .call " < < m_dumpObjectSymbol < < " (1,0,0,0,0,0,0,0) " ;
const char * outData ;
2009-05-22 14:31:37 +02:00
if ( ! callDumper ( callCmd , QByteArray ( ) , & outData , false , errorMessage ) ) {
2009-04-22 17:28:26 +02:00
return false ;
}
2009-04-29 14:15:09 +02:00
if ( ! m_helper . parseQuery ( outData , QtDumperHelper : : CdbDebugger ) ) {
2009-04-22 17:28:26 +02:00
* errorMessage = QString : : fromLatin1 ( " Unable to parse the dumper output: '%1' " ) . arg ( QString : : fromAscii ( output ) ) ;
}
if ( loadDebug )
2009-04-29 14:15:09 +02:00
qDebug ( ) < < Q_FUNC_INFO < < m_helper . toString ( true ) ;
2009-04-22 17:28:26 +02:00
return true ;
}
2009-04-29 14:15:09 +02:00
// Write to debuggee memory in chunks
bool CdbDumperHelper : : writeToDebuggee ( CIDebugDataSpaces * ds , const QByteArray & buffer , quint64 address , QString * errorMessage )
2009-04-22 17:28:26 +02:00
{
2009-04-29 14:15:09 +02:00
char * ptr = const_cast < char * > ( buffer . data ( ) ) ;
ULONG bytesToWrite = buffer . size ( ) ;
while ( bytesToWrite > 0 ) {
ULONG bytesWritten = 0 ;
const HRESULT hr = ds - > WriteVirtual ( address , ptr , bytesToWrite , & bytesWritten ) ;
if ( FAILED ( hr ) ) {
* errorMessage = msgComFailed ( " WriteVirtual " , hr ) ;
return false ;
}
bytesToWrite - = bytesWritten ;
ptr + = bytesWritten ;
}
return true ;
}
2009-05-22 14:31:37 +02:00
bool CdbDumperHelper : : callDumper ( const QString & callCmd , const QByteArray & inBuffer , const char * * outDataPtr ,
bool ignoreAccessViolation , QString * errorMessage )
2009-04-29 14:15:09 +02:00
{
* outDataPtr = 0 ;
CdbExceptionLoggerEventCallback exLogger ( m_messagePrefix , m_access ) ;
EventCallbackRedirector eventRedir ( m_cif - > debugClient , & exLogger ) ;
// write input buffer
if ( ! inBuffer . isEmpty ( ) ) {
if ( ! writeToDebuggee ( m_cif - > debugDataSpaces , inBuffer , m_inBufferAddress , errorMessage ) )
return false ;
}
2009-04-22 17:28:26 +02:00
if ( ! CdbDebugEnginePrivate : : executeDebuggerCommand ( m_cif - > debugControl , callCmd , errorMessage ) )
return false ;
2009-04-29 14:15:09 +02:00
// Set up call and a temporary breakpoint after it.
// Try to skip debuggee crash exceptions and dumper exceptions
2009-05-22 14:31:37 +02:00
// by using 'gN' (go not handled -> pass handling to dumper __try/__catch block)
2009-04-29 14:15:09 +02:00
for ( int i = 0 ; i < 10 ; i + + ) {
const int oldExceptionCount = exLogger . exceptionCount ( ) ;
// Go. If an exception occurs in loop 2, let the dumper handle it.
const QString goCmd = i ? QString ( QLatin1String ( " gN " ) ) : QString ( QLatin1Char ( ' g ' ) ) ;
if ( ! CdbDebugEnginePrivate : : executeDebuggerCommand ( m_cif - > debugControl , goCmd , errorMessage ) )
return false ;
HRESULT hr = m_cif - > debugControl - > WaitForEvent ( 0 , waitTimeOutMS ) ;
if ( FAILED ( hr ) ) {
* errorMessage = msgComFailed ( " WaitForEvent " , hr ) ;
return false ;
}
const int newExceptionCount = exLogger . exceptionCount ( ) ;
// no new exceptions? -> break
if ( oldExceptionCount = = newExceptionCount )
break ;
2009-05-22 14:31:37 +02:00
// If we are to ignore EXCEPTION_ACCESS_VIOLATION, check if anything
// else occurred.
if ( ignoreAccessViolation ) {
const QList < ULONG > newExceptionCodes = exLogger . exceptionCodes ( ) . mid ( oldExceptionCount ) ;
if ( newExceptionCodes . count ( EXCEPTION_ACCESS_VIOLATION ) = = newExceptionCodes . size ( ) )
break ;
}
2009-04-29 14:15:09 +02:00
}
if ( exLogger . exceptionCount ( ) ) {
const QString exMsgs = exLogger . exceptionMessages ( ) . join ( QString ( QLatin1Char ( ' , ' ) ) ) ;
* errorMessage = QString : : fromLatin1 ( " Exceptions occurred during the dumper call: %1 " ) . arg ( exMsgs ) ;
2009-04-22 17:28:26 +02:00
return false ;
}
// Read output
2009-04-29 14:15:09 +02:00
const HRESULT hr = m_cif - > debugDataSpaces - > ReadVirtual ( m_outBufferAddress , m_buffer , m_outBufferSize , 0 ) ;
2009-04-22 17:28:26 +02:00
if ( FAILED ( hr ) ) {
* errorMessage = msgComFailed ( " ReadVirtual " , hr ) ;
return false ;
}
// see QDumper implementation
2009-04-29 14:15:09 +02:00
const char result = m_buffer [ 0 ] ;
2009-04-22 17:28:26 +02:00
switch ( result ) {
case ' t ' :
break ;
case ' + ' :
* errorMessage = QString : : fromLatin1 ( " Dumper call '%1' resulted in output overflow. " ) . arg ( callCmd ) ;
return false ;
case ' f ' :
* errorMessage = QString : : fromLatin1 ( " Dumper call '%1' failed. " ) . arg ( callCmd ) ;
return false ;
default :
* errorMessage = QString : : fromLatin1 ( " Dumper call '%1' failed ('%2'). " ) . arg ( callCmd ) . arg ( QLatin1Char ( result ) ) ;
return false ;
}
2009-04-29 14:15:09 +02:00
* outDataPtr = m_buffer + 1 ;
return true ;
}
2009-05-05 09:52:24 +02:00
static inline QString msgDumpFailed ( const WatchData & wd , const QString * why )
{
return QString : : fromLatin1 ( " Unable to dump '%1' (%2): %3 " ) . arg ( wd . name , wd . type , * why ) ;
}
2009-04-29 14:15:09 +02:00
CdbDumperHelper : : DumpResult CdbDumperHelper : : dumpType ( const WatchData & wd , bool dumpChildren , int source ,
QList < WatchData > * result , QString * errorMessage )
{
// Check failure cache and supported types
2009-05-05 09:52:24 +02:00
if ( m_state = = Disabled | | m_failedTypes . contains ( wd . type ) )
2009-04-29 14:15:09 +02:00
return DumpNotHandled ;
2009-05-05 09:52:24 +02:00
// Ensure types are parsed and known.
if ( ! ensureInitialized ( errorMessage ) ) {
* errorMessage = msgDumpFailed ( wd , errorMessage ) ;
m_access - > showDebuggerOutput ( m_messagePrefix , * errorMessage ) ;
return DumpError ;
}
// Known type?
2009-04-29 16:52:14 +02:00
const QtDumperHelper : : TypeData td = m_helper . typeData ( wd . type ) ;
if ( loadDebug )
2009-05-05 09:52:24 +02:00
qDebug ( ) < < " dumpType " < < wd . type < < td ;
2009-04-29 14:15:09 +02:00
if ( td . type = = QtDumperHelper : : UnknownType )
return DumpNotHandled ;
// Now evaluate
2009-05-07 15:34:52 +02:00
const QString message = QCoreApplication : : translate ( " Debugger::Internal::CdbDumperHelper " ,
2009-04-29 14:15:09 +02:00
" Querying dumpers for '%1'/'%2' (%3) " ) .
arg ( wd . name , wd . exp , wd . type ) ;
m_access - > showDebuggerOutput ( m_messagePrefix , message ) ;
2009-05-05 09:52:24 +02:00
2009-04-29 14:15:09 +02:00
const DumpExecuteResult der = executeDump ( wd , td , dumpChildren , source , result , errorMessage ) ;
if ( der = = DumpExecuteOk )
return DumpOk ;
// Cache types that fail due to complicated template size expressions.
// Exceptions OTOH might occur when accessing variables that are not
// yet initialized in a particular breakpoint. That should be ignored
if ( der = = DumpExecuteSizeFailed )
m_failedTypes . push_back ( wd . type ) ;
// log error
2009-05-05 20:34:13 +02:00
* errorMessage = msgDumpFailed ( wd , errorMessage ) ;
2009-04-29 14:15:09 +02:00
m_access - > showDebuggerOutput ( m_messagePrefix , * errorMessage ) ;
return DumpError ;
}
CdbDumperHelper : : DumpExecuteResult
CdbDumperHelper : : executeDump ( const WatchData & wd ,
const QtDumperHelper : : TypeData & td , bool dumpChildren , int source ,
QList < WatchData > * result , QString * errorMessage )
{
QByteArray inBuffer ;
QStringList extraParameters ;
// Build parameter list.
m_helper . evaluationParameters ( wd , td , QtDumperHelper : : CdbDebugger , & inBuffer , & extraParameters ) ;
// If the parameter list contains sizeof-expressions, execute them separately
// and replace them by the resulting numbers
const QString sizeOfExpr = QLatin1String ( " sizeof " ) ;
const QStringList : : iterator eend = extraParameters . end ( ) ;
for ( QStringList : : iterator it = extraParameters . begin ( ) ; it ! = eend ; + + it ) {
// Strip 'sizeof(X)' to 'X' and query size
QString & ep = * it ;
if ( ep . startsWith ( sizeOfExpr ) ) {
int size ;
ep . truncate ( ep . lastIndexOf ( QLatin1Char ( ' ) ' ) ) ) ;
ep . remove ( 0 , ep . indexOf ( QLatin1Char ( ' ( ' ) ) + 1 ) ;
2009-04-29 16:52:14 +02:00
const bool sizeOk = getTypeSize ( ep , & size , errorMessage ) ;
2009-04-29 14:15:09 +02:00
if ( loadDebug )
2009-04-29 16:52:14 +02:00
qDebug ( ) < < " Size " < < sizeOk < < size < < ep ;
if ( ! sizeOk )
return DumpExecuteSizeFailed ;
2009-04-29 14:15:09 +02:00
ep = QString : : number ( size ) ;
}
}
// Execute call
QString callCmd ;
QTextStream ( & callCmd ) < < " .call " < < m_dumpObjectSymbol
< < " (2,0, " < < wd . addr < < ' , '
< < ( dumpChildren ? 1 : 0 ) < < ' , ' < < extraParameters . join ( QString ( QLatin1Char ( ' , ' ) ) ) < < ' ) ' ;
if ( loadDebug )
2009-04-29 16:52:14 +02:00
qDebug ( ) < < " Query: " < < wd . toString ( ) < < " \n with: " < < callCmd < < ' \n ' ;
2009-04-29 14:15:09 +02:00
const char * outputData ;
2009-05-22 14:31:37 +02:00
// Completely ignore EXCEPTION_ACCESS_VIOLATION crashes in the dumpers.
ExceptionBlocker eb ( m_cif - > debugControl , EXCEPTION_ACCESS_VIOLATION , ExceptionBlocker : : IgnoreException ) ;
if ( ! eb ) {
* errorMessage = eb . errorString ( ) ;
return DumpExecuteCallFailed ;
}
if ( ! callDumper ( callCmd , inBuffer , & outputData , true , errorMessage ) )
2009-04-29 14:15:09 +02:00
return DumpExecuteCallFailed ;
QtDumperResult dumpResult ;
if ( ! QtDumperHelper : : parseValue ( outputData , & dumpResult ) ) {
* errorMessage = QLatin1String ( " Parsing of value query output failed. " ) ;
return DumpExecuteCallFailed ;
}
* result = dumpResult . toWatchData ( source ) ;
return DumpExecuteOk ;
}
// Simplify some types for sizeof expressions
static inline void simplifySizeExpression ( QString * typeName )
{
typeName - > replace ( QLatin1String ( " std::basic_string<char,std::char_traits<char>,std::allocator<char>> " ) ,
QLatin1String ( " std::string " ) ) ;
}
bool CdbDumperHelper : : getTypeSize ( const QString & typeNameIn , int * size , QString * errorMessage )
{
if ( loadDebug > 1 )
qDebug ( ) < < Q_FUNC_INFO < < typeNameIn ;
// Look up cache
QString typeName = typeNameIn ;
simplifySizeExpression ( & typeName ) ;
// "std::" types sometimes only work without namespace.
// If it fails, try again with stripped namespace
* size = 0 ;
bool success = false ;
do {
if ( runTypeSizeQuery ( typeName , size , errorMessage ) ) {
success = true ;
break ;
}
const QString stdNameSpace = QLatin1String ( " std:: " ) ;
if ( ! typeName . contains ( stdNameSpace ) )
break ;
typeName . remove ( stdNameSpace ) ;
errorMessage - > clear ( ) ;
if ( ! runTypeSizeQuery ( typeName , size , errorMessage ) )
break ;
success = true ;
} while ( false ) ;
2009-04-29 16:52:14 +02:00
// Cache in dumper helper
2009-04-29 14:15:09 +02:00
if ( success )
2009-04-29 16:52:14 +02:00
m_helper . addSize ( typeName , * size ) ;
2009-04-29 14:15:09 +02:00
return success ;
}
bool CdbDumperHelper : : runTypeSizeQuery ( const QString & typeName , int * size , QString * errorMessage )
{
// Retrieve by C++ expression. If we knew the module, we could make use
// of the TypeId query mechanism provided by the IDebugSymbolGroup.
DEBUG_VALUE sizeValue ;
QString expression = QLatin1String ( " sizeof( " ) ;
expression + = typeName ;
expression + = QLatin1Char ( ' ) ' ) ;
if ( ! CdbDebugEnginePrivate : : evaluateExpression ( m_cif - > debugControl ,
expression , & sizeValue , errorMessage ) )
return false ;
qint64 size64 ;
if ( ! CdbSymbolGroupContext : : debugValueToInteger ( sizeValue , & size64 ) ) {
* errorMessage = QLatin1String ( " Expression result is not an integer " ) ;
return false ;
}
* size = static_cast < int > ( size64 ) ;
2009-04-22 17:28:26 +02:00
return true ;
2009-04-20 16:40:50 +02:00
}
} // namespace Internal
} // namespace Debugger