This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
Using STL containers with GDB
- From: Vincent Benony <vbenony at nordnet dot fr>
- To: gdb-patches at sourceware dot org
- Date: Tue, 22 Apr 2008 11:39:59 +0200
- Subject: Using STL containers with GDB
Hi,
I use gdb at work for some time now. We currently develop a
multiplatform software which heavily use STL templates. As a result,
it's always difficult to me to debug such code.
I tried to find a way to debug more efficiently by using gdb scripts
that were able to dump std::vector or std::list, but it was always
painful.
So, yesterday, I decided myself to look at the code of GDB. After a
quick look, I wrote this patch. I know that it's not very clean, and
that it makes many assumptions about STL containers, and that it cannot
be considered as a long term feature for GDB, but, believe me, it's very
very usefull !
Now, I can look at the content of list, vector, set, map, multiset,
multimap and string simply with the classic "print" command of GDB.
Two switchs and a parameter are introduced :
* set print stl on / set print stl off
default is off. Controls the type of dumping (classic or "easy")
* set print stl_array_compatible on / set print stl_array_compatible
off
default is on. Controls the kind of formating of STL container when stl
print is on.
* set print stllimit N
set the maximum number of items we could dump when printing an STL
container to N. 0 means unlimited
I hope it could be useful for someone.
Sincerely
Vincent Benony
--- gdb-6.8/gdb/cp-valprint.c 2008-01-01 23:53:09.000000000 +0100
+++ gdb-6.8-patched/gdb/cp-valprint.c 2008-04-22 11:37:25.000000000 +0200
@@ -36,6 +36,10 @@
#include "valprint.h"
#include "cp-support.h"
#include "language.h"
+#include "block.h"
+#include "gdbcore.h"
+#include "value.h"
+#include "expression.h"
/* Controls printing of vtbl's */
int vtblprint;
@@ -70,6 +74,36 @@
value);
}
+/* Controls printing of STL containers */
+int stlprint;
+static void
+show_stlprint (struct ui_file *file, int from_tty,
+ struct cmd_list_element *c, const char *value)
+{
+ fprintf_filtered (file, _("\
+Printing of STL containers is %s.\n"),
+ value);
+}
+
+int stlprintcompatible;
+static void
+show_stlprintcompatible (struct ui_file *file, int from_tty,
+ struct cmd_list_element *c, const char *value)
+{
+ fprintf_filtered (file, _("\
+Compatible printing of STL containers is %s.\n"),
+ value);
+}
+
+int stllimit;
+static void
+show_stllimit (struct ui_file *file, int from_tty,
+ struct cmd_list_element *c, const char *value)
+{
+ fprintf_filtered (file, _("\
+Maximum elements of STL containers is set to %s.\n"), (stllimit == 0 || stllimit == (unsigned int)-1) ? "unlimited" : value);
+}
+
static struct obstack dont_print_vb_obstack;
static struct obstack dont_print_statmem_obstack;
@@ -148,6 +182,494 @@
DONT_PRINT is an array of baseclass types that we
should not print, or zero if called from top level. */
+int
+get_stl_inner_type (struct type *type, char *inner_type_name)
+{
+ int level = 1;
+ char *pname = type->main_type->name;
+
+ while (*pname && *pname != '<') pname++;
+ if (*pname == 0)
+ {
+ // This is not a template !
+ *inner_type_name = 0;
+ return 0;
+ }
+
+ pname++;
+
+ while (!(level == 1 && *pname == ','))
+ {
+ if (*pname == '<') level++;
+ else if (*pname == '>') level--;
+ if (!(level == 1 && *pname == ',')) *inner_type_name++ = *pname++;
+ }
+ *inner_type_name = 0;
+
+ return 1;
+}
+
+int
+get_stl_inner_type_pair (struct type *type, char *inner_type_key, char *inner_type_value)
+{
+ int level = 1;
+ char *pname = type->main_type->name;
+
+ while (*pname && *pname != '<') pname++;
+ if (*pname == 0)
+ {
+ // This is not a template !
+ *inner_type_key = 0;
+ *inner_type_value = 0;
+ return 0;
+ }
+
+ pname++;
+
+ while (!(level == 1 && *pname == ','))
+ {
+ if (*pname == '<') level++;
+ else if (*pname == '>') level--;
+ if (!(level == 1 && *pname == ',')) *inner_type_key++ = *pname++;
+ }
+ *inner_type_key = 0;
+
+ if (*pname == ',')
+ {
+ pname++;
+ while (!(level == 1 && *pname == ','))
+ {
+ if (*pname == '<') level++;
+ else if (*pname == '>') level--;
+ if (!(level == 1 && *pname == ',')) *inner_type_value++ = *pname++;
+ }
+ *inner_type_value = 0;
+ }
+
+ return 1;
+}
+
+struct type *
+cp_get_type(char * name, struct block * block)
+{
+ struct type *type;
+ struct type *ptr_type;
+ char *rname = (char *)malloc(strlen(name) + 1);
+ char *old = rname;
+ int nbPtrs = 0;
+
+ strcpy(rname, name);
+ if (!strncmp(rname, "const ", 6)) rname += 6;
+ while (rname[strlen(rname) - 1] == '*')
+ {
+ nbPtrs++;
+ rname[strlen(rname) - 1] = 0;
+ }
+
+ while (rname[strlen(rname) - 1] == ' ')
+ {
+ rname[strlen(rname) - 1] = 0;
+ }
+
+ type = lookup_typename(rname, block, 1);
+ while (nbPtrs--)
+ {
+ ptr_type = NULL;
+ make_pointer_type(type, &ptr_type);
+ type = ptr_type;
+ }
+
+ free(old);
+ return type;
+}
+
+void
+cp_print_stl_vector(struct type *type, CORE_ADDR address,
+ struct ui_file *stream, int format, int recurse,
+ enum val_prettyprint pretty)
+{
+ char *inner_name;
+ CORE_ADDR pc;
+ struct frame_info *current_frame;
+ struct type *void_type;
+ struct type *inner_type;
+ struct block *block;
+ int ptr_size;
+ unsigned int idx;
+ CORE_ADDR begin;
+ CORE_ADDR end;
+ gdb_byte *b;
+
+ inner_name = (char *)malloc(strlen(type->main_type->name) + 1);
+
+ if (get_stl_inner_type(type, inner_name))
+ {
+ if (!stlprintcompatible) fprintf_filtered (stream, "vector<%s>", inner_name);
+ fprintf_filtered (stream, "{");
+ if (pretty) fprintf_filtered (stream, "\n");
+ current_frame = get_current_frame ();
+ pc = get_frame_address_in_block (current_frame);
+ block = block_for_pc (pc);
+ inner_type = cp_get_type (inner_name, block);
+ void_type = cp_get_type("void *", block);
+ ptr_size = TYPE_LENGTH(void_type);
+
+ if (inner_type == NULL)
+ {
+ if (pretty) print_spaces_filtered (2 + 2 * recurse, stream);
+ fprintf_filtered (stream, " // no information for type %s\n", inner_name);
+ }
+ else
+ {
+ idx = 0;
+ begin = (CORE_ADDR)read_memory_integer (address, ptr_size);
+ end = (CORE_ADDR)read_memory_integer (address + ptr_size, ptr_size);
+ b = (gdb_byte *)malloc(TYPE_LENGTH(inner_type));
+ while (begin != end)
+ {
+ if (idx >= stllimit)
+ {
+ fprintf_filtered (stream, "...");
+ break;
+ }
+ if (pretty) print_spaces_filtered (2 + 2 * recurse, stream);
+ if (!stlprintcompatible) fprintf_filtered(stream, "[%d] = ", idx++);
+ read_memory(begin, b, TYPE_LENGTH(inner_type));
+ c_val_print(inner_type, b, 0, begin, stream, format, 0, recurse + 1, pretty);
+ begin += TYPE_LENGTH(inner_type);
+ if (begin != end) fprintf_filtered(stream, ", ");
+ if (pretty)
+ {
+ fprintf_filtered(stream, "\n");
+ }
+ }
+ free(b);
+ }
+ if (pretty) print_spaces_filtered (2 * recurse, stream);
+ fprintf_filtered (stream, "}");
+ if (recurse == 0) fprintf_filtered (stream, "\n");
+ }
+
+ free(inner_name);
+}
+
+void
+cp_print_stl_list(struct type *type, CORE_ADDR address,
+ struct ui_file *stream, int format, int recurse,
+ enum val_prettyprint pretty)
+{
+ char *inner_name;
+ CORE_ADDR pc;
+ CORE_ADDR node;
+ CORE_ADDR data_ptr;
+ struct frame_info *current_frame;
+ struct type *inner_type;
+ struct type *void_type;
+ struct block *block;
+ int ptr_size;
+ unsigned int idx;
+ gdb_byte *b;
+
+ inner_name = (char *)malloc(strlen(type->main_type->name) + 1);
+
+ if (get_stl_inner_type(type, inner_name))
+ {
+ if (!stlprintcompatible) fprintf_filtered (stream, "list<%s>", inner_name);
+ fprintf_filtered (stream, "{");
+ if (pretty) fprintf_filtered (stream, "\n");
+ current_frame = get_current_frame ();
+ pc = get_frame_address_in_block (current_frame);
+ block = block_for_pc (pc);
+ inner_type = cp_get_type (inner_name, block);
+ void_type = cp_get_type("void *", block);
+ ptr_size = TYPE_LENGTH(void_type);
+
+ if (inner_type == NULL)
+ {
+ if (pretty) print_spaces_filtered (2 + 2 * recurse, stream);
+ fprintf_filtered (stream, " // no information for type %s\n", inner_name);
+ }
+ else
+ {
+ idx = 0;
+ node = (CORE_ADDR)read_memory_integer (address, ptr_size);
+ b = (gdb_byte *)malloc(TYPE_LENGTH(inner_type));
+ do
+ {
+ if (idx >= stllimit)
+ {
+ fprintf_filtered (stream, "...");
+ break;
+ }
+ data_ptr = (CORE_ADDR)(node + ptr_size * 2);
+ if (pretty) print_spaces_filtered (2 + 2 * recurse, stream);
+ if (!stlprintcompatible) fprintf_filtered(stream, "[%d] = ", idx++);
+ read_memory(data_ptr, b, TYPE_LENGTH(inner_type));
+ c_val_print(inner_type, b, 0, data_ptr, stream, format, 0, recurse + 1, pretty);
+ node = (CORE_ADDR)read_memory_integer (node, ptr_size);
+ if (node != address) fprintf_filtered(stream, ", ");
+ if (pretty)
+ {
+ fprintf_filtered(stream, "\n");
+ }
+ } while (node != address);
+ free(b);
+ }
+ if (pretty) print_spaces_filtered (2 * recurse, stream);
+ fprintf_filtered (stream, "}");
+ if (recurse == 0) fprintf_filtered (stream, "\n");
+ }
+
+ free(inner_name);
+}
+
+#define TREE_PARENT(A) ((CORE_ADDR)read_memory_integer ((A) + 1 * ptr_size, ptr_size))
+#define TREE_LEFT(A) ((CORE_ADDR)read_memory_integer ((A) + 2 * ptr_size, ptr_size))
+#define TREE_RIGHT(A) ((CORE_ADDR)read_memory_integer ((A) + 3 * ptr_size, ptr_size))
+
+void
+cp_print_stl_set(struct type *type, CORE_ADDR address,
+ struct ui_file *stream, int format, int recurse,
+ enum val_prettyprint pretty)
+{
+ char *inner_name;
+ CORE_ADDR pc;
+ CORE_ADDR node;
+ CORE_ADDR left;
+ CORE_ADDR tmp_node;
+ CORE_ADDR data_ptr;
+ struct frame_info *current_frame;
+ struct type *inner_type;
+ struct type *void_type;
+ struct block *block;
+ int ptr_size;
+ unsigned int idx;
+ int count;
+ gdb_byte *b;
+
+ inner_name = (char *)malloc(strlen(type->main_type->name) + 1);
+
+ if (get_stl_inner_type(type, inner_name))
+ {
+ if (!stlprintcompatible) fprintf_filtered (stream, "set<%s>", inner_name);
+ fprintf_filtered (stream, "{");
+ if (pretty) fprintf_filtered (stream, "\n");
+ current_frame = get_current_frame ();
+ pc = get_frame_address_in_block (current_frame);
+ block = block_for_pc (pc);
+ inner_type = cp_get_type (inner_name, block);
+ void_type = cp_get_type("void *", block);
+ ptr_size = TYPE_LENGTH(void_type);
+
+ if (inner_type == NULL)
+ {
+ if (pretty) print_spaces_filtered (2 + 2 * recurse, stream);
+ fprintf_filtered (stream, " // no information for type %s\n", inner_name);
+ }
+ else
+ {
+ b = (gdb_byte *)malloc(TYPE_LENGTH(inner_type));
+ node = (CORE_ADDR)read_memory_integer (address + 3 * ptr_size, ptr_size);
+ count = (int)read_memory_integer(address + 5 * ptr_size, ptr_size);
+ for (idx=0; idx<count; idx++)
+ {
+ if (idx >= stllimit)
+ {
+ fprintf_filtered (stream, "...");
+ break;
+ }
+ data_ptr = (CORE_ADDR)(node + ptr_size * 4);
+ if (pretty) print_spaces_filtered (2 + 2 * recurse, stream);
+ if (!stlprintcompatible) fprintf_filtered(stream, "[%d] = ", idx);
+ read_memory(data_ptr, b, TYPE_LENGTH(inner_type));
+ c_val_print(inner_type, b, 0, data_ptr, stream, format, 0, recurse + 1, pretty);
+
+ if (TREE_RIGHT(node) != 0)
+ {
+ node = TREE_RIGHT(node);
+ while ((left = TREE_LEFT(node)))
+ {
+ node = left;
+ }
+ }
+ else
+ {
+ tmp_node = TREE_PARENT(node);
+ while (node == TREE_RIGHT(node))
+ {
+ node = tmp_node;
+ tmp_node = TREE_PARENT(node);
+ }
+ if (TREE_RIGHT(node) != tmp_node)
+ {
+ node = tmp_node;
+ }
+ }
+
+ if (idx != count - 1) fprintf_filtered(stream, ", ");
+ if (pretty)
+ {
+ fprintf_filtered(stream, "\n");
+ }
+ }
+ free(b);
+ }
+ if (pretty) print_spaces_filtered (2 * recurse, stream);
+ fprintf_filtered (stream, "}");
+ if (recurse == 0) fprintf_filtered (stream, "\n");
+ }
+
+ free(inner_name);
+}
+
+void
+cp_print_stl_map(struct type *type, CORE_ADDR address,
+ struct ui_file *stream, int format, int recurse,
+ enum val_prettyprint pretty)
+{
+ char *inner_key;
+ char *inner_value;
+ CORE_ADDR pc;
+ CORE_ADDR node;
+ CORE_ADDR left;
+ CORE_ADDR tmp_node;
+ CORE_ADDR data_ptr;
+ struct frame_info *current_frame;
+ struct type *inner_type_key;
+ struct type *inner_type_value;
+ struct type *void_type;
+ struct block *block;
+ int ptr_size;
+ unsigned int idx;
+ int count;
+ gdb_byte *b_key;
+ gdb_byte *b_value;
+
+ inner_key = (char *)malloc(strlen(type->main_type->name) + 1);
+ inner_value = (char *)malloc(strlen(type->main_type->name) + 1);
+
+ if (get_stl_inner_type_pair(type, inner_key, inner_value))
+ {
+ if (!stlprintcompatible) fprintf_filtered (stream, "map<%s, %s>", inner_key, inner_value);
+ fprintf_filtered (stream, "{");
+ if (pretty) fprintf_filtered (stream, "\n");
+ current_frame = get_current_frame ();
+ pc = get_frame_address_in_block (current_frame);
+ block = block_for_pc (pc);
+ inner_type_key = cp_get_type (inner_key, block);
+ inner_type_value = cp_get_type (inner_value, block);
+ void_type = cp_get_type("void *", block);
+ ptr_size = TYPE_LENGTH(void_type);
+
+ if (inner_type_key == NULL || inner_type_value == NULL)
+ {
+ if (pretty) print_spaces_filtered (2 + 2 * recurse, stream);
+ fprintf_filtered (stream, " // no information for type std::pair<%s, %s>\n", inner_key, inner_value);
+ }
+ else
+ {
+ b_key = (gdb_byte *)malloc(TYPE_LENGTH(inner_type_key));
+ b_value = (gdb_byte *)malloc(TYPE_LENGTH(inner_type_value));
+ node = (CORE_ADDR)read_memory_integer (address + 3 * ptr_size, ptr_size);
+ count = (int)read_memory_integer(address + 5 * ptr_size, ptr_size);
+ for (idx=0; idx<count; idx++)
+ {
+ if (idx >= stllimit)
+ {
+ fprintf_filtered (stream, "...");
+ break;
+ }
+ data_ptr = (CORE_ADDR)(node + ptr_size * 4);
+ read_memory(data_ptr, b_key, TYPE_LENGTH(inner_type_key));
+ read_memory(data_ptr + ptr_size, b_value, TYPE_LENGTH(inner_type_value));
+
+ if (pretty) print_spaces_filtered (2 + 2 * recurse, stream);
+ if (!stlprintcompatible) fprintf_filtered(stream, "[%d] = ", idx);
+ fprintf_filtered (stream, "{");
+ if (pretty) fprintf_filtered(stream, "\n");
+
+ if (pretty) print_spaces_filtered (4 + 2 * recurse, stream);
+ if (!stlprintcompatible)
+ {
+ fprintf_filtered(stream, "key ");
+ if (pretty) fprintf_filtered(stream, " ");
+ fprintf_filtered(stream, "= ");
+ }
+ c_val_print(inner_type_key, b_key, 0, data_ptr, stream, format, 0, recurse + 3, pretty);
+ if (pretty) fprintf_filtered(stream, ",\n"); else fprintf_filtered(stream, ", ");
+ if (pretty) print_spaces_filtered (4 + 2 * recurse, stream);
+ if (!stlprintcompatible)
+ {
+ fprintf_filtered(stream, "value = ");
+ }
+ c_val_print(inner_type_value, b_value, 0, data_ptr, stream, format, 0, recurse + 3, pretty);
+ if (pretty) fprintf_filtered(stream, "\n");
+
+ if (pretty) print_spaces_filtered (4 + 2 * recurse, stream);
+ fprintf_filtered(stream, "}");
+
+ if (TREE_RIGHT(node) != 0)
+ {
+ node = TREE_RIGHT(node);
+ while ((left = TREE_LEFT(node)))
+ {
+ node = left;
+ }
+ }
+ else
+ {
+ tmp_node = TREE_PARENT(node);
+ while (node == TREE_RIGHT(node))
+ {
+ node = tmp_node;
+ tmp_node = TREE_PARENT(node);
+ }
+ if (TREE_RIGHT(node) != tmp_node)
+ {
+ node = tmp_node;
+ }
+ }
+
+ if (idx != count - 1) fprintf_filtered(stream, ", ");
+ if (pretty)
+ {
+ fprintf_filtered(stream, "\n");
+ }
+ }
+ free(b_key);
+ free(b_value);
+ }
+ if (pretty) print_spaces_filtered (2 * recurse, stream);
+ fprintf_filtered (stream, "}");
+ if (recurse == 0) fprintf_filtered (stream, "\n");
+ }
+
+ free(inner_key);
+ free(inner_value);
+}
+
+void
+cp_print_stl_string(struct type *type, CORE_ADDR address,
+ struct ui_file *stream, int format, int recurse,
+ enum val_prettyprint pretty)
+{
+ struct type *char_type;
+ struct frame_info *current_frame;
+ struct block *block;
+ int ptr_size;
+ int pc;
+ CORE_ADDR string_ptr;
+
+ current_frame = get_current_frame ();
+ pc = get_frame_address_in_block (current_frame);
+ block = block_for_pc (pc);
+ char_type = cp_get_type("char *", block);
+ ptr_size = TYPE_LENGTH(char_type);
+
+ string_ptr = read_memory_integer (address, ptr_size);
+ val_print_string (string_ptr, -1, 1, stream);
+}
+
void
cp_print_value_fields (struct type *type, struct type *real_type,
const gdb_byte *valaddr, int offset, CORE_ADDR address,
@@ -161,6 +683,35 @@
CHECK_TYPEDEF (type);
+ if (stlprint)
+ {
+ if (!strncmp(type->main_type->name, "std::vector<", 12))
+ {
+ cp_print_stl_vector(type, address, stream, format, recurse, pretty);
+ return;
+ }
+ if (!strncmp(type->main_type->name, "std::list<", 10))
+ {
+ cp_print_stl_list(type, address, stream, format, recurse, pretty);
+ return;
+ }
+ if (!strncmp(type->main_type->name, "std::set<", 9) || !strncmp(type->main_type->name, "std::multiset<", 14))
+ {
+ cp_print_stl_set(type, address, stream, format, recurse, pretty);
+ return;
+ }
+ if (!strncmp(type->main_type->name, "std::map<", 9) || !strncmp(type->main_type->name, "std::multimap<", 14))
+ {
+ cp_print_stl_map(type, address, stream, format, recurse, pretty);
+ return;
+ }
+ if (!strncmp(type->main_type->name, "std::basic_string<char,", 23))
+ {
+ cp_print_stl_string(type, address, stream, format, recurse, pretty);
+ return;
+ }
+ }
+
fprintf_filtered (stream, "{");
len = TYPE_NFIELDS (type);
n_baseclasses = TYPE_N_BASECLASSES (type);
@@ -608,6 +1159,32 @@
show_objectprint,
&setprintlist, &showprintlist);
+ add_setshow_boolean_cmd ("stl", class_support, &stlprint, _("\
+Set printing of STL containers."), _("\
+Show printing of STL containers."), NULL,
+ NULL,
+ show_stlprint,
+ &setprintlist, &showprintlist);
+ stlprint = 0;
+
+ add_setshow_boolean_cmd ("stl_array_compatible", class_support, &stlprintcompatible, _("\
+Set compatibility with classic arrays while printing STL containers."), _("\
+Show compatibility of STL containers printing."), NULL,
+ NULL,
+ show_stlprintcompatible,
+ &setprintlist, &showprintlist);
+ stlprintcompatible = 1;
+
+ add_setshow_uinteger_cmd ("stllimit", class_support,
+ &stllimit,
+ _("Set the maximum number of elements to display in STL containers."),
+ _("Show the maximum number of elements to display in STL containers."),
+ _("Show the maximum number of elements to display in STL containers. Zero is unlimited."),
+ NULL,
+ show_stllimit,
+ &setprintlist, &showprintlist);
+ stllimit = 50;
+
/* Give people the defaults which they are used to. */
objectprint = 0;
vtblprint = 0;