diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbext.def b/src/libs/qtcreatorcdbext/qtcreatorcdbext.def index 0187da13ebb96b69cdc415138b5f5eb3adc89115..2f84541610a3a67142421dedf1ad11c66d18eb80 100644 --- a/src/libs/qtcreatorcdbext/qtcreatorcdbext.def +++ b/src/libs/qtcreatorcdbext/qtcreatorcdbext.def @@ -5,6 +5,7 @@ DebugExtensionNotify pid expandlocals locals +dumplocal assign threads registers diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbext_build.pro b/src/libs/qtcreatorcdbext/qtcreatorcdbext_build.pro index 60041522e58bf2a8518cc5075fc32597f3035e6e..1ddfc45a9bed9d2c8cd2312268a5a3e40fc156c1 100644 --- a/src/libs/qtcreatorcdbext/qtcreatorcdbext_build.pro +++ b/src/libs/qtcreatorcdbext/qtcreatorcdbext_build.pro @@ -53,7 +53,8 @@ SOURCES += qtcreatorcdbextension.cpp \ stringutils.cpp \ gdbmihelpers.cpp \ outputcallback.cpp \ - base64.cpp + base64.cpp \ + symbolgroupvalue.cpp HEADERS += extensioncontext.h \ common.h \ @@ -63,5 +64,5 @@ HEADERS += extensioncontext.h \ stringutils.h \ gdbmihelpers.h \ outputcallback.h \ - base64.h - + base64.h \ + symbolgroupvalue.h diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp index 700c1c9b2e6021666114fc8187ed3118baacfaab..53bef0ab7d909caa44b2176ded655bc5a5c846cd 100644 --- a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp +++ b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp @@ -31,6 +31,7 @@ #include "outputcallback.h" #include "eventcallback.h" #include "symbolgroup.h" +#include "symbolgroupvalue.h" #include "stringutils.h" #include "gdbmihelpers.h" @@ -224,6 +225,59 @@ extern "C" HRESULT CALLBACK locals(CIDebugClient *client, PCSTR args) return S_OK; } +// Extension command 'dumplocal': +// Dump a local variable using dumpers (testing command). + +const char dumpLocalUsageC[] = "Usage: dumplocal <frame> <iname>"; + +static std::string dumplocalHelper(ExtensionCommandContext &exc,PCSTR args, int *token, std::string *errorMessage) +{ + // Parse the command + StringList tokens = commandTokens<StringList>(args, token); + // Frame and iname + unsigned frame; + if (tokens.empty() || sscanf(tokens.front().c_str(), "%u", &frame) != 1) { + *errorMessage = dumpLocalUsageC; + return std::string(); + } + tokens.pop_front(); + if (tokens.empty()) { + *errorMessage = dumpLocalUsageC; + return std::string(); + } + const std::string iname = tokens.front(); + + SymbolGroup * const symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), frame, errorMessage); + if (!symGroup) + return std::string(); + + SymbolGroupNode *n = symGroup->find(iname); + if (!n) { + *errorMessage = "No such iname " + iname; + return std::string(); + } + std::wstring value; + if (!dumpSimpleType(n, SymbolGroupValueContext(exc.dataSpaces()), &value)) { + *errorMessage = "Cannot dump " + iname; + return std::string(); + } + return wStringToString(value); +} + +extern "C" HRESULT CALLBACK dumplocal(CIDebugClient *client, PCSTR argsIn) +{ + ExtensionCommandContext exc(client); + std::string errorMessage; + int token = 0; + const std::string value = dumplocalHelper(exc,argsIn, &token, &errorMessage); + if (value.empty()) { + ExtensionContext::instance().report('N', token, "dumplocal", errorMessage.c_str()); + } else { + ExtensionContext::instance().report('R', token, "dumplocal", value.c_str()); + } + return S_OK; +} + // Extension command 'assign': // Assign locals by iname: 'assign locals.x=5' diff --git a/src/libs/qtcreatorcdbext/stringutils.cpp b/src/libs/qtcreatorcdbext/stringutils.cpp index ed0c7309fda9a2a0fe935edb784800f60429d2a4..b0f476d1b30edf4145129b88b7e736847532d863 100644 --- a/src/libs/qtcreatorcdbext/stringutils.cpp +++ b/src/libs/qtcreatorcdbext/stringutils.cpp @@ -97,6 +97,15 @@ void replace(std::wstring &s, wchar_t before, wchar_t after) s[i] = after; } +bool endsWith(const std::string &haystack, const char *needle) +{ + const size_t needleLen = strlen(needle); + const size_t haystackLen = haystack.size(); + if (needleLen > haystackLen) + return false; + return haystack.compare(haystackLen - needleLen, needleLen, needle) == 0; +} + static inline void formatGdbmiChar(std::ostream &str, wchar_t c) { switch (c) { @@ -164,6 +173,18 @@ std::string wStringToString(const std::wstring &w) return rc; } +std::wstring stringToWString(const std::string &w) +{ + if (w.empty()) + return std::wstring(); + const std::wstring::size_type size = w.size(); + std::wstring rc; + rc.reserve(size); + for (std::wstring::size_type i = 0; i < size; i++) + rc.push_back(w.at(i)); + return rc; +} + // Format a map as a GDBMI hash {key="value",..} void formatGdbmiHash(std::ostream &os, const std::map<std::string, std::string> &m) { diff --git a/src/libs/qtcreatorcdbext/stringutils.h b/src/libs/qtcreatorcdbext/stringutils.h index cb809aeb918dc8b0b8e588aad05aa5d691edef09..e8a7342a2490f70f419a1882f528f60053ce6257 100644 --- a/src/libs/qtcreatorcdbext/stringutils.h +++ b/src/libs/qtcreatorcdbext/stringutils.h @@ -63,6 +63,8 @@ std::string toString(const Streamable s) return str.str(); } +bool endsWith(const std::string &haystack, const char *needle); + // Read an integer from a string as '10' or '0xA' template <class Integer> bool integerFromString(const std::string &s, Integer *v) @@ -115,6 +117,7 @@ inline std::ostream &operator<<(std::ostream &str, const gdbmiWStringFormat &wsf std::string wStringToGdbmiString(const std::wstring &w); std::string wStringToString(const std::wstring &w); +std::wstring stringToWString(const std::string &w); // Format a map as a GDBMI hash {key="value",..} void formatGdbmiHash(std::ostream &os, const std::map<std::string, std::string> &); diff --git a/src/libs/qtcreatorcdbext/symbolgroup.cpp b/src/libs/qtcreatorcdbext/symbolgroup.cpp index 6c2059787cab31b6603e2714d21f38087c90429b..5c2a1e69b6a6f407a6f19737e9d03009913cfc0a 100644 --- a/src/libs/qtcreatorcdbext/symbolgroup.cpp +++ b/src/libs/qtcreatorcdbext/symbolgroup.cpp @@ -457,7 +457,7 @@ static bool isSevenBitClean(const wchar_t *buf, ULONG size) return true; } -std::string SymbolGroupNode::getType() const +std::string SymbolGroupNode::type() const { static char buf[BufSize]; const HRESULT hr = m_symbolGroup->debugSymbolGroup()->GetSymbolTypeName(m_index, buf, BufSize, NULL); @@ -509,7 +509,7 @@ std::wstring SymbolGroupNode::rawValue() const std::wstring SymbolGroupNode::fixedValue() const { std::wstring value = rawValue(); - fixValue(getType(), &value); + fixValue(type(), &value); return value; } @@ -518,6 +518,15 @@ SymbolGroupNode *SymbolGroupNode::childAt(unsigned i) const return i < m_children.size() ? m_children.at(i) : static_cast<SymbolGroupNode *>(0); } +SymbolGroupNode *SymbolGroupNode::childByIName(const char *n) const +{ + const SymbolGroupNodePtrVector::const_iterator childrenEnd = m_children.end(); + for (SymbolGroupNodePtrVector::const_iterator it = m_children.begin(); it != childrenEnd; ++it) + if ( (*it)->iName() == n ) + return *it; + return 0; +} + static inline void indentStream(std::ostream &str, unsigned depth) { for (unsigned d = 0; d < depth; d++) @@ -528,7 +537,7 @@ void SymbolGroupNode::dump(std::ostream &str, unsigned child, unsigned depth, bool humanReadable) const { const std::string iname = fullIName(); - const std::string type = getType(); + const std::string t = type(); if (child) { // Separate list of children str << ','; @@ -539,7 +548,7 @@ void SymbolGroupNode::dump(std::ostream &str, unsigned child, unsigned depth, if (humanReadable) indentStream(str, depth); str << "{iname=\"" << iname << "\",exp=\"" << iname << "\",name=\"" << m_name - << "\",type=\"" << type << '"'; + << "\",type=\"" << t << '"'; if (const ULONG64 addr = address()) str << ",addr=\"" << std::hex << std::showbase << addr << std::noshowbase << std::dec @@ -559,7 +568,7 @@ void SymbolGroupNode::dump(std::ostream &str, unsigned child, unsigned depth, // ASCII or base64? if (isSevenBitClean(wbuf, valueSize)) { std::wstring value = wbuf; - fixValue(type, &value); + fixValue(t, &value); str << ",valueencoded=\"0\",value=\"" << gdbmiWStringFormat(value) << '"'; } else { str << ",valueencoded=\"2\",value=\""; @@ -625,7 +634,7 @@ void SymbolGroupNode::debug(std::ostream &str, unsigned verbosity, unsigned dept str << " node-flags=" << m_flags; if (verbosity) { str << ",name=\"" << m_name << "\", Address=0x" << std::hex << address() << std::dec - << " Type=\"" << getType() << '"'; + << " Type=\"" << type() << '"'; if (!(m_flags & Uninitialized)) str << "\" Value=\"" << gdbmiWStringFormat(rawValue()) << '"'; } diff --git a/src/libs/qtcreatorcdbext/symbolgroup.h b/src/libs/qtcreatorcdbext/symbolgroup.h index b5556d891b0db56ed54fde7351d17cb7df2162a6..59d8c47c6347005b5f45f0440125f7245680df67 100644 --- a/src/libs/qtcreatorcdbext/symbolgroup.h +++ b/src/libs/qtcreatorcdbext/symbolgroup.h @@ -75,6 +75,8 @@ public: const SymbolGroupNodePtrVector &children() const { return m_children; } SymbolGroupNode *childAt(unsigned) const; + SymbolGroupNode *childByIName(const char *) const; + const SymbolGroupNode *parent() const { return m_parent; } // I/O: Gdbmi dump for Visitors @@ -86,6 +88,7 @@ public: std::wstring rawValue() const; std::wstring fixedValue() const; + std::string type() const; ULONG64 address() const; bool accept(SymbolGroupNodeVisitor &visitor, unsigned child, unsigned depth) const; @@ -101,7 +104,6 @@ public: private: // Return allocated wide string array of value wchar_t *getValue(ULONG *obtainedSize = 0) const; - std::string getType() const; bool isArrayElement() const; // Notify about expansion of a node, shift indexes bool notifyExpanded(ULONG index, ULONG insertedCount); @@ -174,6 +176,7 @@ public: ULONG threadId() const { return m_threadId; } SymbolGroupNode *root() { return m_root; } const SymbolGroupNode *root() const { return m_root; } + SymbolGroupNode *find(const std::string &iname) const; // Expand a node list "locals.i1,locals.i2", expanding all nested child nodes // (think mkdir -p). @@ -201,7 +204,6 @@ public: std::string *errorMessage); private: - SymbolGroupNode *find(const std::string &iname) const; inline SymbolGroupNode *findI(const std::string &iname) const; static bool getSymbolParameters(CIDebugSymbolGroup *m_symbolGroup, SymbolParameterVector *vec, diff --git a/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp b/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9e279e5dd2eda63aa39ed2fec91a21b3215831fd --- /dev/null +++ b/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp @@ -0,0 +1,161 @@ +/************************************************************************** +** +** 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 "symbolgroupvalue.h" +#include "symbolgroup.h" +#include "stringutils.h" + +SymbolGroupValueContext::SymbolGroupValueContext(CIDebugDataSpaces *ds) + : dataspaces(ds) +{ +} + +SymbolGroupValueContext::SymbolGroupValueContext() : dataspaces(0) +{ +} + +SymbolGroupValue::SymbolGroupValue(SymbolGroupNode *node, + const SymbolGroupValueContext &ctx) : + m_node(node), m_context(ctx) +{ +} + +SymbolGroupValue::SymbolGroupValue() : + m_node(0), m_errorMessage("Invalid") +{ +} + +bool SymbolGroupValue::isValid() const +{ + return m_node != 0 && m_context.dataspaces != 0; +} + +SymbolGroupValue SymbolGroupValue::operator[](const char *name) const +{ + if (isValid() && m_node->expand(&m_errorMessage)) + if (SymbolGroupNode *child = m_node->childByIName(name)) + return SymbolGroupValue(child, m_context); + return SymbolGroupValue(); +} + +std::string SymbolGroupValue::type() const +{ + return isValid() ? m_node->type() : std::string(); +} + +std::wstring SymbolGroupValue::value() const +{ + return isValid() ? m_node->fixedValue() : std::wstring(); +} + +int SymbolGroupValue::intValue(int defaultValue) const +{ + if (isValid()) { + int rc = 0; + if (integerFromString(wStringToString(value()), &rc)) + return rc; + } + return defaultValue; +} + +ULONG64 SymbolGroupValue::pointerValue(ULONG64 defaultValue) const +{ + if (isValid()) { + ULONG64 rc = 0; + if (integerFromString(wStringToString(value()), &rc)) + return rc; + } + return defaultValue; +} + +// Return allocated array of data +unsigned char *SymbolGroupValue::pointerData(unsigned length) const +{ + if (isValid()) { + if (const ULONG64 ptr = pointerValue()) { + unsigned char *data = new unsigned char[length]; + std::fill(data, data + length, 0); + const HRESULT hr = m_context.dataspaces->ReadVirtual(ptr, data, length, NULL); + if (FAILED(hr)) { + delete [] data; + return 0; + } + return data; + } + } + return 0; +} + +std::wstring SymbolGroupValue::wcharPointerData(unsigned charCount, unsigned maxCharCount) const +{ + const bool truncated = charCount > maxCharCount; + if (truncated) + charCount = maxCharCount; + if (const unsigned char *ucData = pointerData(charCount * sizeof(wchar_t))) { + const wchar_t *utf16Data = reinterpret_cast<const wchar_t *>(ucData); + // Be smart about terminating 0-character + if (charCount && utf16Data[charCount - 1] == 0) + charCount--; + std::wstring rc = std::wstring(utf16Data, charCount); + if (truncated) + rc += L"..."; + delete [] ucData; + return rc; + } + return std::wstring(); +} + +std::string SymbolGroupValue::error() const +{ + return m_errorMessage; +} + +// Dump a QString. +static bool dumpQString(const SymbolGroupValue &v, std::wstring *s) +{ + if (endsWith(v.type(), "QString")) { + if (SymbolGroupValue d = v["d"]) { + if (SymbolGroupValue sizeValue = d["size"]) { + const int size = sizeValue.intValue(); + if (size >= 0) { + *s = d["data"].wcharPointerData(size); + return true; + } + } + } + } + return false; +} + +// Dump builtin simple types using SymbolGroupValue expressions. +bool dumpSimpleType(SymbolGroupNode *n, const SymbolGroupValueContext &ctx, std::wstring *s) +{ + const SymbolGroupValue v(n, ctx); + return dumpQString(v, s); +} diff --git a/src/libs/qtcreatorcdbext/symbolgroupvalue.h b/src/libs/qtcreatorcdbext/symbolgroupvalue.h new file mode 100644 index 0000000000000000000000000000000000000000..bb18ab223cc19c91cabfd0a1c117d0600e9f5af1 --- /dev/null +++ b/src/libs/qtcreatorcdbext/symbolgroupvalue.h @@ -0,0 +1,83 @@ +/************************************************************************** +** +** 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 SYMBOLGROUPVALUE_H +#define SYMBOLGROUPVALUE_H + +#include "common.h" + +#include <string> + +class SymbolGroupNode; + +// Structure to pass all IDebug interfaces used for SymbolGroupValue +struct SymbolGroupValueContext +{ + SymbolGroupValueContext(CIDebugDataSpaces *ds); + SymbolGroupValueContext(); + + CIDebugDataSpaces *dataspaces; +}; + +/* SymbolGroupValue: Flyweight tied to a SymbolGroupNode + * providing a convenient operator[] + value getters for notation of dumpers. + * Inaccesible members return a SymbolGroupValue in state 'invalid'. */ + +class SymbolGroupValue +{ +public: + explicit SymbolGroupValue(SymbolGroupNode *node, const SymbolGroupValueContext &c); + SymbolGroupValue(); + + operator bool() const { return isValid(); } + bool isValid() const; + + SymbolGroupValue operator[](const char *name) const; + + std::string type() const; + std::wstring value() const; + int intValue(int defaultValue = -1) const; + ULONG64 pointerValue(ULONG64 defaultValue = 0) const; + // Return allocated array of data pointed to + unsigned char *pointerData(unsigned length) const; + // Return data pointed to as wchar_t/std::wstring (UTF16) + std::wstring wcharPointerData(unsigned charCount, unsigned maxCharCount = 512) const; + + std::string error() const; + +private: + SymbolGroupNode *m_node; + SymbolGroupValueContext m_context; + mutable std::string m_errorMessage; +}; + +// Dump builtin simple types using SymbolGroupValue expressions. +bool dumpSimpleType(SymbolGroupNode *n, const SymbolGroupValueContext &ctx, std::wstring *s); + +#endif // SYMBOLGROUPVALUE_H