Commit 5f04706a authored by Friedemann Kleint's avatar Friedemann Kleint
Browse files

Implement basics for edit value handling in CDB.



- Add routine for formatting edit values.
- Optionally store memory in SymbolGroupNode (along
  with special info) to be able to re-use it for
  edit values using a simple convenience class
  MemoryHandle.
- Prototypically implement QString and QByteArray / Qt 5.

Task-number: QTCREATORBUG-8344

Change-Id: I6d2cac7a1e9ac48e94335142c41dc1bfb984c515
Reviewed-by: default avatarFriedemann Kleint <Friedemann.Kleint@digia.com>
parent d1d4b7c2
......@@ -275,7 +275,7 @@ extern "C" HRESULT CALLBACK pid(CIDebugClient *client, PCSTR args)
int token;
commandTokens<StringList>(args, &token);
dprintf("Qt Creator CDB extension version 2.6.1 (Qt 5 support) %d bit built %s.\n",
dprintf("Qt Creator CDB extension version 2.7 (Qt 5 support) %d bit built %s.\n",
sizeof(void *) * 8, __DATE__);
if (const ULONG pid = currentProcessId(client)) {
ExtensionContext::instance().report('R', token, 0, "pid", "%u", pid);
......
......@@ -27,6 +27,10 @@
**
****************************************************************************/
#ifndef _SCL_SECURE_NO_WARNINGS // silence std::string::copy
# define _SCL_SECURE_NO_WARNINGS
#endif
#include "stringutils.h"
#include <cctype>
......@@ -285,12 +289,29 @@ void decodeHex(const char *p, const char *end, unsigned char *target)
}
}
std::wstring dataToHexW(const unsigned char *p, const unsigned char *end)
MemoryHandle *MemoryHandle::fromStdString(const std::string &s)
{
const size_t size = s.size();
unsigned char *data = new unsigned char[size];
s.copy(reinterpret_cast<char *>(data), size);
return new MemoryHandle(data, size);
}
MemoryHandle *MemoryHandle::fromStdWString(const std::wstring &ws)
{
const size_t size = ws.size();
wchar_t *data = new wchar_t[size];
ws.copy(data, size);
return new MemoryHandle(data, size);
}
template <class String>
inline String dataToHexHelper(const unsigned char *p, const unsigned char *end)
{
if (p == end)
return std::wstring();
return String();
std::wstring rc;
String rc;
rc.reserve(2 * (end - p));
for ( ; p < end ; ++p) {
const unsigned c = *p;
......@@ -300,6 +321,16 @@ std::wstring dataToHexW(const unsigned char *p, const unsigned char *end)
return rc;
}
std::wstring dataToHexW(const unsigned char *p, const unsigned char *end)
{
return dataToHexHelper<std::wstring>(p, end);
}
std::string dataToHex(const unsigned char *p, const unsigned char *end)
{
return dataToHexHelper<std::string>(p, end);
}
// Readable hex: '0xAA 0xBB'..
std::wstring dataToReadableHexW(const unsigned char *begin, const unsigned char *end)
{
......
......@@ -188,9 +188,41 @@ std::string stringFromHex(const char *begin, const char *end);
void decodeHex(const char *begin, const char *end, unsigned char *target);
std::wstring dataToHexW(const unsigned char *begin, const unsigned char *end);
std::string dataToHex(const unsigned char *begin, const unsigned char *end);
// Create readable hex: '0xAA 0xBB'..
std::wstring dataToReadableHexW(const unsigned char *begin, const unsigned char *end);
// Flat memory handle to pass allocated memory around.
class MemoryHandle {
MemoryHandle(const MemoryHandle &);
MemoryHandle& operator=(const MemoryHandle &);
public:
explicit MemoryHandle(const unsigned char *memory, size_t size) : m_size(size), m_memory(memory) {}
explicit MemoryHandle(const wchar_t *wcharData, size_t size) :
m_size(size * sizeof(wchar_t)),
m_memory(reinterpret_cast<const unsigned char *>(wcharData)) {}
static MemoryHandle* fromStdString(const std::string &s);
static MemoryHandle* fromStdWString(const std::wstring &s);
~MemoryHandle() { delete [] m_memory; }
size_t size() const { return m_size; }
const unsigned char *data() const { return m_memory; }
const unsigned char *begin() const { return m_memory; }
const unsigned char *end() const { return m_memory + m_size; }
std::string dump(bool wantQuotes) const { return dumpMemory(begin(), m_size, wantQuotes); }
std::wstring toHexW() const { return dataToHexW(begin(), end()); }
std::string toHex() const { return dataToHex(begin(), end()); }
std::wstring toReadableHexW() const { return dataToReadableHexW(begin(), end()); }
private:
const size_t m_size;
const unsigned char *m_memory;
};
// Format a map as a GDBMI hash {key="value",..}
void formatGdbmiHash(std::ostream &os, const std::map<std::string, std::string> &, bool closeHash = true);
......
......@@ -327,7 +327,12 @@ int DumpParameters::format(const std::string &type, const std::string &iname) co
return iit->second;
}
if (!typeFormats.empty()) {
const FormatMap::const_iterator tit = typeFormats.find(type);
// Strip 'struct' / 'class' prefixes and pointer types
// when called with a raw cdb types.
std::string stripped = SymbolGroupValue::stripClassPrefixes(type);
if (stripped != type)
stripped = SymbolGroupValue::stripPointerType(stripped);
const FormatMap::const_iterator tit = typeFormats.find(stripped);
if (tit != typeFormats.end())
return tit->second;
}
......@@ -589,15 +594,25 @@ SymbolGroupNode::SymbolGroupNode(SymbolGroup *symbolGroup,
ULONG index,
const std::string &module,
const std::string &name,
const std::string &iname) :
BaseSymbolGroupNode(name, iname),
m_symbolGroup(symbolGroup),
m_module(module), m_index(index), m_dumperType(-1), m_dumperContainerSize(-1), m_dumperSpecialInfo(0)
const std::string &iname)
: BaseSymbolGroupNode(name, iname)
, m_symbolGroup(symbolGroup)
, m_module(module)
, m_index(index)
, m_dumperType(-1)
, m_dumperContainerSize(-1)
, m_dumperSpecialInfo(0)
, m_memory(0)
{
memset(&m_parameters, 0, sizeof(DEBUG_SYMBOL_PARAMETERS));
m_parameters.ParentSymbol = DEBUG_ANY_ID;
}
SymbolGroupNode::~SymbolGroupNode()
{
delete m_memory;
}
const SymbolGroupNode *SymbolGroupNode::symbolGroupNodeParent() const
{
if (const AbstractSymbolGroupNode *p = parent())
......@@ -1004,7 +1019,8 @@ bool SymbolGroupNode::runSimpleDumpers(const SymbolGroupValueContext &ctx)
if (testFlags(SimpleDumperMask))
return false;
addFlags(dumpSimpleType(this , ctx, &m_dumperValue,
&m_dumperType, &m_dumperContainerSize, &m_dumperSpecialInfo));
&m_dumperType, &m_dumperContainerSize,
&m_dumperSpecialInfo, &m_memory));
if (symbolGroupDebug)
DebugPrint() << "<SymbolGroupNode::runSimpleDumpers " << name() << " '"
<< wStringToString(m_dumperValue) << "' Type="
......@@ -1105,6 +1121,9 @@ int SymbolGroupNode::dumpNode(std::ostream &str,
base64Encode(str, reinterpret_cast<const unsigned char *>(value.c_str()), value.size() * sizeof(wchar_t));
str << '"';
}
const int format = dumpParameters.format(t, aFullIName);
if (format > 0)
dumpEditValue(this, ctx, format, str);
}
// Children: Dump all known non-obscured or subelements
unsigned childCountGuess = 0;
......
......@@ -45,6 +45,7 @@ class SymbolGroupNodeVisitor;
class SymbolGroup;
struct SymbolGroupValueContext;
class SymbolGroupNode;
class MemoryHandle;
// Helper struct used for check results when recoding CDB char pointer output.
struct DumpParameterRecodeResult
......@@ -214,6 +215,8 @@ public:
WatchNode = 0x100
};
~SymbolGroupNode();
typedef std::vector<DEBUG_SYMBOL_PARAMETERS> SymbolParameterVector;
void parseParameters(SymbolParameterVector::size_type index,
......@@ -268,6 +271,8 @@ public:
ULONG subElements() const { return m_parameters.SubElements; }
ULONG index() const { return m_index; }
MemoryHandle *memory() const { return m_memory; }
virtual SymbolGroupNode *asSymbolGroupNode() { return this; }
virtual const SymbolGroupNode *asSymbolGroupNode() const { return this; }
......@@ -296,6 +301,7 @@ private:
int m_dumperType;
int m_dumperContainerSize;
void *m_dumperSpecialInfo; // Opaque information passed from simple to complex dumpers
MemoryHandle *m_memory; // Memory shared between simple dumper and edit value.
};
class ReferenceSymbolGroupNode : public AbstractSymbolGroupNode
......
......@@ -1546,7 +1546,8 @@ bool readQt5StringData(const SymbolGroupValue &dV, int qtMajorVersion,
return true;
}
static inline bool dumpQString(const SymbolGroupValue &v, std::wostream &str)
static inline bool dumpQString(const SymbolGroupValue &v, std::wostream &str,
MemoryHandle **memoryHandle = 0)
{
const QtInfo &qtInfo = QtInfo::get(v.context());
const SymbolGroupValue dV = v["d"];
......@@ -1557,7 +1558,10 @@ static inline bool dumpQString(const SymbolGroupValue &v, std::wostream &str)
if (const SymbolGroupValue sizeValue = dV["size"]) {
const int size = sizeValue.intValue();
if (size >= 0) {
str << L'"' << dV["data"].wcharPointerData(size) << L'"';
const std::wstring stringData = dV["data"].wcharPointerData(size);
str << L'"' << stringData << L'"';
if (memoryHandle)
*memoryHandle = MemoryHandle::fromStdWString(stringData);
return true;
}
}
......@@ -1581,7 +1585,11 @@ static inline bool dumpQString(const SymbolGroupValue &v, std::wostream &str)
} else {
str << L"\"\"";
}
delete [] memory;
if (memoryHandle) {
*memoryHandle = new MemoryHandle(memory, size);
} else {
delete [] memory;
}
return true;
}
......@@ -1634,7 +1642,8 @@ static unsigned qAtomicIntSize(const SymbolGroupValueContext &ctx)
}
// Dump a QByteArray
static inline bool dumpQByteArray(const SymbolGroupValue &v, std::wostream &str)
static inline bool dumpQByteArray(const SymbolGroupValue &v, std::wostream &str,
MemoryHandle **memoryHandle = 0)
{
const QtInfo &qtInfo = QtInfo::get(v.context());
const SymbolGroupValue dV = v["d"];
......@@ -1682,7 +1691,11 @@ static inline bool dumpQByteArray(const SymbolGroupValue &v, std::wostream &str)
} else {
str << L"<empty>";
}
delete [] memory;
if (memoryHandle) {
*memoryHandle = new MemoryHandle(reinterpret_cast<unsigned char *>(memory), size);
} else {
delete [] memory;
}
return true;
}
......@@ -2441,7 +2454,8 @@ static inline bool dumpQSharedPointer(const SymbolGroupValue &v, std::wostream &
unsigned dumpSimpleType(SymbolGroupNode *n, const SymbolGroupValueContext &ctx,
std::wstring *s, int *knownTypeIn /* = 0 */,
int *containerSizeIn /* = 0 */,
void **specialInfoIn /* = 0 */)
void **specialInfoIn /* = 0 */,
MemoryHandle **memoryHandleIn /* = 0 */)
{
QTC_TRACE_IN
if (containerSizeIn)
......@@ -2491,7 +2505,7 @@ unsigned dumpSimpleType(SymbolGroupNode *n, const SymbolGroupValueContext &ctx,
rc = dumpQChar(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
break;
case KT_QByteArray:
rc = dumpQByteArray(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
rc = dumpQByteArray(v, str, memoryHandleIn) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
break;
case KT_QFileInfo:
rc = dumpQFileInfo(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
......@@ -2518,7 +2532,7 @@ unsigned dumpSimpleType(SymbolGroupNode *n, const SymbolGroupValueContext &ctx,
rc = dumpQScriptValue(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
break;
case KT_QString:
rc = dumpQString(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
rc = dumpQString(v, str, memoryHandleIn) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
break;
case KT_QColor:
rc = dumpQColor(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
......@@ -2594,6 +2608,51 @@ unsigned dumpSimpleType(SymbolGroupNode *n, const SymbolGroupValueContext &ctx,
return rc;
}
static inline void formatEditValue(int displayFormat, const MemoryHandle *mh, std::ostream &str)
{
str << "editformat=\"" << displayFormat << "\",editvalue=\""
<< mh->toHex() << "\",";
}
bool dumpEditValue(const SymbolGroupNode *n, const SymbolGroupValueContext &,
int desiredFormat, std::ostream &str)
{
// Keep in sync watchhandler.cpp/showEditValue(), dumper.py.
enum DebuggerEditFormats {
DisplayImageData = 1,
DisplayUtf16String = 2,
DisplayImageFile = 3,
DisplayProcess = 4,
DisplayLatin1String = 5,
DisplayUtf8String = 6
};
enum Formats {
NormalFormat = 0,
StringSeparateWindow = 1 // corresponds to menu index.
};
if (desiredFormat <= 0)
return true;
if (SymbolGroupValue::verbose)
DebugPrint() << __FUNCTION__ << ' ' << n->name() << '/' << desiredFormat;
switch (n->dumperType()) {
case KT_QString:
if (desiredFormat == StringSeparateWindow)
if (const MemoryHandle *mh = n->memory())
formatEditValue(DisplayUtf16String, mh, str);
break;
case KT_QByteArray:
if (desiredFormat == StringSeparateWindow)
if (const MemoryHandle *mh = n->memory())
formatEditValue(DisplayLatin1String, mh, str);
break;
}
return true;
}
// Dump of QByteArray: Display as an array of unsigned chars.
static inline std::vector<AbstractSymbolGroupNode *>
complexDumpQByteArray(SymbolGroupNode *n, const SymbolGroupValueContext &ctx)
......
......@@ -40,6 +40,7 @@
class AbstractSymbolGroupNode;
class SymbolGroupNode;
class SymbolGroup;
class MemoryHandle;
struct SymbolGroupValueContext
{
......@@ -248,7 +249,11 @@ unsigned dumpSimpleType(SymbolGroupNode *n, const SymbolGroupValueContext &ctx,
std::wstring *s,
int *knownType = 0,
int *containerSizeIn = 0,
void **specialInfoIn = 0);
void **specialInfoIn = 0,
MemoryHandle **memoryHandleIn = 0);
bool dumpEditValue(const SymbolGroupNode *n, const SymbolGroupValueContext &,
int desiredFormat, std::ostream &str);
enum AssignEncoding
{
......
......@@ -625,6 +625,13 @@ void CdbEngine::setupEngine()
STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
notifyEngineSetupFailed();
}
const QStringList stringFormats = QStringList()
<< tr("Normal") << tr("Separate Window");
WatchHandler *wh = watchHandler();
wh->addTypeFormats("QString", stringFormats);
wh->addTypeFormats("QString *", stringFormats);
wh->addTypeFormats("QByteArray", stringFormats);
wh->addTypeFormats("QByteArray *", stringFormats);
}
bool CdbEngine::launchCDB(const DebuggerStartParameters &sp, QString *errorMessage)
......
......@@ -83,7 +83,7 @@ enum DebuggerEncoding
Hex2EncodedFloat8 = 26
};
// Keep in sync with dumper.py
// Keep in sync with dumper.py, symbolgroupvalue.cpp of CDB
enum DebuggerDisplay {
StopDisplay = 0,
DisplayImageData = 1,
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment