summaryrefslogtreecommitdiffstats
path: root/languages/cpp/debugger/gdbparser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'languages/cpp/debugger/gdbparser.cpp')
-rw-r--r--languages/cpp/debugger/gdbparser.cpp432
1 files changed, 432 insertions, 0 deletions
diff --git a/languages/cpp/debugger/gdbparser.cpp b/languages/cpp/debugger/gdbparser.cpp
new file mode 100644
index 00000000..79057af5
--- /dev/null
+++ b/languages/cpp/debugger/gdbparser.cpp
@@ -0,0 +1,432 @@
+// **************************************************************************
+// begin : Tue Aug 17 1999
+// copyright : (C) 1999 by John Birch
+// email : jbb@kdevelop.org
+// **************************************************************************
+
+// **************************************************************************
+// *
+// This program is free software; you can redistribute it and/or modify *
+// it under the terms of the GNU General Public License as published by *
+// the Free Software Foundation; either version 2 of the License, or *
+// (at your option) any later version. *
+// *
+// **************************************************************************
+
+#include "gdbparser.h"
+#include "variablewidget.h"
+#include <kdebug.h>
+
+#include <qregexp.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+
+namespace GDBDebugger
+{
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+GDBParser *GDBParser::GDBParser_ = 0;
+
+GDBParser *GDBParser::getGDBParser()
+{
+ if (!GDBParser_)
+ GDBParser_ = new GDBParser();
+
+ return GDBParser_;
+}
+
+// **************************************************************************
+
+void GDBParser::destroy()
+{
+ delete GDBParser_;
+ GDBParser_ = 0;
+}
+
+// **************************************************************************
+
+GDBParser::GDBParser()
+{
+}
+
+// **************************************************************************
+
+GDBParser::~GDBParser()
+{
+}
+
+
+// **************************************************************************
+
+QString GDBParser::getName(const char **buf)
+{
+ const char *start = skipNextTokenStart(*buf);
+ if (*start) {
+ *buf = skipTokenValue(start);
+ return QCString(start, *buf - start + 1);
+ } else
+ *buf = start;
+
+ return QString();
+}
+
+// **************************************************************************
+
+QString GDBParser::getValue(const char **buf)
+{
+ const char *start = skipNextTokenStart(*buf);
+ *buf = skipTokenValue(start);
+
+ QString value(QCString(start, *buf - start + 1).data());
+ return value;
+}
+
+QString GDBParser::undecorateValue(DataType type, const QString& s)
+{
+ QCString l8 = s.local8Bit();
+ const char* start = l8;
+ const char* end = start + s.length();
+
+ if (*start == '{')
+ {
+ // Gdb uses '{' in two cases:
+ // - composites (arrays and structures)
+ // - pointers to functions. In this case type is
+ // enclosed in "{}". Not sure why it's so, as
+ // when printing pointer, type is in parenthesis.
+ if (type == typePointer)
+ {
+ // Looks like type in braces at the beginning. Strip it.
+ start = skipDelim(start, '{', '}');
+ }
+ else
+ {
+ // Looks like composite, strip the braces and return.
+ return QCString(start+1, end - start -1);
+ }
+ }
+ else if (*start == '(')
+ {
+ // Strip the type of the pointer from the value.
+ //
+ // When printing values of pointers, gdb prints the pointer
+ // type as well. This is not necessary for kdevelop -- after
+ // all, there's separate column with value type. But that behaviour
+ // is not configurable. The only way to change it is to explicitly
+ // pass the 'x' format specifier to the 'print' command.
+ //
+ // We probably can achieve that by sending an 'print in hex' request
+ // as soon as we know the type of variable, but that would be complex
+ // and probably conflict with 'toggle hex/decimal' command.
+ // So, address this problem as close to debugger as possible.
+
+ // We can't find the first ')', because type can contain '(' and ')'
+ // characters if its function pointer. So count opening and closing
+ // parentheses.
+
+ start = skipDelim(start, '(', ')');
+ }
+
+ QString value(QCString(start, end - start + 1).data());
+
+ value = value.stripWhiteSpace();
+
+ if (value[0] == '@')
+ {
+ // It's a reference, we need to show just the value.
+ if (int i = value.find(":"))
+ {
+ value = value.mid(i+2);
+ }
+ else
+ {
+ // Just reference, no value at all, remove all
+ value = "";
+ }
+ }
+
+ if (value.find("Cannot access memory") == 0)
+ value = "(inaccessible)";
+
+ return value.stripWhiteSpace();
+}
+
+QString GDBParser::undecorateValue(const QString& s)
+{
+ DataType dataType = determineType(s.local8Bit());
+ QString r = undecorateValue(dataType, s.local8Bit());
+ return r;
+}
+
+// Given a value that starts with 0xNNNNNN determines if
+// it looks more like pointer, or a string value.
+DataType pointerOrValue(const char *buf)
+{
+ while (*buf) {
+ if (!isspace(*buf))
+ buf++;
+ else if (*(buf+1) == '\"')
+ return typeValue;
+ else
+ break;
+ }
+
+ return typePointer;
+}
+
+
+DataType GDBParser::determineType(const char *buf) const
+{
+ if (!buf || !*(buf= skipNextTokenStart(buf)))
+ return typeUnknown;
+
+ // A reference, probably from a parameter value.
+ if (*buf == '@')
+ return typeReference;
+
+ // Structures and arrays - (but which one is which?)
+ // {void (void)} 0x804a944 <__builtin_new+41> - this is a fn pointer
+ // (void (*)(void)) 0x804a944 <f(E *, char)> - so is this - ugly!!!
+ if (*buf == '{') {
+ if (strncmp(buf, "{{", 2) == 0)
+ return typeArray;
+
+ if (strncmp(buf, "{<No data fields>}", 18) == 0)
+ return typeValue;
+
+ buf++;
+ while (*buf) {
+ switch (*buf) {
+ case '=':
+ return typeStruct;
+ case '"':
+ buf = skipString(buf);
+ break;
+ case '\'':
+ buf = skipQuotes(buf, '\'');
+ break;
+ case ',':
+ if (*(buf-1) == '}')
+ Q_ASSERT(false);
+ return typeArray;
+ case '}':
+ if (*(buf+1) == ',' || *(buf+1) == '\n' || !*(buf+1))
+ return typeArray; // Hmm a single element array??
+ if (strncmp(buf+1, " 0x", 3) == 0)
+ return typePointer; // What about references?
+ return typeUnknown; // very odd?
+ case '(':
+ buf = skipDelim(buf, '(', ')');
+ break;
+ case '<':
+ buf = skipDelim(buf, '<', '>');
+ // gdb may produce this output:
+ // $1 = 0x804ddf3 ' ' <repeats 20 times>, "TESTSTRING"
+ // after having finished with the "repeats"-block we need
+ // to check if the string continues
+ if ( buf[0] == ',' && (buf[2] == '\"' || buf[2] == '\'') ) {
+ buf++; //set the buffer behind the comma to indicate that the string continues
+ }
+ break;
+ default:
+ buf++;
+ break;
+ }
+ }
+ return typeUnknown;
+ }
+
+ // some sort of address. We need to sort out if we have
+ // a 0x888888 "this is a char*" type which we'll term a value
+ // or whether we just have an address
+ if (strncmp(buf, "0x", 2) == 0) {
+ return pointerOrValue(buf);
+ }
+
+ // Pointers and references - references are a bit odd
+ // and cause GDB to fail to produce all the local data
+ // if they haven't been initialised. but that's not our problem!!
+ // (void (*)(void)) 0x804a944 <f(E *, char)> - this is a fn pointer
+ if (*buf == '(') {
+ buf = skipDelim(buf, '(', ')');
+ // This 'if' handles values like this:
+ // (int (&)[3]) @0xbffff684: {5, 6, 7}
+ // which is a reference to array.
+ if (buf[1] == '@')
+ return typeReference;
+ // This 'if' handles values like this:
+ // (int (*)[3]) 0xbffff810
+ if (strncmp(buf, " 0x", 3) == 0)
+ {
+ ++buf;
+ return pointerOrValue(buf);
+ }
+
+ switch (*(buf-2)) {
+ case '*':
+ return typePointer;
+ case '&':
+ return typeReference;
+ default:
+ switch (*(buf-8)) {
+ case '*':
+ return typePointer;
+ case '&':
+ return typeReference;
+ }
+ return typeUnknown;
+ }
+ }
+
+ buf = skipTokenValue(buf);
+ if ((strncmp(buf, " = ", 3) == 0) || (*buf == '='))
+ return typeName;
+
+ return typeValue;
+}
+
+// **************************************************************************
+
+const char *GDBParser::skipString(const char *buf) const
+{
+ if (buf && *buf == '\"') {
+ buf = skipQuotes(buf, *buf);
+ while (*buf) {
+ if ((strncmp(buf, ", \"", 3) == 0) ||
+ (strncmp(buf, ", '", 3) == 0))
+ buf = skipQuotes(buf+2, *(buf+2));
+ else if (strncmp(buf, " <", 2) == 0) // take care of <repeats
+ buf = skipDelim(buf+1, '<', '>');
+ else
+ break;
+ }
+
+ // If the string is long then it's chopped and has ... after it.
+ while (*buf && *buf == '.')
+ buf++;
+ }
+
+ return buf;
+}
+
+// ***************************************************************************
+
+const char *GDBParser::skipQuotes(const char *buf, char quotes) const
+{
+ if (buf && *buf == quotes) {
+ buf++;
+
+ while (*buf) {
+ if (*buf == '\\')
+ buf++; // skips \" or \' problems
+ else if (*buf == quotes)
+ return buf+1;
+
+ buf++;
+ }
+ }
+
+ return buf;
+}
+
+// **************************************************************************
+
+const char *GDBParser::skipDelim(const char *buf, char open, char close) const
+{
+ if (buf && *buf == open) {
+ buf++;
+
+ while (*buf) {
+ if (*buf == open)
+ buf = skipDelim(buf, open, close);
+ else if (*buf == close)
+ return buf+1;
+ else if (*buf == '\"')
+ buf = skipString(buf);
+ else if (*buf == '\'')
+ buf = skipQuotes(buf, *buf);
+ else if (*buf)
+ buf++;
+ }
+ }
+ return buf;
+}
+
+// **************************************************************************
+
+const char *GDBParser::skipTokenValue(const char *buf) const
+{
+ if (buf) {
+ while (true) {
+ buf = skipTokenEnd(buf);
+
+ const char *end = buf;
+ while (*end && isspace(*end) && *end != '\n')
+ end++;
+
+ if (*end == 0 || *end == ',' || *end == '\n' || *end == '=' || *end == '}')
+ break;
+
+ if (buf == end)
+ break;
+
+ buf = end;
+ }
+ }
+
+ return buf;
+}
+
+// **************************************************************************
+
+const char *GDBParser::skipTokenEnd(const char *buf) const
+{
+ if (buf) {
+ switch (*buf) {
+ case '"':
+ return skipString(buf);
+ case '\'':
+ return skipQuotes(buf, *buf);
+ case '{':
+ return skipDelim(buf, '{', '}');
+ case '<':
+ buf = skipDelim(buf, '<', '>');
+ // gdb may produce this output:
+ // $1 = 0x804ddf3 ' ' <repeats 20 times>, "TESTSTRING"
+ // after having finished with the "repeats"-block we need
+ // to check if the string continues
+ if ( buf[0] == ',' && (buf[2] == '\"' || buf[2] == '\'') ) {
+ buf++; //set the buffer behind the comma to indicate that the string continues
+ }
+ return buf;
+ case '(':
+ return skipDelim(buf, '(', ')');
+ }
+
+ while (*buf && !isspace(*buf) && *buf != ',' && *buf != '}' && *buf != '=')
+ buf++;
+ }
+
+ return buf;
+}
+
+// **************************************************************************
+
+const char *GDBParser::skipNextTokenStart(const char *buf) const
+{
+ if (buf)
+ while (*buf && (isspace(*buf) || *buf == ',' || *buf == '}' || *buf == '='))
+ buf++;
+
+ return buf;
+}
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+}