2010-11-18 13:55:52 +01:00
|
|
|
/**************************************************************************
|
|
|
|
|
**
|
|
|
|
|
** This file is part of Qt Creator
|
|
|
|
|
**
|
2011-01-11 16:28:15 +01:00
|
|
|
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
2010-11-18 13:55:52 +01:00
|
|
|
**
|
2011-04-13 08:42:33 +02:00
|
|
|
** Contact: Nokia Corporation (info@qt.nokia.com)
|
2010-11-18 13:55:52 +01:00
|
|
|
**
|
|
|
|
|
**
|
|
|
|
|
** GNU Lesser General Public License Usage
|
|
|
|
|
**
|
2011-04-13 08:42:33 +02:00
|
|
|
** 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.
|
2010-11-18 13:55:52 +01:00
|
|
|
**
|
2010-12-17 17:14:20 +01:00
|
|
|
** In addition, as a special exception, Nokia gives you certain additional
|
2011-04-13 08:42:33 +02:00
|
|
|
** rights. These rights are described in the Nokia Qt LGPL Exception
|
2010-12-17 17:14:20 +01:00
|
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
|
|
**
|
2011-04-13 08:42:33 +02:00
|
|
|
** Other Usage
|
|
|
|
|
**
|
|
|
|
|
** Alternatively, this file may be used in accordance with the terms and
|
|
|
|
|
** conditions contained in a signed written agreement between you and Nokia.
|
|
|
|
|
**
|
2010-12-17 17:14:20 +01:00
|
|
|
** If you have questions regarding the use of this file, please contact
|
|
|
|
|
** Nokia at qt-info@nokia.com.
|
2010-11-18 13:55:52 +01:00
|
|
|
**
|
|
|
|
|
**************************************************************************/
|
|
|
|
|
|
|
|
|
|
#include "gdbmihelpers.h"
|
|
|
|
|
#include "stringutils.h"
|
|
|
|
|
#include "iinterfacepointer.h"
|
|
|
|
|
#include "base64.h"
|
2011-01-18 11:40:45 +01:00
|
|
|
#include "symbolgroupvalue.h"
|
|
|
|
|
#include "extensioncontext.h"
|
2010-11-18 13:55:52 +01:00
|
|
|
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
StackFrame::StackFrame(ULONG64 a) : address(a), line(0) {}
|
|
|
|
|
|
|
|
|
|
std::wstring StackFrame::fileName() const
|
|
|
|
|
{
|
|
|
|
|
std::wstring::size_type lastSlash = fullPathName.rfind(L'\\');
|
|
|
|
|
if (lastSlash == std::wstring::npos)
|
|
|
|
|
return fullPathName;
|
|
|
|
|
return fullPathName.substr(lastSlash + 1, fullPathName.size() - lastSlash - 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void StackFrame::formatGDBMI(std::ostream &str, unsigned level) const
|
|
|
|
|
{
|
|
|
|
|
str << "frame={level=\"" << level << "\",addr=\"0x"
|
|
|
|
|
<< std::hex << address << std::dec << '"';
|
2010-11-22 13:50:40 +01:00
|
|
|
if (!function.empty()) {
|
|
|
|
|
// Split into module/function
|
|
|
|
|
const std::wstring::size_type exclPos = function.find('!');
|
|
|
|
|
if (exclPos == std::wstring::npos) {
|
|
|
|
|
str << ",func=\"" << gdbmiWStringFormat(function) << '"';
|
|
|
|
|
} else {
|
|
|
|
|
const std::wstring module = function.substr(0, exclPos);
|
|
|
|
|
const std::wstring fn = function.substr(exclPos + 1, function.size() - exclPos - 1);
|
|
|
|
|
str << ",func=\"" << gdbmiWStringFormat(fn)
|
|
|
|
|
<< "\",from=\"" << gdbmiWStringFormat(module) << '"';
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-11-18 13:55:52 +01:00
|
|
|
if (!fullPathName.empty()) { // Creator/gdbmi expects 'clean paths'
|
|
|
|
|
std::wstring cleanPath = fullPathName;
|
|
|
|
|
replace(cleanPath, L'\\', L'/');
|
|
|
|
|
str << ",fullname=\"" << gdbmiWStringFormat(fullPathName)
|
|
|
|
|
<< "\",file=\"" << gdbmiWStringFormat(fileName()) << "\",line=\"" << line << '"';
|
|
|
|
|
}
|
|
|
|
|
str << '}';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Thread::Thread(ULONG i, ULONG sysId) : id(i), systemId(sysId) {}
|
|
|
|
|
|
|
|
|
|
void Thread::formatGDBMI(std::ostream &str) const
|
|
|
|
|
{
|
|
|
|
|
str << "{id=\"" << id << "\",target-id=\"" << systemId << "\",";
|
|
|
|
|
frame.formatGDBMI(str);
|
|
|
|
|
if (!name.empty())
|
|
|
|
|
str << ",name=\"" << gdbmiWStringFormat(name) << '"';
|
|
|
|
|
if (!state.empty())
|
|
|
|
|
str << ",state=\"" << state << '"';
|
|
|
|
|
str << '}';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline std::string msgGetThreadsFailed(const std::string &why)
|
|
|
|
|
{
|
|
|
|
|
return std::string("Unable to determine the thread information: ") + why;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline bool setCurrentThread(CIDebugSystemObjects *debugSystemObjects,
|
|
|
|
|
ULONG id, std::string *errorMessage)
|
|
|
|
|
{
|
|
|
|
|
const HRESULT hr = debugSystemObjects->SetCurrentThreadId(id);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
*errorMessage = msgDebugEngineComFailed("SetCurrentThreadId", hr);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fill in stack frame info
|
|
|
|
|
void getFrame(CIDebugSymbols *debugSymbols,
|
|
|
|
|
const DEBUG_STACK_FRAME &s, StackFrame *f)
|
|
|
|
|
{
|
|
|
|
|
WCHAR wBuf[MAX_PATH];
|
|
|
|
|
f->address = s.InstructionOffset;
|
|
|
|
|
HRESULT hr = debugSymbols->GetNameByOffsetWide(f->address, wBuf, MAX_PATH, 0, 0);
|
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
|
f->function = wBuf;
|
|
|
|
|
} else {
|
|
|
|
|
f->function.clear();
|
|
|
|
|
}
|
|
|
|
|
ULONG64 ul64Displacement = 0;
|
|
|
|
|
hr = debugSymbols->GetLineByOffsetWide(f->address, &(f->line), wBuf, MAX_PATH, 0, &ul64Displacement);
|
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
|
f->fullPathName = wBuf;
|
|
|
|
|
} else {
|
|
|
|
|
f->fullPathName.clear();
|
|
|
|
|
f->line = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Determine the frames of the threads.
|
|
|
|
|
// Note: Current thread is switched as a side effect!
|
|
|
|
|
static bool getThreadFrames(CIDebugSymbols *debugSymbols,
|
|
|
|
|
CIDebugSystemObjects *debugSystemObjects,
|
|
|
|
|
CIDebugControl *debugControl,
|
|
|
|
|
Threads *threads, std::string *errorMessage)
|
|
|
|
|
{
|
|
|
|
|
enum { MaxFrames = 1 };
|
|
|
|
|
|
|
|
|
|
DEBUG_STACK_FRAME frames[MaxFrames];
|
|
|
|
|
const Threads::iterator end = threads->end();
|
|
|
|
|
for (Threads::iterator it = threads->begin(); it != end; ++it) {
|
|
|
|
|
if (!setCurrentThread(debugSystemObjects, it->id, errorMessage))
|
|
|
|
|
return false;
|
|
|
|
|
ULONG frameCount;
|
|
|
|
|
const HRESULT hr = debugControl->GetStackTrace(0, 0, 0, frames, MaxFrames, &frameCount);
|
|
|
|
|
if (SUCCEEDED(hr))
|
|
|
|
|
getFrame(debugSymbols, frames[0], &(it->frame));
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool getFrame(unsigned n, StackFrame *f, std::string *errorMessage)
|
|
|
|
|
{
|
|
|
|
|
IInterfacePointer<CIDebugClient> client;
|
|
|
|
|
if (!client.create()) {
|
|
|
|
|
*errorMessage = "Cannot obtain client.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
IInterfacePointer<CIDebugSymbols> symbols(client.data());
|
|
|
|
|
IInterfacePointer<CIDebugControl> control(client.data());
|
|
|
|
|
if (!symbols || !control) {
|
|
|
|
|
*errorMessage = "Cannot obtain required objects.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return getFrame(symbols.data(), control.data(), n, f, errorMessage);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool getFrame(CIDebugSymbols *debugSymbols,
|
|
|
|
|
CIDebugControl *debugControl,
|
|
|
|
|
unsigned n, StackFrame *f, std::string *errorMessage)
|
|
|
|
|
{
|
|
|
|
|
DEBUG_STACK_FRAME *frames = new DEBUG_STACK_FRAME[n + 1];
|
|
|
|
|
ULONG frameCount;
|
|
|
|
|
const HRESULT hr = debugControl->GetStackTrace(0, 0, 0, frames, n + 1, &frameCount);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
delete [] frames;
|
|
|
|
|
*errorMessage = msgDebugEngineComFailed("GetStackTrace", hr);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
getFrame(debugSymbols, frames[n], f);
|
|
|
|
|
delete [] frames;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool threadList(CIDebugSystemObjects *debugSystemObjects,
|
|
|
|
|
CIDebugSymbols *debugSymbols,
|
|
|
|
|
CIDebugControl *debugControl,
|
|
|
|
|
CIDebugAdvanced *debugAdvanced,
|
|
|
|
|
Threads* threads, ULONG *currentThreadId,
|
|
|
|
|
std::string *errorMessage)
|
|
|
|
|
{
|
|
|
|
|
threads->clear();
|
|
|
|
|
ULONG threadCount;
|
|
|
|
|
*currentThreadId = 0;
|
|
|
|
|
// Get count
|
|
|
|
|
HRESULT hr= debugSystemObjects->GetNumberThreads(&threadCount);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
*errorMessage= msgGetThreadsFailed(msgDebugEngineComFailed("GetNumberThreads", hr));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// Get index of current
|
|
|
|
|
if (!threadCount)
|
|
|
|
|
return true;
|
|
|
|
|
hr = debugSystemObjects->GetCurrentThreadId(currentThreadId);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
*errorMessage= msgGetThreadsFailed(msgDebugEngineComFailed("GetCurrentThreadId", hr));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// Get Identifiers
|
|
|
|
|
threads->reserve(threadCount);
|
|
|
|
|
ULONG *ids = new ULONG[threadCount];
|
|
|
|
|
ULONG *systemIds = new ULONG[threadCount];
|
|
|
|
|
hr = debugSystemObjects->GetThreadIdsByIndex(0, threadCount, ids, systemIds);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
*errorMessage= msgGetThreadsFailed(msgDebugEngineComFailed("GetThreadIdsByIndex", hr));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// Create entries
|
|
|
|
|
static WCHAR name[256];
|
|
|
|
|
for (ULONG i= 0; i < threadCount ; i++) {
|
|
|
|
|
const ULONG id = ids[i];
|
|
|
|
|
Thread thread(id, systemIds[i]);
|
|
|
|
|
// Thread name
|
|
|
|
|
ULONG bytesReceived = 0;
|
|
|
|
|
hr = debugAdvanced->GetSystemObjectInformation(DEBUG_SYSOBJINFO_THREAD_NAME_WIDE,
|
|
|
|
|
0, id, name,
|
|
|
|
|
sizeof(name), &bytesReceived);
|
|
|
|
|
if (SUCCEEDED(hr) && bytesReceived)
|
|
|
|
|
thread.name = name;
|
|
|
|
|
threads->push_back(thread);
|
|
|
|
|
}
|
|
|
|
|
delete [] ids;
|
|
|
|
|
delete [] systemIds;
|
|
|
|
|
// Get frames and at all events,
|
|
|
|
|
// restore current thread after switching frames.
|
|
|
|
|
const bool framesOk = getThreadFrames(debugSymbols,
|
|
|
|
|
debugSystemObjects,
|
|
|
|
|
debugControl,
|
|
|
|
|
threads, errorMessage);
|
|
|
|
|
const bool restoreOk =setCurrentThread(debugSystemObjects, *currentThreadId, errorMessage);
|
|
|
|
|
return framesOk && restoreOk;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Format as: \code
|
|
|
|
|
|
|
|
|
|
52^done,threads=[{id="1",target-id="Thread 4740.0xb30",
|
|
|
|
|
frame={level="0",addr="0x00403487",func="foo",
|
|
|
|
|
args=[{name="this",value="0x27fee4"}],file="mainwindow.cpp",fullname="c:\\qt\\projects\\gitguim\\app/mainwindow.cpp",line="298"},state="stopped"}],
|
|
|
|
|
current-thread-id="1"
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
std::string gdbmiThreadList(CIDebugSystemObjects *debugSystemObjects,
|
|
|
|
|
CIDebugSymbols *debugSymbols,
|
|
|
|
|
CIDebugControl *debugControl,
|
|
|
|
|
CIDebugAdvanced *debugAdvanced,
|
|
|
|
|
std::string *errorMessage)
|
|
|
|
|
{
|
|
|
|
|
Threads threads;
|
|
|
|
|
ULONG currentThreadId;
|
|
|
|
|
if (!threadList(debugSystemObjects, debugSymbols, debugControl,
|
|
|
|
|
debugAdvanced, &threads, ¤tThreadId, errorMessage))
|
|
|
|
|
return std::string();
|
|
|
|
|
std::ostringstream str;
|
|
|
|
|
str << "{threads=[";
|
|
|
|
|
const Threads::const_iterator cend = threads.end();
|
|
|
|
|
for (Threads::const_iterator it = threads.begin(); it != cend; ++it)
|
|
|
|
|
it->formatGDBMI(str);
|
|
|
|
|
str << "],current-thread-id=\"" << currentThreadId << "\"}";
|
|
|
|
|
return str.str();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Module::Module() : deferred(false), base(0), size(0)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Modules getModules(CIDebugSymbols *syms, std::string *errorMessage)
|
|
|
|
|
{
|
|
|
|
|
enum { BufSize = 1024 };
|
|
|
|
|
|
|
|
|
|
char nameBuf[BufSize];
|
|
|
|
|
char fileBuf[BufSize];
|
|
|
|
|
|
|
|
|
|
ULONG Loaded;
|
|
|
|
|
ULONG Unloaded;
|
|
|
|
|
HRESULT hr = syms->GetNumberModules(&Loaded, &Unloaded);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
*errorMessage = msgDebugEngineComFailed("GetNumberModules", hr);
|
|
|
|
|
return Modules();
|
|
|
|
|
}
|
|
|
|
|
const ULONG count = Loaded + Unloaded;
|
|
|
|
|
Modules rc;
|
|
|
|
|
rc.reserve(count);
|
|
|
|
|
DEBUG_MODULE_PARAMETERS *parameters = new DEBUG_MODULE_PARAMETERS[count];
|
|
|
|
|
hr = syms->GetModuleParameters(count, NULL, 0, parameters);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
delete [] parameters;
|
|
|
|
|
*errorMessage = msgDebugEngineComFailed("GetModuleParameters", hr);
|
|
|
|
|
return Modules();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (ULONG m = 0; m < count; m++) {
|
|
|
|
|
Module module;
|
|
|
|
|
module.base = parameters[m].Base;
|
|
|
|
|
module.size = parameters[m].Size;
|
|
|
|
|
module.deferred = parameters[m].Flags == DEBUG_SYMTYPE_DEFERRED;
|
|
|
|
|
hr = syms->GetModuleNames(m, 0, fileBuf, BufSize, NULL,
|
|
|
|
|
nameBuf, BufSize, NULL, NULL, NULL, NULL);
|
|
|
|
|
if (FAILED(hr))
|
|
|
|
|
break; // Fail silently should unloaded modules not work.
|
|
|
|
|
module.name = nameBuf;
|
|
|
|
|
module.image = fileBuf;
|
|
|
|
|
rc.push_back(module);
|
|
|
|
|
}
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string gdbmiModules(CIDebugSymbols *syms, bool humanReadable, std::string *errorMessage)
|
|
|
|
|
{
|
|
|
|
|
const Modules modules = getModules(syms, errorMessage);
|
|
|
|
|
if (modules.empty())
|
|
|
|
|
return std::string();
|
|
|
|
|
|
|
|
|
|
std::ostringstream str;
|
|
|
|
|
str << '[' << std::hex << std::showbase;
|
|
|
|
|
const Modules::size_type size = modules.size();
|
|
|
|
|
for (Modules::size_type m = 0; m < size; m++) {
|
|
|
|
|
const Module &module = modules.at(m);
|
|
|
|
|
if (m)
|
|
|
|
|
str << ',';
|
|
|
|
|
str << "{name=\"" << module.name
|
|
|
|
|
<< "\",image=\"" << gdbmiStringFormat(module.image)
|
|
|
|
|
<< "\",start=\"" << module.base << "\",end=\""
|
|
|
|
|
<< (module.base + module.size - 1) << '"';
|
|
|
|
|
if (module.deferred)
|
|
|
|
|
str << "{deferred=\"true\"";
|
|
|
|
|
str << '}';
|
|
|
|
|
if (humanReadable)
|
|
|
|
|
str << '\n';
|
|
|
|
|
}
|
|
|
|
|
str << ']';
|
|
|
|
|
return str.str();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Description of a DEBUG_VALUE type field
|
|
|
|
|
const wchar_t *valueType(ULONG type)
|
|
|
|
|
{
|
|
|
|
|
switch (type) {
|
|
|
|
|
case DEBUG_VALUE_INT8:
|
|
|
|
|
return L"I8";
|
|
|
|
|
case DEBUG_VALUE_INT16:
|
|
|
|
|
return L"I16";
|
|
|
|
|
case DEBUG_VALUE_INT32:
|
|
|
|
|
return L"I32";
|
|
|
|
|
case DEBUG_VALUE_INT64:
|
|
|
|
|
return L"I64";
|
|
|
|
|
case DEBUG_VALUE_FLOAT32:
|
|
|
|
|
return L"F32";
|
|
|
|
|
case DEBUG_VALUE_FLOAT64:
|
|
|
|
|
return L"F64";
|
|
|
|
|
case DEBUG_VALUE_FLOAT80:
|
|
|
|
|
return L"F80";
|
|
|
|
|
case DEBUG_VALUE_FLOAT128:
|
|
|
|
|
return L"F128";
|
|
|
|
|
case DEBUG_VALUE_VECTOR64:
|
|
|
|
|
return L"V64";
|
|
|
|
|
case DEBUG_VALUE_VECTOR128:
|
|
|
|
|
return L"V128";
|
|
|
|
|
}
|
|
|
|
|
return L"";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Format a 128bit vector register by adding digits in reverse order
|
|
|
|
|
void formatVectorRegister(std::ostream &str, const unsigned char *array, int size)
|
|
|
|
|
{
|
|
|
|
|
const char oldFill = str.fill('0');
|
|
|
|
|
|
|
|
|
|
str << "0x" << std::hex;
|
|
|
|
|
for (int i = size - 1; i >= 0; i--) {
|
|
|
|
|
str.width(2);
|
|
|
|
|
str << unsigned(array[i]);
|
|
|
|
|
}
|
|
|
|
|
str << std::dec;
|
|
|
|
|
|
|
|
|
|
str.fill(oldFill);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void formatDebugValue(std::ostream &str, const DEBUG_VALUE &dv, CIDebugControl *ctl)
|
|
|
|
|
{
|
|
|
|
|
const std::ios::fmtflags savedState = str.flags();
|
|
|
|
|
switch (dv.Type) {
|
|
|
|
|
// Do not use showbase to get the hex prefix as this omits it for '0'. Grmpf.
|
|
|
|
|
case DEBUG_VALUE_INT8:
|
|
|
|
|
str << std::hex << "0x" << unsigned(dv.I8);
|
|
|
|
|
break;
|
|
|
|
|
case DEBUG_VALUE_INT16:
|
|
|
|
|
str << std::hex << "0x" << dv.I16;
|
|
|
|
|
break;
|
|
|
|
|
case DEBUG_VALUE_INT32:
|
|
|
|
|
str << std::hex << "0x" << dv.I32;
|
|
|
|
|
break;
|
|
|
|
|
case DEBUG_VALUE_INT64:
|
|
|
|
|
str << std::hex << "0x" << dv.I64;
|
|
|
|
|
break;
|
|
|
|
|
case DEBUG_VALUE_FLOAT32:
|
|
|
|
|
str << dv.F32;
|
|
|
|
|
break;
|
|
|
|
|
case DEBUG_VALUE_FLOAT64:
|
|
|
|
|
str << dv.F64;
|
|
|
|
|
break;
|
|
|
|
|
case DEBUG_VALUE_FLOAT80:
|
|
|
|
|
case DEBUG_VALUE_FLOAT128: { // Convert to double
|
|
|
|
|
DEBUG_VALUE doubleValue;
|
|
|
|
|
if (ctl && SUCCEEDED(ctl->CoerceValue(const_cast<DEBUG_VALUE*>(&dv), DEBUG_VALUE_FLOAT64, &doubleValue)))
|
|
|
|
|
str << dv.F64;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case DEBUG_VALUE_VECTOR64:
|
|
|
|
|
formatVectorRegister(str, dv.VI8, 8);
|
|
|
|
|
break;
|
|
|
|
|
case DEBUG_VALUE_VECTOR128:
|
|
|
|
|
formatVectorRegister(str, dv.VI8, 16);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
str.flags(savedState);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Register::Register() : subRegister(false), pseudoRegister(false)
|
|
|
|
|
{
|
|
|
|
|
value.Type = DEBUG_VALUE_INT32;
|
|
|
|
|
value.I32 = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline std::wstring registerDescription(const DEBUG_REGISTER_DESCRIPTION &d)
|
|
|
|
|
{
|
|
|
|
|
std::wostringstream str;
|
|
|
|
|
str << valueType(d.Type);
|
|
|
|
|
if (d.Flags & DEBUG_REGISTER_SUB_REGISTER)
|
|
|
|
|
str << ", sub-register of " << d.SubregMaster;
|
|
|
|
|
return str.str();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Registers getRegisters(CIDebugRegisters *regs,
|
|
|
|
|
unsigned flags,
|
|
|
|
|
std::string *errorMessage)
|
|
|
|
|
{
|
|
|
|
|
enum { bufSize= 128 };
|
|
|
|
|
WCHAR buf[bufSize];
|
|
|
|
|
|
|
|
|
|
ULONG registerCount = 0;
|
|
|
|
|
HRESULT hr = regs->GetNumberRegisters(®isterCount);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
*errorMessage = msgDebugEngineComFailed("GetNumberRegisters", hr);
|
|
|
|
|
return Registers();
|
|
|
|
|
}
|
|
|
|
|
ULONG pseudoRegisterCount = 0;
|
|
|
|
|
if (flags & IncludePseudoRegisters) {
|
|
|
|
|
hr = regs->GetNumberPseudoRegisters(&pseudoRegisterCount);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
*errorMessage = msgDebugEngineComFailed("GetNumberPseudoRegisters", hr);
|
|
|
|
|
return Registers();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Registers rc;
|
|
|
|
|
rc.reserve(registerCount + pseudoRegisterCount);
|
|
|
|
|
|
|
|
|
|
// Standard registers
|
|
|
|
|
DEBUG_REGISTER_DESCRIPTION description;
|
|
|
|
|
DEBUG_VALUE value;
|
|
|
|
|
for (ULONG r = 0; r < registerCount; r++) {
|
|
|
|
|
hr = regs->GetDescriptionWide(r, buf, bufSize, NULL, &description);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
*errorMessage = msgDebugEngineComFailed("GetDescription", hr);
|
|
|
|
|
return Registers();
|
|
|
|
|
}
|
|
|
|
|
hr = regs->GetValue(r, &value);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
*errorMessage = msgDebugEngineComFailed("GetValue", hr);
|
|
|
|
|
return Registers();
|
|
|
|
|
}
|
|
|
|
|
const bool isSubRegister = (description.Flags & DEBUG_REGISTER_SUB_REGISTER);
|
|
|
|
|
if (!isSubRegister || (flags & IncludeSubRegisters)) {
|
|
|
|
|
Register reg;
|
|
|
|
|
reg.name = buf;
|
|
|
|
|
reg.description = registerDescription(description);
|
|
|
|
|
reg.subRegister = isSubRegister;
|
|
|
|
|
reg.value = value;
|
|
|
|
|
rc.push_back(reg);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Pseudo
|
|
|
|
|
for (ULONG r = 0; r < pseudoRegisterCount; r++) {
|
|
|
|
|
ULONG type;
|
|
|
|
|
hr = regs->GetPseudoDescriptionWide(r, buf, bufSize, NULL, NULL, &type);
|
|
|
|
|
if (FAILED(hr))
|
|
|
|
|
continue; // Fails for some pseudo registers
|
|
|
|
|
hr = regs->GetValue(r, &value);
|
|
|
|
|
if (FAILED(hr))
|
|
|
|
|
continue; // Fails for some pseudo registers
|
|
|
|
|
Register reg;
|
|
|
|
|
reg.pseudoRegister = true;
|
|
|
|
|
reg.name = buf;
|
|
|
|
|
reg.description = valueType(type);
|
|
|
|
|
reg.value = value;
|
|
|
|
|
rc.push_back(reg);
|
|
|
|
|
}
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string gdbmiRegisters(CIDebugRegisters *regs,
|
|
|
|
|
CIDebugControl *control,
|
|
|
|
|
bool humanReadable,
|
|
|
|
|
unsigned flags,
|
|
|
|
|
std::string *errorMessage)
|
|
|
|
|
{
|
|
|
|
|
if (regs == 0 || control == 0) {
|
|
|
|
|
*errorMessage = "Required interfaces missing for registers dump.";
|
|
|
|
|
return std::string();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const Registers registers = getRegisters(regs, flags, errorMessage);
|
|
|
|
|
if (registers.empty())
|
|
|
|
|
return std::string();
|
|
|
|
|
|
|
|
|
|
std::ostringstream str;
|
|
|
|
|
str << '[';
|
|
|
|
|
if (humanReadable)
|
|
|
|
|
str << '\n';
|
|
|
|
|
const Registers::size_type size = registers.size();
|
|
|
|
|
for (Registers::size_type r = 0; r < size; r++) {
|
|
|
|
|
const Register ® = registers.at(r);
|
|
|
|
|
if (r)
|
|
|
|
|
str << ',';
|
|
|
|
|
str << "{number=\"" << r << "\",name=\"" << gdbmiWStringFormat(reg.name) << '"';
|
|
|
|
|
if (!reg.description.empty())
|
|
|
|
|
str << ",description=\"" << gdbmiWStringFormat(reg.description) << '"';
|
|
|
|
|
if (reg.subRegister)
|
|
|
|
|
str << ",subregister=\"true\"";
|
|
|
|
|
if (reg.pseudoRegister)
|
|
|
|
|
str << ",pseudoregister=\"true\"";
|
|
|
|
|
str << ",value=\"";
|
|
|
|
|
formatDebugValue(str, reg.value, control);
|
|
|
|
|
str << "\"}";
|
|
|
|
|
if (humanReadable)
|
|
|
|
|
str << '\n';
|
|
|
|
|
}
|
|
|
|
|
str << ']';
|
|
|
|
|
if (humanReadable)
|
|
|
|
|
str << '\n';
|
|
|
|
|
return str.str();
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-24 10:36:21 +01:00
|
|
|
std::string memoryToBase64(CIDebugDataSpaces *ds, ULONG64 address, ULONG length,
|
|
|
|
|
std::string *errorMessage /* = 0 */)
|
|
|
|
|
{
|
2011-04-01 17:30:17 +02:00
|
|
|
if (const unsigned char *buffer = SymbolGroupValue::readMemory(ds, address, length, errorMessage)) {
|
2011-03-24 10:36:21 +01:00
|
|
|
std::ostringstream str;
|
|
|
|
|
base64Encode(str, buffer, length);
|
|
|
|
|
delete [] buffer;
|
|
|
|
|
return str.str();
|
|
|
|
|
}
|
|
|
|
|
return std::string();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::wstring memoryToHexW(CIDebugDataSpaces *ds, ULONG64 address, ULONG length,
|
|
|
|
|
std::string *errorMessage /* = 0 */)
|
|
|
|
|
{
|
2011-04-01 17:30:17 +02:00
|
|
|
if (const unsigned char *buffer = SymbolGroupValue::readMemory(ds, address, length, errorMessage)) {
|
2011-03-24 10:36:21 +01:00
|
|
|
const std::wstring hex = dataToHexW(buffer, buffer + length);
|
|
|
|
|
delete [] buffer;
|
|
|
|
|
return hex;
|
|
|
|
|
}
|
|
|
|
|
return std::wstring();
|
2010-11-18 13:55:52 +01:00
|
|
|
}
|
2010-11-22 13:50:40 +01:00
|
|
|
|
|
|
|
|
// Format stack as GDBMI
|
|
|
|
|
static StackFrames getStackTrace(CIDebugControl *debugControl,
|
|
|
|
|
CIDebugSymbols *debugSymbols,
|
|
|
|
|
unsigned maxFrames,
|
|
|
|
|
std::string *errorMessage)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
if (!maxFrames)
|
|
|
|
|
return StackFrames();
|
|
|
|
|
DEBUG_STACK_FRAME *frames = new DEBUG_STACK_FRAME[maxFrames];
|
|
|
|
|
ULONG frameCount = 0;
|
|
|
|
|
const HRESULT hr = debugControl->GetStackTrace(0, 0, 0, frames, maxFrames, &frameCount);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
delete [] frames;
|
|
|
|
|
*errorMessage = msgDebugEngineComFailed("GetStackTrace", hr);
|
|
|
|
|
}
|
|
|
|
|
StackFrames rc(frameCount, StackFrame());
|
|
|
|
|
for (ULONG f = 0; f < frameCount; f++)
|
|
|
|
|
getFrame(debugSymbols, frames[f], &(rc[f]));
|
|
|
|
|
delete [] frames;
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string gdbmiStack(CIDebugControl *debugControl,
|
|
|
|
|
CIDebugSymbols *debugSymbols,
|
|
|
|
|
unsigned maxFrames,
|
|
|
|
|
bool humanReadable, std::string *errorMessage)
|
|
|
|
|
{
|
|
|
|
|
const StackFrames frames = getStackTrace(debugControl, debugSymbols,
|
|
|
|
|
maxFrames, errorMessage);
|
|
|
|
|
if (frames.empty() && maxFrames > 0)
|
|
|
|
|
return std::string();
|
|
|
|
|
|
|
|
|
|
std::ostringstream str;
|
|
|
|
|
str << '[';
|
|
|
|
|
const StackFrames::size_type size = frames.size();
|
|
|
|
|
for (StackFrames::size_type i = 0; i < size; i++) {
|
|
|
|
|
if (i)
|
|
|
|
|
str << ',';
|
|
|
|
|
frames.at(i).formatGDBMI(str, (int)i);
|
|
|
|
|
if (humanReadable)
|
|
|
|
|
str << '\n';
|
|
|
|
|
}
|
|
|
|
|
str << ']';
|
|
|
|
|
return str.str();
|
|
|
|
|
}
|
2011-01-18 11:40:45 +01:00
|
|
|
|
|
|
|
|
// Find the widget of the application by calling QApplication::widgetAt().
|
|
|
|
|
// Return "Qualified_ClassName:Address"
|
|
|
|
|
|
|
|
|
|
static inline std::string msgWidgetParseError(std::wstring wo)
|
|
|
|
|
{
|
|
|
|
|
replace(wo, L'\n', L';');
|
|
|
|
|
return "Output parse error :" + wStringToString(wo);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string widgetAt(const SymbolGroupValueContext &ctx, int x, int y, std::string *errorMessage)
|
|
|
|
|
{
|
|
|
|
|
typedef SymbolGroupValue::SymbolList SymbolList;
|
|
|
|
|
// First, resolve symbol since there are ambiguities. Take the first one which is the
|
|
|
|
|
// overload for (int,int) and call by address instead off name to overcome that.
|
|
|
|
|
const std::string func = QtInfo::get(ctx).prependQtGuiModule("QApplication::widgetAt");
|
|
|
|
|
const SymbolList symbols = SymbolGroupValue::resolveSymbol(func.c_str(), ctx, errorMessage);
|
|
|
|
|
if (symbols.empty())
|
|
|
|
|
return std::string(); // Not a gui application, likely
|
|
|
|
|
std::ostringstream callStr;
|
|
|
|
|
callStr << std::showbase << std::hex << symbols.front().second
|
|
|
|
|
<< std::noshowbase << std::dec << '(' << x << ',' << y << ')';
|
|
|
|
|
std::wstring wOutput;
|
|
|
|
|
if (!ExtensionContext::instance().call(callStr.str(), &wOutput, errorMessage))
|
|
|
|
|
return std::string();
|
|
|
|
|
// Returns: ".call returns\nclass QWidget * 0x00000000`022bf100\nbla...".
|
|
|
|
|
// Chop lines in front and after 'class ...' and convert first line.
|
|
|
|
|
const std::wstring::size_type classPos = wOutput.find(L"class ");
|
|
|
|
|
if (classPos == std::wstring::npos) {
|
|
|
|
|
*errorMessage = msgWidgetParseError(wOutput);
|
|
|
|
|
return std::string();
|
|
|
|
|
}
|
|
|
|
|
wOutput.erase(0, classPos + 6);
|
|
|
|
|
const std::wstring::size_type nlPos = wOutput.find(L'\n');
|
|
|
|
|
if (nlPos != std::wstring::npos)
|
|
|
|
|
wOutput.erase(nlPos, wOutput.size() - nlPos);
|
|
|
|
|
const std::string::size_type addressPos = wOutput.find(L" * 0x");
|
|
|
|
|
if (addressPos == std::string::npos) {
|
|
|
|
|
*errorMessage = msgWidgetParseError(wOutput);
|
|
|
|
|
return std::string();
|
|
|
|
|
}
|
|
|
|
|
// "QWidget * 0x00000000`022bf100" -> "QWidget:0x00000000022bf100"
|
|
|
|
|
wOutput.replace(addressPos, 3, L":");
|
|
|
|
|
const std::string::size_type sepPos = wOutput.find(L'`');
|
|
|
|
|
if (sepPos != std::string::npos)
|
|
|
|
|
wOutput.erase(sepPos, 1);
|
|
|
|
|
return wStringToString(wOutput);
|
|
|
|
|
}
|
2011-02-02 13:45:40 +01:00
|
|
|
|
|
|
|
|
static inline void formatGdbmiFlag(std::ostream &str, const char *name, bool v)
|
|
|
|
|
{
|
|
|
|
|
str << name << "=\"" << (v ? "true" : "false") << '"';
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-24 10:36:21 +01:00
|
|
|
std::pair<ULONG64, ULONG> breakPointMemoryRange(IDebugBreakpoint *bp)
|
|
|
|
|
{
|
|
|
|
|
// Get address. Can fail for deferred breakpoints.
|
|
|
|
|
std::pair<ULONG64, ULONG> result = std::pair<ULONG64, ULONG>(0, 0);
|
|
|
|
|
if (FAILED(bp->GetOffset(&result.first)) || result.first == 0)
|
|
|
|
|
return result;
|
|
|
|
|
// Fill 'size' only for data breakpoints
|
|
|
|
|
ULONG breakType = DEBUG_BREAKPOINT_CODE;
|
|
|
|
|
ULONG cpuType = 0;
|
|
|
|
|
if (FAILED(bp->GetType(&breakType, &cpuType)) || breakType != DEBUG_BREAKPOINT_DATA)
|
|
|
|
|
return result;
|
|
|
|
|
ULONG accessType = 0;
|
|
|
|
|
bp->GetDataParameters(&result.second, &accessType);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-02 13:45:40 +01:00
|
|
|
static bool gdbmiFormatBreakpoint(std::ostream &str,
|
|
|
|
|
IDebugBreakpoint *bp,
|
|
|
|
|
CIDebugSymbols *symbols /* = 0 */,
|
2011-03-24 10:36:21 +01:00
|
|
|
CIDebugDataSpaces *dataSpaces /* = 0 */,
|
2011-02-03 16:26:23 +01:00
|
|
|
unsigned verbose, std::string *errorMessage)
|
2011-02-02 13:45:40 +01:00
|
|
|
{
|
|
|
|
|
enum { BufSize = 512 };
|
|
|
|
|
ULONG flags = 0;
|
2011-02-03 16:26:23 +01:00
|
|
|
ULONG id = 0;
|
|
|
|
|
if (SUCCEEDED(bp->GetId(&id)))
|
|
|
|
|
str << ",id=\"" << id << '"';
|
2011-02-02 13:45:40 +01:00
|
|
|
HRESULT hr = bp->GetFlags(&flags);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
*errorMessage = msgDebugEngineComFailed("GetFlags", hr);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
const bool deferred = (flags & DEBUG_BREAKPOINT_DEFERRED) != 0;
|
|
|
|
|
formatGdbmiFlag(str, ",deferred", deferred);
|
2011-02-03 16:26:23 +01:00
|
|
|
formatGdbmiFlag(str, ",enabled", (flags & DEBUG_BREAKPOINT_ENABLED) != 0);
|
2011-02-02 13:45:40 +01:00
|
|
|
if (verbose) {
|
|
|
|
|
formatGdbmiFlag(str, ",oneshot", (flags & DEBUG_BREAKPOINT_ONE_SHOT) != 0);
|
|
|
|
|
str << ",flags=\"" << flags << '"';
|
|
|
|
|
ULONG threadId = 0;
|
|
|
|
|
if (SUCCEEDED(bp->GetMatchThreadId(&threadId))) // Fails if none set
|
|
|
|
|
str << ",thread=\"" << threadId << '"';
|
|
|
|
|
ULONG passCount = 0;
|
|
|
|
|
if (SUCCEEDED(bp->GetPassCount(&passCount)))
|
|
|
|
|
str << ",passcount=\"" << passCount << '"';
|
|
|
|
|
}
|
|
|
|
|
// Offset: Fails for deferred ones
|
2011-03-24 10:36:21 +01:00
|
|
|
if (!deferred) {
|
|
|
|
|
const std::pair<ULONG64, ULONG> memoryRange = breakPointMemoryRange(bp);
|
|
|
|
|
if (memoryRange.first) {
|
|
|
|
|
str << ",address=\"" << std::hex << std::showbase << memoryRange.first
|
|
|
|
|
<< std::dec << std::noshowbase << '"';
|
|
|
|
|
// Resolve module to be specified in next run for speed-up.
|
|
|
|
|
if (symbols) {
|
|
|
|
|
const std::string module = moduleNameByOffset(symbols, memoryRange.first);
|
|
|
|
|
if (!module.empty())
|
|
|
|
|
str << ",module=\"" << module << '"';
|
|
|
|
|
} // symbols
|
|
|
|
|
// Report the memory of watchpoints for comparing bitfields
|
|
|
|
|
if (dataSpaces && memoryRange.second > 0) {
|
|
|
|
|
str << ",size=\"" << memoryRange.second << '"';
|
|
|
|
|
const std::wstring memoryHex = memoryToHexW(dataSpaces, memoryRange.first, memoryRange.second);
|
|
|
|
|
if (!memoryHex.empty())
|
|
|
|
|
str << ",memory=\"" << gdbmiWStringFormat(memoryHex) << '"';
|
|
|
|
|
}
|
|
|
|
|
} // Got address
|
|
|
|
|
} // !deferred
|
2011-02-02 13:45:40 +01:00
|
|
|
// Expression
|
2011-02-03 16:26:23 +01:00
|
|
|
if (verbose > 1) {
|
|
|
|
|
char buf[BufSize];
|
|
|
|
|
if (SUCCEEDED(bp->GetOffsetExpression(buf, BUFSIZ, 0)))
|
|
|
|
|
str << ",expression=\"" << gdbmiStringFormat(buf) << '"';
|
|
|
|
|
}
|
2011-02-02 13:45:40 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Format breakpoints as GDBMI
|
|
|
|
|
std::string gdbmiBreakpoints(CIDebugControl *ctrl,
|
|
|
|
|
CIDebugSymbols *symbols /* = 0 */,
|
2011-03-24 10:36:21 +01:00
|
|
|
CIDebugDataSpaces *dataSpaces /* = 0 */,
|
2011-02-03 16:26:23 +01:00
|
|
|
bool humanReadable, unsigned verbose, std::string *errorMessage)
|
2011-02-02 13:45:40 +01:00
|
|
|
{
|
|
|
|
|
ULONG breakPointCount = 0;
|
|
|
|
|
HRESULT hr = ctrl->GetNumberBreakpoints(&breakPointCount);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
*errorMessage = msgDebugEngineComFailed("GetNumberBreakpoints", hr);
|
|
|
|
|
return std::string();
|
|
|
|
|
}
|
|
|
|
|
std::ostringstream str;
|
|
|
|
|
str << '[';
|
|
|
|
|
if (humanReadable)
|
|
|
|
|
str << '\n';
|
|
|
|
|
for (ULONG i = 0; i < breakPointCount; i++) {
|
2011-02-03 16:26:23 +01:00
|
|
|
str << "{number=\"" << i << '"';
|
2011-02-02 13:45:40 +01:00
|
|
|
IDebugBreakpoint *bp = 0;
|
|
|
|
|
hr = ctrl->GetBreakpointByIndex(i, &bp);
|
|
|
|
|
if (FAILED(hr) || !bp) {
|
|
|
|
|
*errorMessage = msgDebugEngineComFailed("GetBreakpointByIndex", hr);
|
|
|
|
|
return std::string();
|
|
|
|
|
}
|
2011-03-24 10:36:21 +01:00
|
|
|
if (!gdbmiFormatBreakpoint(str, bp, symbols, dataSpaces, verbose, errorMessage))
|
2011-02-02 13:45:40 +01:00
|
|
|
return std::string();
|
|
|
|
|
str << '}';
|
|
|
|
|
if (humanReadable)
|
|
|
|
|
str << '\n';
|
|
|
|
|
}
|
|
|
|
|
str << ']';
|
|
|
|
|
return str.str();
|
|
|
|
|
}
|
2011-03-23 15:08:57 +01:00
|
|
|
|
|
|
|
|
std::string msgEvaluateExpressionFailed(const std::string &expression,
|
|
|
|
|
const std::string &why)
|
|
|
|
|
{
|
|
|
|
|
std::ostringstream str;
|
|
|
|
|
str << "Failed to evaluate expression '" << expression << "': " << why;
|
|
|
|
|
return str.str();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool evaluateExpression(CIDebugControl *control, const std::string expression,
|
|
|
|
|
ULONG desiredType, DEBUG_VALUE *v, std::string *errorMessage)
|
|
|
|
|
{
|
|
|
|
|
// Ensure we are in C++
|
|
|
|
|
ULONG oldSyntax;
|
|
|
|
|
HRESULT hr = control->GetExpressionSyntax(&oldSyntax);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
*errorMessage = msgEvaluateExpressionFailed(expression, msgDebugEngineComFailed("GetExpressionSyntax", hr));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (oldSyntax != DEBUG_EXPR_CPLUSPLUS) {
|
|
|
|
|
HRESULT hr = control->SetExpressionSyntax(DEBUG_EXPR_CPLUSPLUS);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
*errorMessage = msgEvaluateExpressionFailed(expression, msgDebugEngineComFailed("SetExpressionSyntax", hr));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
hr = control->Evaluate(expression.c_str(), desiredType, v, NULL);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
*errorMessage = msgEvaluateExpressionFailed(expression, msgDebugEngineComFailed("Evaluate", hr));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (oldSyntax != DEBUG_EXPR_CPLUSPLUS) {
|
|
|
|
|
HRESULT hr = control->SetExpressionSyntax(oldSyntax);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
*errorMessage = msgEvaluateExpressionFailed(expression, msgDebugEngineComFailed("SetExpressionSyntax", hr));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool evaluateInt64Expression(CIDebugControl *control, const std::string expression,
|
|
|
|
|
LONG64 *v, std::string *errorMessage)
|
|
|
|
|
{
|
|
|
|
|
*v= 0;
|
|
|
|
|
DEBUG_VALUE dv;
|
|
|
|
|
if (!evaluateExpression(control, expression, DEBUG_VALUE_INT64, &dv, errorMessage))
|
|
|
|
|
return false;
|
|
|
|
|
*v = dv.I64;
|
|
|
|
|
return true;
|
|
|
|
|
}
|