// ************************************************************************** // 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 #include #include #include 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() { } // ************************************************************************** TQString GDBParser::getName(const char **buf) { const char *start = skipNextTokenStart(*buf); if (*start) { *buf = skipTokenValue(start); return TQCString(start, *buf - start + 1); } else *buf = start; return TQString(); } // ************************************************************************** TQString GDBParser::getValue(const char **buf) { const char *start = skipNextTokenStart(*buf); *buf = skipTokenValue(start); TQString value(TQCString(start, *buf - start + 1).data()); return value; } TQString GDBParser::undecorateValue(DataType type, const TQString& s) { TQCString 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 TQCString(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, '(', ')'); } TQString value(TQCString(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.tqfind(":")) { value = value.mid(i+2); } else { // Just reference, no value at all, remove all value = ""; } } if (value.tqfind("Cannot access memory") == 0) value = "(inaccessible)"; return value.stripWhiteSpace(); } TQString GDBParser::undecorateValue(const TQString& s) { DataType dataType = determineType(s.local8Bit()); TQString 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 - so is this - ugly!!! if (*buf == '{') { if (strncmp(buf, "{{", 2) == 0) return typeArray; if (strncmp(buf, "{}", 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 ' ' , "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 - 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 '); 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 ' ' , "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; } // ************************************************************************** // ************************************************************************** // ************************************************************************** }