Commit 56d36649 authored by Friedemann Kleint's avatar Friedemann Kleint
Browse files

Debugger: Add extension library to be loaded into CDB.exe

parent 8477c7bc
......@@ -14,3 +14,5 @@ SUBDIRS = \
qmleditorwidgets \
symbianutils \
3rdparty
win32:SUBDIRS += qtcreatorcdbext
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** 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
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include "base64.h"
#include <ostream>
static void base64EncodeTriple(std::ostream &str, const unsigned char triple[3], size_t length = 3)
{
static const char base64Encoding[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// Convert 3 bytes of triplet into 4 bytes, write out 64 bit-wise using a character mapping,
// see description of base64.
unsigned tripleValue = triple[0] * 256;
tripleValue += triple[1];
tripleValue *= 256;
tripleValue += triple[2];
char result[4]= {0, 0, 0, 0};
for (int i = 3; i >= 0 ; i--) {
result[i] = base64Encoding[tripleValue % 64];
tripleValue /= 64;
}
// Write out quad and pad if it is a padding triple.
const size_t writeLength = length + 1;
size_t i = 0;
for ( ; i < writeLength; i++)
str << result[i];
for ( ; i < 4; i++)
str << '=';
}
void base64Encode(std::ostream &str, const unsigned char *source, size_t sourcelen)
{
if (!sourcelen) {
str << "====";
return;
}
/* Encode triples */
const unsigned char *sourceEnd = source + sourcelen;
for (const unsigned char *triple = source; triple < sourceEnd; ) {
const unsigned char *nextTriple = triple + 3;
if (nextTriple <= sourceEnd) { // Encode full triple, including very last one
base64EncodeTriple(str, triple);
triple = nextTriple;
} else { // Past end and some padding required.
unsigned char paddingTriple[3] = {0, 0, 0};
std::copy(triple, sourceEnd, paddingTriple);
base64EncodeTriple(str, paddingTriple, sourceEnd - triple);
break;
}
}
}
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** 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
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#ifndef BASE64_H
#define BASE64_H
#include <iosfwd>
// Base 64 encoding helper
void base64Encode(std::ostream &str, const unsigned char *source, size_t sourcelen);
#endif // BASE64_H
# Detect presence of "Debugging Tools For Windows"
# in case MS VS compilers are used.
CDB_PATH=""
win32 {
contains(QMAKE_CXX, cl) {
CDB_PATH="$$(CDB_PATH)"
isEmpty(CDB_PATH):CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows/sdk"
!exists($$CDB_PATH):CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows (x86)/sdk"
!exists($$CDB_PATH):CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows (x64)/sdk"
!exists($$CDB_PATH):CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows 64-bit/sdk"
}
}
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** 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
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include "common.h"
#include "iinterfacepointer.h"
#include <sstream>
std::string winErrorMessage(unsigned long error)
{
char *lpMsgBuf;
const int len = FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, error, 0, (LPSTR)&lpMsgBuf, 0, NULL);
if (len) {
const std::string rc(lpMsgBuf, len);
LocalFree(lpMsgBuf);
return rc;
}
std::ostringstream str;
str << "Unknown error " << error;
return str.str();
}
std::string winErrorMessage()
{
return winErrorMessage(GetLastError());
}
std::string msgDebugEngineComResult(HRESULT hr)
{
switch (hr) {
case S_OK:
return std::string("S_OK");
case S_FALSE:
return std::string("S_FALSE");
case E_FAIL:
break;
case E_INVALIDARG:
return std::string("E_INVALIDARG");
case E_NOINTERFACE:
return std::string("E_NOINTERFACE");
case E_OUTOFMEMORY:
return std::string("E_OUTOFMEMORY");
case E_UNEXPECTED:
return std::string("E_UNEXPECTED");
case E_NOTIMPL:
return std::string("E_NOTIMPL");
}
if (hr == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED))
return std::string("ERROR_ACCESS_DENIED");;
if (hr == HRESULT_FROM_NT(STATUS_CONTROL_C_EXIT))
return std::string("STATUS_CONTROL_C_EXIT");
return std::string("E_FAIL ") + winErrorMessage(HRESULT_CODE(hr));
}
std::string msgDebugEngineComFailed(const char *func, HRESULT hr)
{
std::string rc = func;
rc += " failed: ";
rc += msgDebugEngineComResult(hr);
return rc;
}
ULONG currentThreadId(IDebugSystemObjects *sysObjects)
{
ULONG id = 0;
if (sysObjects->GetCurrentThreadId(&id) == S_OK)
return id;
return 0;
}
ULONG currentThreadId(CIDebugClient *client)
{
IInterfacePointer<IDebugSystemObjects> sysObjects(client);
if (sysObjects)
return currentThreadId(sysObjects.data());
return 0;
}
ULONG currentProcessId(IDebugSystemObjects *sysObjects)
{
ULONG64 handle = 0;
if (sysObjects->GetCurrentProcessHandle(&handle) == S_OK)
return GetProcessId((HANDLE)handle);
return 0;
}
ULONG currentProcessId(CIDebugClient *client)
{
IInterfacePointer<IDebugSystemObjects> sysObjects(client);
if (sysObjects)
return currentProcessId(sysObjects.data());
return 0;
}
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** 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
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#ifndef COMMON_H
#define COMMON_H
// Define KDEXT_64BIT to make all wdbgexts APIs recognize 64 bit addresses
// It is recommended for extensions to use 64 bit headers from wdbgexts so
// the extensions could support 64 bit targets.
#include <string>
#include <sstream>
#include <windows.h>
#define KDEXT_64BIT
#include <wdbgexts.h>
#include <dbgeng.h>
static const char creatorOutputPrefixC[] = "QtCreatorExt: ";
typedef IDebugControl CIDebugControl;
typedef IDebugSymbols3 CIDebugSymbols;
typedef IDebugSymbolGroup2 CIDebugSymbolGroup;
typedef IDebugClient5 CIDebugClient;
typedef IDebugSystemObjects CIDebugSystemObjects;
typedef IDebugDataSpaces4 CIDebugDataSpaces;
typedef IDebugAdvanced2 CIDebugAdvanced;
typedef IDebugRegisters2 CIDebugRegisters;
// Utility messages
std::string winErrorMessage(unsigned long error);
std::string winErrorMessage();
std::string msgDebugEngineComResult(HRESULT hr);
std::string msgDebugEngineComFailed(const char *func, HRESULT hr);
// Debug helper for anything streamable as in 'DebugPrint() << object'
// Derives from std::ostringstream and print out everything accumulated in destructor.
struct DebugPrint : public std::ostringstream {
DebugPrint() {}
~DebugPrint() {
dprintf("DEBUG: %s\n", str().c_str());
}
};
ULONG currentThreadId(IDebugSystemObjects *sysObjects);
ULONG currentThreadId(CIDebugClient *client);
ULONG currentProcessId(IDebugSystemObjects *sysObjects);
ULONG currentProcessId(CIDebugClient *client);
#endif // COMMON_H
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** 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
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include "eventcallback.h"
#include "extensioncontext.h"
#include "stringutils.h"
#include "gdbmihelpers.h"
static const char eventContextC[] = "event";
static const char moduleContextC[] = "module";
// Special exception codes (see dbgwinutils.cpp).
enum { winExceptionCppException = 0xe06d7363,
winExceptionStartupCompleteTrap = 0x406d1388,
winExceptionRpcServerUnavailable = 0x6ba,
winExceptionRpcServerInvalid = 0x6a6,
winExceptionDllNotFound = 0xc0000135,
winExceptionDllEntryPointNoFound = 0xc0000139,
winExceptionDllInitFailed = 0xc0000142,
winExceptionMissingSystemFile = 0xc0000143,
winExceptionAppInitFailed = 0xc0000143
};
EventCallback::EventCallback(IDebugEventCallbacks *wrapped) :
m_wrapped(wrapped)
{
}
EventCallback::~EventCallback()
{
}
STDMETHODIMP EventCallback::QueryInterface(
THIS_
IN REFIID InterfaceId,
OUT PVOID* Interface)
{
*Interface = NULL;
if (IsEqualIID(InterfaceId, __uuidof(IUnknown)) ||
IsEqualIID(InterfaceId, __uuidof(IDebugOutputCallbacks))) {
*Interface = (IDebugOutputCallbacks *)this;
AddRef();
return S_OK;
} else {
return E_NOINTERFACE;
}
}
STDMETHODIMP_(ULONG) EventCallback::AddRef(THIS)
{
// This class is designed to be static so
// there's no true refcount.
return 1;
}
STDMETHODIMP_(ULONG) EventCallback::Release(THIS)
{
// This class is designed to be static so
// there's no true refcount.
return 0;
}
STDMETHODIMP EventCallback::GetInterestMask(THIS_ __out PULONG mask)
{
if (m_wrapped)
m_wrapped->GetInterestMask(mask);
*mask |= DEBUG_EVENT_CREATE_PROCESS | DEBUG_EVENT_EXIT_PROCESS
| DEBUG_EVENT_BREAKPOINT
| DEBUG_EVENT_EXCEPTION | DEBUG_EVENT_LOAD_MODULE | DEBUG_EVENT_UNLOAD_MODULE;
return S_OK;
}
STDMETHODIMP EventCallback::Breakpoint(THIS_ __in PDEBUG_BREAKPOINT b)
{
// Breakpoint hit - Set the stop reason parameters on the extension context.
typedef ExtensionContext::StopReasonMap StopReasonMap;
typedef ExtensionContext::StopReasonMap::value_type StopReasonMapValue;
ULONG id = 0;
ULONG64 address = 0;
b->GetId(&id);
b->GetOffset(&address);
StopReasonMap stopReason;
stopReason.insert(StopReasonMapValue(std::string("breakpointId"), toString(id)));
if (address)
stopReason.insert(StopReasonMapValue(std::string("breakpointAddress"), toString(address)));
ExtensionContext::instance().setStopReason(stopReason, "breakpoint");
return m_wrapped ? m_wrapped->Breakpoint(b) : S_OK;
}
static inline ExtensionContext::StopReasonMap exceptionParameters(const EXCEPTION_RECORD64 &e,
unsigned firstChance)
{
typedef ExtensionContext::StopReasonMap StopReasonMap;
typedef ExtensionContext::StopReasonMap::value_type StopReasonMapValue;
// Fill exception record
StopReasonMap parameters;
parameters.insert(StopReasonMapValue(std::string("firstChance"), toString(firstChance)));
parameters.insert(StopReasonMapValue(std::string("exceptionAddress"),
toString(e.ExceptionAddress)));
parameters.insert(StopReasonMapValue(std::string("exceptionCode"),
toString(e.ExceptionCode)));
parameters.insert(StopReasonMapValue(std::string("exceptionFlags"),
toString(e.ExceptionFlags)));
// Hard code some parameters (used for access violations)
if (e.NumberParameters >= 1)
parameters.insert(StopReasonMapValue(std::string("exceptionInformation0"),
toString(e.ExceptionInformation[0])));
if (e.NumberParameters >= 2)
parameters.insert(StopReasonMapValue(std::string("exceptionInformation1"),
toString(e.ExceptionInformation[1])));
// Add top stack frame if possible
StackFrame frame;
std::string errorMessage;
// If it is a C++ exception, get frame #2 (first outside MSVC runtime)
const unsigned frameNumber = e.ExceptionCode == winExceptionCppException ? 2 : 0;
if (getFrame(frameNumber, &frame, &errorMessage)) {
if (!frame.fullPathName.empty()) {
parameters.insert(StopReasonMapValue(std::string("exceptionFile"),
wStringToString(frame.fullPathName)));
parameters.insert(StopReasonMapValue(std::string("exceptionLine"),
toString(frame.line)));
}
if (!frame.function.empty())
parameters.insert(StopReasonMapValue(std::string("exceptionFunction"),
wStringToString(frame.function)));
} // getCurrentFrame
return parameters;
}
STDMETHODIMP EventCallback::Exception(
THIS_
__in PEXCEPTION_RECORD64 Ex,
__in ULONG FirstChance
)
{
// Report the exception as GBMI and set potential stop reason
const ExtensionContext::StopReasonMap parameters =
exceptionParameters(*Ex, FirstChance);
std::ostringstream str;
formatGdbmiHash(str, parameters);
ExtensionContext::instance().setStopReason(parameters, "exception");
ExtensionContext::instance().report('E', 0, "exception", "%s", str.str().c_str());
return m_wrapped ? m_wrapped->Exception(Ex, FirstChance) : S_OK;
}
STDMETHODIMP EventCallback::CreateThread(
THIS_
__in ULONG64 Handle,
__in ULONG64 DataOffset,
__in ULONG64 StartOffset
)
{
return m_wrapped ? m_wrapped->CreateThread(Handle, DataOffset, StartOffset) : S_OK;
}
STDMETHODIMP EventCallback::ExitThread(
THIS_
__in ULONG ExitCode
)
{
return m_wrapped ? m_wrapped->ExitThread(ExitCode) : S_OK;
}
STDMETHODIMP EventCallback::CreateProcess(
THIS_
__in ULONG64 ImageFileHandle,
__in ULONG64 Handle,
__in ULONG64 BaseOffset,
__in ULONG ModuleSize,
__in_opt PCSTR ModuleName,
__in_opt PCSTR ImageName,
__in ULONG CheckSum,
__in ULONG TimeDateStamp,
__in ULONG64 InitialThreadHandle,
__in ULONG64 ThreadDataOffset,
__in ULONG64 StartOffset
)
{
return m_wrapped ? m_wrapped->CreateProcess(ImageFileHandle, Handle,
BaseOffset, ModuleSize, ModuleName,
ImageName, CheckSum, TimeDateStamp,
InitialThreadHandle, ThreadDataOffset,
StartOffset) : S_OK;
}
STDMETHODIMP EventCallback::ExitProcess(
THIS_
__in ULONG ExitCode
)
{
ExtensionContext::instance().report('E', 0, eventContextC, "Process exited (%lu)",
ExitCode);
dprintf("%s ExitProcess %u\n", creatorOutputPrefixC, ExitCode);
return m_wrapped ? m_wrapped->ExitProcess(ExitCode) : S_OK;
}
STDMETHODIMP EventCallback::LoadModule(
THIS_
__in ULONG64 ImageFileHandle,
__in ULONG64 BaseOffset,
__in ULONG ModuleSize,
__in_opt PCSTR ModuleName,
__in_opt PCSTR ImageName,
__in ULONG CheckSum,
__in ULONG TimeDateStamp
)
{
ExtensionContext::instance().report('E', 0, moduleContextC, "L:%s:%s:0x%llx:0x%llx\n",
ModuleName, ImageName, BaseOffset, ModuleSize);
return m_wrapped ? m_wrapped->LoadModule(ImageFileHandle, BaseOffset,
ModuleSize, ModuleName, ImageName,
CheckSum, TimeDateStamp) : S_OK;
}
STDMETHODIMP EventCallback::UnloadModule(
THIS_
__in_opt PCSTR ImageBaseName,
__in ULONG64 BaseOffset
)
{
ExtensionContext::instance().report('U', 0, moduleContextC, "U:%s\n",
ImageBaseName);
return m_wrapped ? m_wrapped->UnloadModule(ImageBaseName, BaseOffset) : S_OK;
}
STDMETHODIMP EventCallback::SystemError(
THIS_
__in ULONG Error,
__in ULONG Level
)
{
return m_wrapped ? m_wrapped->SystemError(Error, Level) : S_OK;
}
STDMETHODIMP EventCallback::SessionStatus(