Commit 7baf7b24 authored by Friedemann Kleint's avatar Friedemann Kleint
Browse files

Debugger[New CDB]: Pass formatting parameters.

Pass around formatting parameters as structure.
Prototypically implement formatting of char *-Pointers
as UTF8.
Transfer registers and modules only if dock window is visible.
parent 691aa41e
......@@ -104,11 +104,14 @@ static const CommandDescription commandDescriptions[] = {
"iname1-list: Comma-separated list of inames"},
{"locals",
"Prints local variables of symbol group in GDBMI or debug format",
"[-t token] [-h] [-d] [-e expand-list] [-u uninitialized-list]\n<frame-number> [iname]\n"
"[-t token] [T formats] [-I formats] [-c] [-h] [-d] [-e expand-list] [-u uninitialized-list]\n<frame-number> [iname]\n"
"-h human-readable ouput\n"
"-d debug output\n"
"-c complex dumpers\n"
"-e expand-list Comma-separated list of inames to be expanded beforehand\n"
"-u uninitialized-list Comma-separated list of uninitialized inames"},
"-u uninitialized-list Comma-separated list of uninitialized inames\n"
"-I formatmap map of 'hex-encoded-iname=typecode'\n"
"-T formatmap map of 'hex-encoded-type-name=typecode'"},
{"dumplocal", "Dumps local variable using simple dumpers (testing command).",
"[-t token] <frame-number> <iname>"},
{"typecast","Performs a type cast on an unexpanded iname of symbol group.",
......@@ -270,12 +273,11 @@ static std::string commmandLocals(ExtensionCommandContext &exc,PCSTR args, int *
{
// Parse the command
unsigned debugOutput = 0;
bool humanReadableGdbmi = false;
std::string iname;
StringList tokens = commandTokens<StringList>(args, token);
StringVector expandedInames;
StringVector uninitializedInames;
DumpParameters parameters;
// Parse away options
while (!tokens.empty() && tokens.front().size() == 2 && tokens.front().at(0) == '-') {
const char option = tokens.front().at(1);
......@@ -285,7 +287,10 @@ static std::string commmandLocals(ExtensionCommandContext &exc,PCSTR args, int *
debugOutput++;
break;
case 'h':
humanReadableGdbmi = true;
parameters.dumpFlags |= DumpParameters::DumpHumanReadable;
break;
case 'c':
parameters.dumpFlags |= DumpParameters::DumpComplexDumpers;
break;
case 'u':
if (tokens.empty()) {
......@@ -303,8 +308,25 @@ static std::string commmandLocals(ExtensionCommandContext &exc,PCSTR args, int *
split(tokens.front(), ',', std::back_inserter(expandedInames));
tokens.pop_front();
break;
}
}
case 'T': // typeformats: 'hex'ed name = formatnumber,...'
if (tokens.empty()) {
*errorMessage = singleLineUsage(commandDescriptions[CmdLocals]);
return std::string();
}
parameters.typeFormats = DumpParameters::decodeFormatArgument(tokens.front());
tokens.pop_front();
break;
case 'I': // individual formats: 'hex'ed name = formatnumber,...'
if (tokens.empty()) {
*errorMessage = singleLineUsage(commandDescriptions[CmdLocals]);
return std::string();
}
parameters.individualFormats = DumpParameters::decodeFormatArgument(tokens.front());
tokens.pop_front();
break;
} // case option
} // for options
// Frame and iname
unsigned frame;
if (tokens.empty() || !integerFromString(tokens.front(), &frame)) {
......@@ -329,8 +351,8 @@ static std::string commmandLocals(ExtensionCommandContext &exc,PCSTR args, int *
const SymbolGroupValueContext dumpContext(exc.dataSpaces());
return iname.empty() ?
symGroup->dump(dumpContext, humanReadableGdbmi) :
symGroup->dump(iname, dumpContext, humanReadableGdbmi, errorMessage);
symGroup->dump(dumpContext, parameters) :
symGroup->dump(iname, dumpContext, parameters, errorMessage);
}
extern "C" HRESULT CALLBACK locals(CIDebugClient *client, PCSTR args)
......
......@@ -185,6 +185,55 @@ std::wstring stringToWString(const std::string &w)
return rc;
}
// Convert an ASCII hex digit to its value 'A'->10
inline unsigned hexDigit(char c)
{
if (c <= '9')
return c - '0';
if (c <= 'F')
return c - 'A' + 10;
return c - 'a' + 10;
}
// Convert an ASCII hex digit to its value 'A'->10
inline char toHexDigit(unsigned v)
{
if (v < 10)
return char(v) + '0';
return char(v - 10) + 'a';
}
// String from hex "414A" -> "AJ".
std::string stringFromHex(const char *p, const char *end)
{
if (p == end)
return std::string();
std::string rc;
rc.reserve((end - p) / 2);
for ( ; p < end; p++) {
unsigned c = 16 * hexDigit(*p);
c += hexDigit(*++p);
rc.push_back(char(c));
}
return rc;
}
std::wstring dataToHexW(const unsigned char *p, const unsigned char *end)
{
if (p == end)
return std::wstring();
std::wstring rc;
rc.reserve(2 * (end - p));
for ( ; p < end ; p++) {
const unsigned c = *p;
rc.push_back(toHexDigit(c / 16));
rc.push_back(toHexDigit(c &0xF));
}
return rc;
}
// Format a map as a GDBMI hash {key="value",..}
void formatGdbmiHash(std::ostream &os, const std::map<std::string, std::string> &m)
{
......
......@@ -86,6 +86,18 @@ bool integerFromString(const std::string &s, Integer *v)
return !str.fail();
}
// Read an integer from a wstring as '10' or '0xA'
template <class Integer>
bool integerFromWString(const std::wstring &s, Integer *v)
{
const bool isHex = s.compare(0, 2, L"0x") == 0;
std::wistringstream str(isHex ? s.substr(2, s.size() - 2) : s);
if (isHex)
str >> std::hex;
str >> *v;
return !str.fail();
}
void replace(std::wstring &s, wchar_t before, wchar_t after);
// Stream a string onto a char stream doing backslash & octal escaping
......@@ -128,6 +140,10 @@ std::string wStringToGdbmiString(const std::wstring &w);
std::string wStringToString(const std::wstring &w);
std::wstring stringToWString(const std::string &w);
// String from hex "414A" -> "AJ".
std::string stringFromHex(const char *begin, const char *end);
std::wstring dataToHexW(const unsigned char *begin, const unsigned char *end);
// Format a map as a GDBMI hash {key="value",..}
void formatGdbmiHash(std::ostream &os, const std::map<std::string, std::string> &);
......
......@@ -76,6 +76,128 @@ std::ostream &operator<<(std::ostream &str, const DEBUG_SYMBOL_PARAMETERS &param
return str;
}
// --------------- DumpParameters
DumpParameters::DumpParameters() : dumpFlags(0)
{
}
// typeformats: decode hex-encoded name, value pairs:
// '414A=2,...' -> map of "AB:2".
DumpParameters::FormatMap DumpParameters::decodeFormatArgument(const std::string &f)
{
FormatMap rc;
const std::string::size_type size = f.size();
// Split 'hexname=4,'
for (std::string::size_type pos = 0; pos < size ; ) {
// Cut out key
const std::string::size_type equalsPos = f.find('=', pos);
if (equalsPos == std::string::npos)
return rc;
const std::string name = stringFromHex(f.c_str() + pos, f.c_str() + equalsPos);
// Search for number
const std::string::size_type numberPos = equalsPos + 1;
std::string::size_type nextPos = f.find(',', numberPos);
if (nextPos == std::string::npos)
nextPos = size;
int format;
if (!integerFromString(f.substr(numberPos, nextPos - numberPos), &format))
return rc;
rc.insert(FormatMap::value_type(name, format));
pos = nextPos + 1;
}
return rc;
}
int DumpParameters::format(const std::string &type, const std::string &iname) const
{
if (!individualFormats.empty()) {
const FormatMap::const_iterator iit = individualFormats.find(iname);
if (iit != individualFormats.end())
return iit->second;
}
if (!typeFormats.empty()) {
const FormatMap::const_iterator tit = typeFormats.find(type);
if (tit != typeFormats.end())
return tit->second;
}
return -1;
}
enum PointerFormats // Watch data pointer format requests
{
FormatRawPointer = 0,
FormatLatin1String = 1,
FormatUtf8String = 2,
FormatUtf16String = 3,
FormatUcs4String = 4
};
enum DumpEncoding // WatchData encoding of GDBMI values
{
DumpEncodingAscii = 0,
DumpEncodingBase64 = 1,
DumpEncodingBase64_Utf16 = 2,
DumpEncodingBase64_Ucs4 = 3,
DumpEncodingHex_Latin1 = 6,
DumpEncodingHex_Utf161 = 7,
DumpEncodingHex_Ucs4_LittleEndian = 8,
DumpEncodingHex_Utf8_LittleEndian = 9,
DumpEncodingHex_Ucs4_BigEndian = 10,
DumpEncodingHex_Utf16_BigEndian = 11,
DumpEncodingHex_Utf16_LittleEndian = 12
};
bool DumpParameters::recode(const std::string &type,
const std::string &iname,
const SymbolGroupValueContext &ctx,
std::wstring *value, int *encoding) const
{
// We basically handle char formats for 'char *', '0x834478 "hallo.."'
// Determine address and length from the pointer value output,
// read the raw memory and recode if that is possible.
const int newFormat = format(type, iname);
if (newFormat < 2)
return false;
if (value->compare(0, 2, L"0x"))
return false;
const std::wstring::size_type quote1 = value->find(L'"', 2);
if (quote1 == std::wstring::npos)
return false;
const std::wstring::size_type quote2 = value->find(L'"', quote1 + 1);
if (quote2 == std::wstring::npos)
return false;
const std::wstring::size_type length = quote2 - quote1 - 1;
if (!length)
return false;
ULONG64 address = 0;
if (!integerFromWString(value->substr(0, quote1 - 1), &address) || !address)
return false;
// Allocate real length + 4 bytes ('\0') for largest format.
// '\0' is not listed in the CDB output.
const std::wstring::size_type allocLength = length + 4;
unsigned char *buffer = new unsigned char[allocLength];
std::fill(buffer, buffer + allocLength, 0);
ULONG obtained = 0;
if (FAILED(ctx.dataspaces->ReadVirtual(address, buffer, ULONG(length), &obtained))) {
delete [] buffer;
return false;
}
// Recode raw memory
switch (newFormat) {
case FormatUtf8String:
*value = dataToHexW(buffer, buffer + length + 1); // UTF8 + 0
*encoding = DumpEncodingHex_Utf8_LittleEndian;
break;
case FormatUtf16String:
break;
case FormatUcs4String:
break;
}
delete [] buffer;
return true;
}
// ------------- SymbolGroup
SymbolGroup::SymbolGroup(IDebugSymbolGroup2 *sg,
const SymbolParameterVector &vec,
ULONG threadId,
......@@ -579,7 +701,9 @@ static inline void indentStream(std::ostream &str, unsigned depth)
str << " ";
}
void SymbolGroupNode::dump(std::ostream &str, const SymbolGroupValueContext &ctx)
void SymbolGroupNode::dump(std::ostream &str,
const DumpParameters &p,
const SymbolGroupValueContext &ctx)
{
const std::string iname = fullIName();
const std::string t = type();
......@@ -595,16 +719,22 @@ void SymbolGroupNode::dump(std::ostream &str, const SymbolGroupValueContext &ctx
bool valueEditable = !uninitialized;
bool valueEnabled = !uninitialized;
const std::wstring value = displayValue(ctx);
// ASCII or base64?
if (isSevenBitClean(value.c_str(), value.size())) {
str << ",valueencoded=\"0\",value=\"" << gdbmiWStringFormat(value) << '"';
} else {
str << ",valueencoded=\"2\",value=\"";
base64Encode(str, reinterpret_cast<const unsigned char *>(value.c_str()), value.size() * sizeof(wchar_t));
str << '"';
// Shall it be recoded?
std::wstring value = displayValue(ctx);
int encoding = 0;
if (p.recode(t, iname, ctx, &value, &encoding)) {
str << ",valueencoded=\"" << encoding
<< "\",value=\"" << gdbmiWStringFormat(value) <<'"';
} else { // As is: ASCII or base64?
if (isSevenBitClean(value.c_str(), value.size())) {
str << ",valueencoded=\"" << DumpEncodingAscii << "\",value=\""
<< gdbmiWStringFormat(value) << '"';
} else {
str << ",valueencoded=\"" << DumpEncodingBase64_Utf16 << "\",value=\"";
base64Encode(str, reinterpret_cast<const unsigned char *>(value.c_str()), value.size() * sizeof(wchar_t));
str << '"';
}
}
// Children: Dump all known or subelements (guess).
const VectorIndexType childCountGuess = uninitialized ? 0 :
(m_children.empty() ? m_parameters.SubElements : m_children.size());
......@@ -792,11 +922,12 @@ static inline std::string msgNotFound(const std::string &nodeName)
return str.str();
}
std::string SymbolGroup::dump(const SymbolGroupValueContext &ctx, bool humanReadable) const
std::string SymbolGroup::dump(const SymbolGroupValueContext &ctx,
const DumpParameters &p) const
{
std::ostringstream str;
DumpSymbolGroupNodeVisitor visitor(str, ctx, humanReadable);
if (humanReadable)
DumpSymbolGroupNodeVisitor visitor(str, ctx, p);
if (p.humanReadable())
str << '\n';
str << '[';
accept(visitor);
......@@ -805,7 +936,10 @@ std::string SymbolGroup::dump(const SymbolGroupValueContext &ctx, bool humanRead
}
// Dump a node, potentially expand
std::string SymbolGroup::dump(const std::string &iname, const SymbolGroupValueContext &ctx, bool humanReadable, std::string *errorMessage)
std::string SymbolGroup::dump(const std::string &iname,
const SymbolGroupValueContext &ctx,
const DumpParameters &p,
std::string *errorMessage)
{
SymbolGroupNode *const node = find(iname);
if (node == 0) {
......@@ -819,9 +953,9 @@ std::string SymbolGroup::dump(const std::string &iname, const SymbolGroupValueCo
return false;
}
std::ostringstream str;
if (humanReadable)
if (p.humanReadable())
str << '\n';
DumpSymbolGroupNodeVisitor visitor(str, ctx, humanReadable);
DumpSymbolGroupNodeVisitor visitor(str, ctx, p);
str << '[';
node->accept(visitor, 0, 0);
str << ']';
......@@ -1056,8 +1190,9 @@ SymbolGroupNodeVisitor::VisitResult
// --------------------- DumpSymbolGroupNodeVisitor
DumpSymbolGroupNodeVisitor::DumpSymbolGroupNodeVisitor(std::ostream &os,
const SymbolGroupValueContext &context,
bool humanReadable) :
m_os(os), m_humanReadable(humanReadable),m_context(context), m_visitChildren(false)
const DumpParameters &parameters) :
m_os(os), m_context(context), m_parameters(parameters),
m_visitChildren(false)
{
}
......@@ -1073,18 +1208,18 @@ SymbolGroupNodeVisitor::VisitResult
// Do not recurse into children unless the node was expanded by the watch model
if (child)
m_os << ','; // Separator in parents list
if (m_humanReadable) {
if (m_parameters.humanReadable()) {
m_os << '\n';
indentStream(m_os, depth * 2);
}
m_os << '{';
node->dump(m_os, m_context);
node->dump(m_os, m_parameters, m_context);
if (m_visitChildren) { // open children array
m_os << ",children=[";
} else { // No children, close array.
m_os << '}';
}
if (m_humanReadable)
if (m_parameters.humanReadable())
m_os << '\n';
return m_visitChildren ? VisitContinue : VisitSkipChildren;
}
......@@ -1092,6 +1227,6 @@ SymbolGroupNodeVisitor::VisitResult
void DumpSymbolGroupNodeVisitor::childrenVisited(const SymbolGroupNode *, unsigned)
{
m_os << "]}"; // Close children array and self
if (m_humanReadable)
if (m_parameters.humanReadable())
m_os << '\n';
}
......@@ -34,6 +34,7 @@
#include <string>
#include <vector>
#include <map>
#include <iosfwd>
std::ostream &operator<<(std::ostream &, const DEBUG_SYMBOL_PARAMETERS&p);
......@@ -42,6 +43,31 @@ class SymbolGroupNodeVisitor;
class SymbolGroup;
struct SymbolGroupValueContext;
// All parameters for dumping in one struct.
struct DumpParameters
{
typedef std::map<std::string, int> FormatMap; // type or iname to format
enum DumpFlags
{
DumpHumanReadable = 0x1,
DumpComplexDumpers = 0x2
};
DumpParameters();
bool humanReadable() const { return dumpFlags & DumpHumanReadable; }
// Helper to decode format option arguments.
static FormatMap decodeFormatArgument(const std::string &f);
bool recode(const std::string &type, const std::string &iname,
const SymbolGroupValueContext &ctx,
std::wstring *value, int *encoding) const;
int format(const std::string &type, const std::string &iname) const;
unsigned dumpFlags;
FormatMap typeFormats;
FormatMap individualFormats;
};
// Thin wrapper around a symbol group entry. Provides accessors for fixed-up
// symbol group value and a dumping facility triggered by dump()/displayValue()
// calling dumpSimpleType() based on SymbolGroupValue expressions. These values
......@@ -70,6 +96,7 @@ public:
ExpandedByDumper = 0x10,
AdditionalSymbol = 0x20 // Introduced by addSymbol, should not be visible
};
typedef std::vector<DEBUG_SYMBOL_PARAMETERS> SymbolParameterVector;
typedef std::vector<SymbolGroupNode *> SymbolGroupNodePtrVector;
typedef SymbolGroupNodePtrVector::iterator SymbolGroupNodePtrVectorIterator;
......@@ -102,7 +129,7 @@ public:
SymbolGroup *symbolGroup() const { return m_symbolGroup; }
// I/O: Gdbmi dump for Visitors
void dump(std::ostream &str, const SymbolGroupValueContext &ctx);
void dump(std::ostream &str, const DumpParameters &p, const SymbolGroupValueContext &ctx);
// I/O: debug for Visitors
void debug(std::ostream &os, unsigned verbosity, unsigned depth) const;
......@@ -202,10 +229,11 @@ public:
~SymbolGroup();
// Dump all
std::string dump(const SymbolGroupValueContext &ctx, bool humanReadable = false) const;
std::string dump(const SymbolGroupValueContext &ctx,
const DumpParameters &p = DumpParameters()) const;
// Expand node and dump
std::string dump(const std::string &iname, const SymbolGroupValueContext &ctx,
bool humanReadable, std::string *errorMessage);
const DumpParameters &p, std::string *errorMessage);
std::string debug(const std::string &iname = std::string(), unsigned verbosity = 0) const;
unsigned frame() const { return m_frame; }
......@@ -274,15 +302,15 @@ class DumpSymbolGroupNodeVisitor : public SymbolGroupNodeVisitor {
public:
explicit DumpSymbolGroupNodeVisitor(std::ostream &os,
const SymbolGroupValueContext &context,
bool humanReadable);
const DumpParameters &parameters = DumpParameters());
private:
virtual VisitResult visit(SymbolGroupNode *node, unsigned child, unsigned depth);
virtual void childrenVisited(const SymbolGroupNode * node, unsigned depth);
std::ostream &m_os;
const bool m_humanReadable;
const SymbolGroupValueContext &m_context;
const DumpParameters &m_parameters;
bool m_visitChildren;
};
......
......@@ -653,13 +653,26 @@ void CdbEngine::updateWatchData(const Debugger::Internal::WatchData &dataIn,
updateLocalVariable(dataIn.iname);
}
void CdbEngine::addLocalsOptions(ByteArrayInputStream &str) const
{
if (debuggerCore()->boolSetting(UseDebuggingHelpers))
str << blankSeparator << "-c";
const QByteArray typeFormats = watchHandler()->typeFormatRequests();
if (!typeFormats.isEmpty())
str << blankSeparator << "-T " << typeFormats;
const QByteArray individualFormats = watchHandler()->individualFormatRequests();
if (!individualFormats.isEmpty())
str << blankSeparator << "-I " << individualFormats;
}
void CdbEngine::updateLocalVariable(const QByteArray &iname)
{
const int stackFrame = stackHandler()->currentIndex();
if (stackFrame >= 0) {
QByteArray localsArguments;
ByteArrayInputStream str(localsArguments);
str << stackFrame << ' ' << iname;
addLocalsOptions(str);
str << blankSeparator << stackFrame << ' ' << iname;
postExtensionCommand("locals", localsArguments, 0, &CdbEngine::handleLocals);
} else {
qWarning("Internal error; no stack frame in updateLocalVariable");
......@@ -973,6 +986,7 @@ void CdbEngine::activateFrame(int index)
str << e;
}
}
addLocalsOptions(str);
// Uninitialized variables if desired
if (debuggerCore()->boolSetting(UseCodeModel)) {
QStringList uninitializedVariables;
......@@ -1299,8 +1313,10 @@ void CdbEngine::handleSessionIdle(const QByteArray &message)
notifyInferiorSpontaneousStop();
}
// Start sequence to get all relevant data. Hack: Avoid module reload?
unsigned sequence = CommandListStack|CommandListRegisters|CommandListThreads;
if (modulesHandler()->modules().size() == 0)
unsigned sequence = CommandListStack;
if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_REGISTER)))
sequence |= CommandListRegisters;
if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_MODULES)))
sequence |= CommandListModules;
postCommandSequence(sequence);
// Report stop reason (GDBMI)
......
......@@ -45,6 +45,7 @@ class DisassemblerViewAgent;
struct CdbBuiltinCommand;
struct CdbExtensionCommand;
struct CdbOptions;
class ByteArrayInputStream;
class CdbEngine : public Debugger::DebuggerEngine
{
......@@ -174,6 +175,7 @@ private:
QString normalizeFileName(const QString &f);
void updateLocalVariable(const QByteArray &iname);
int elapsedLogTime() const;
void addLocalsOptions(ByteArrayInputStream &s) const;
const QByteArray m_creatorExtPrefix;
const QByteArray m_tokenPrefix;
......
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