This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[PATCH] record.c: make prec can save the execution log to a pic file
- From: Hui Zhu <teawater at gmail dot com>
- To: gdb-patches ml <gdb-patches at sourceware dot org>
- Date: Wed, 16 Jun 2010 01:12:44 +0800
- Subject: [PATCH] record.c: make prec can save the execution log to a pic file
Hi guys,
Before you read my introduce about this feature, please see the pic
file in http://www.tuhigh.com/photo/p/1600958
This is a inferior execute log pic file. The [BEGIN] and [END] is the
begin and the end this record. The red line means call a function.
The blue line means function return. The black line means simple
execute.
This pic is convented from vcg file to jpg file by graph-easy
(http://search.cpan.org/~tels/Graph-Easy/bin/graph-easy)
To see the vcg file directly. I suggest you to use vcgviewer
(http://code.google.com/p/vcgviewer/).
This patch add some new commands:
record pic
Save the execution log to a vcg file.
set record pic type line/function
Set the type of record pic command saved file.
When LINE, show each line of the inferior.
When FUNCTION, just show the each function.
set record hide-nofunction on/off
Set whether record pic command hide the node that don't have function name.
Default is ON.
When ON, record pic command will hide the node that don't have function name.
When OFF, record pic command will show the node that don't have function name.
set record hide-nosource on/off
Set whether record pic command hide the node that don't have source message.
Default is ON.
When ON, record pic command will hide the node that don't have function name.
When OFF, record pic command will show the node that don't have function name.
set record hide-same on/off
Set whether record pic command hide the node that was shown in before.
Default is ON.
When ON, record pic command will hide the node that was shown in before.
It will show the execute count number of this line in format \"c:number\".
When OFF, record pic command will show the node that was shown in before.
It will show the instruction number in format \"i:number\" that
\"record goto\" support.
If this patch get approved. I will post patch for doc and NEWS.
Thanks,
Hui
2010-06-16 Hui Zhu <teawater@gmail.com>
* record.c (set_record_pic_cmdlist,
show_record_pic_cmdlist): New variables.
(set_record_pic_command,
show_record_pic_command): New functions.
(record_pic_function, record_pic_line, record_pic_enum,
set_record_pic_type, record_pic_hide_nofunction,
record_pic_hide_nosource, record_pic_hide_same): New variables.
(record_pic_fputs): New function.
(node_list, edge_list): New struct.
(node_list, edge_list): New variables.
(record_pic_cleanups, record_pic_node,
record_pic_edge, cmd_record_pic): New functions.
(_initialize_record): Add new commands for record pic.
---
record.c | 537 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 537 insertions(+)
--- a/record.c
+++ b/record.c
@@ -2545,6 +2545,487 @@ cmd_record_save (char *args, int from_tt
recfilename);
}
+/* For "record pic" command. */
+
+static struct cmd_list_element *set_record_pic_cmdlist,
+ *show_record_pic_cmdlist;
+
+static void
+set_record_pic_command (char *args, int from_tty)
+{
+ printf_unfiltered (_("\
+\"set record pic\" must be followed by an apporpriate subcommand.\n"));
+ help_list (set_record_cmdlist, "set record pic ", all_commands, gdb_stdout);
+}
+
+static void
+show_record_pic_command (char *args, int from_tty)
+{
+ cmd_show_list (show_record_pic_cmdlist, from_tty, "");
+}
+
+static const char record_pic_function[] = "function";
+static const char record_pic_line[] = "line";
+static const char *record_pic_enum[] =
+{
+ record_pic_function,
+ record_pic_line,
+ NULL,
+};
+static const char *set_record_pic_type = record_pic_line;
+
+static int record_pic_hide_nofunction = 1;
+static int record_pic_hide_nosource = 1;
+static int record_pic_hide_same = 1;
+
+static void
+record_pic_fputs (FILE *fp, const char *buf)
+{
+ if (fputs (buf, fp) == EOF)
+ error (_("Write to file error."));
+}
+
+struct node_list
+{
+ struct node_list *next;
+ int count;
+ CORE_ADDR addr;
+ int showall;
+ struct symtab *symtab;
+ int line;
+ struct minimal_symbol *function;
+};
+struct edge_list
+{
+ struct edge_list *next;
+ int count;
+ struct node_list *s;
+ struct node_list *t;
+ int frame_diff;
+ int is_return;
+};
+struct node_list *node_list = NULL;
+struct edge_list *edge_list = NULL;
+
+static void
+record_pic_cleanups (void *data)
+{
+ FILE *fp = data;
+ struct node_list *nl, *nl2;
+ struct edge_list *el, *el2;
+
+ nl = node_list;
+ while (nl)
+ {
+ nl2 = nl;
+ nl = nl->next;
+ xfree (nl2);
+ }
+ node_list = NULL;
+
+ el = edge_list;
+ while (el)
+ {
+ el2 = el;
+ el = el->next;
+ xfree (el2);
+ }
+ edge_list = NULL;
+
+ fclose (fp);
+}
+
+static void
+record_pic_node (char *buf, int buf_max, struct gdbarch *gdbarch,
+ const char *type, struct node_list *nlp)
+{
+ if (type == record_pic_function)
+ {
+ snprintf (buf, buf_max, "%s %s %s",
+ (nlp->symtab) ? nlp->symtab->filename : "",
+ (nlp->function) ? SYMBOL_LINKAGE_NAME (nlp->function) : "",
+ (!nlp->function) ? paddress (gdbarch, nlp->addr) : "");
+ }
+ else
+ {
+ if (nlp->showall)
+ {
+ snprintf (buf, buf_max, "%s:%d %s %s", nlp->symtab->filename,
+ nlp->line,
+ (nlp->function) ? SYMBOL_LINKAGE_NAME (nlp->function) : "",
+ paddress (gdbarch, nlp->addr));
+ }
+ else
+ {
+ if (nlp->symtab)
+ snprintf (buf, buf_max, "%s %d %s",
+ (nlp->function) ? SYMBOL_LINKAGE_NAME
(nlp->function) : "",
+ nlp->line, paddress (gdbarch, nlp->addr));
+ else
+ snprintf (buf, buf_max, "%s %s",
+ (nlp->function) ? SYMBOL_LINKAGE_NAME
(nlp->function) : "",
+ paddress (gdbarch, nlp->addr));
+ }
+ }
+}
+
+static void
+record_pic_edge (char *buf, int buf_max, struct edge_list *elp,
+ char *node, char *prev_node)
+{
+ if (elp->frame_diff)
+ {
+ if (elp->is_return)
+ snprintf (buf, buf_max, "edge: {color:blue sourcename: \"%s\" "
+ "targetname: \"%s\"",
+ prev_node, node);
+ else
+ snprintf (buf, buf_max, "edge: {color:red sourcename: \"%s\" "
+ "targetname: \"%s\"",
+ prev_node, node);
+ }
+ else
+ snprintf (buf, buf_max,
+ "nearedge: {sourcename: \"%s\" targetname: \"%s\"",
+ prev_node, node);
+}
+
+/* Save the execution log to a vcg file. */
+
+static void
+cmd_record_pic (char *args, int from_tty)
+{
+ char *recfilename, recfilename_buffer[40];
+ FILE *fp;
+ struct cleanup *old_cleanups, *set_cleanups;
+ struct regcache *regcache;
+ struct gdbarch *gdbarch;
+ struct record_entry *cur_record_list;
+ char prev_node[256], line[256];
+ CORE_ADDR prev_addr;
+ struct frame_id fi, caller_fi, prev_fi, prev_caller_fi;
+ struct edge_list *edge_list_tail = NULL;
+ struct node_list *node_list_tail = NULL;
+ struct symtab_and_line sal, prev_sal;
+ struct node_list *prev_nlp;
+ struct node_list prev_nlp_real;
+
+ /* Check if record target is running. */
+ if (current_target.to_stratum != record_stratum)
+ error (_("This command can only be used with target 'record' \
+or target 'record-core'."));
+
+ if (args && *args)
+ recfilename = args;
+ else
+ {
+ /* Default recfile name is "gdb_record_PID.vcg". */
+ snprintf (recfilename_buffer, sizeof (recfilename_buffer),
+ "gdb_record_%d.vcg", PIDGET (inferior_ptid));
+ recfilename = recfilename_buffer;
+ }
+
+ /* Open the output file. */
+ fp = fopen (recfilename, "wb");
+ if (!fp)
+ error (_("Unable to open file '%s'"), recfilename);
+
+ old_cleanups = make_cleanup (record_pic_cleanups, fp);
+
+ /* Save the current record entry to "cur_record_list". */
+ cur_record_list = record_list;
+
+ /* Get the values of regcache and gdbarch. */
+ regcache = get_current_regcache ();
+ gdbarch = get_regcache_arch (regcache);
+
+ /* Disable the GDB operation record. */
+ set_cleanups = record_gdb_operation_disable_set ();
+
+ /* Reverse execute to the begin of record list. */
+ while (1)
+ {
+ /* Check for beginning and end of log. */
+ if (record_list == &record_first)
+ break;
+
+ record_exec_insn (regcache, gdbarch, record_list);
+
+ if (record_list->prev)
+ record_list = record_list->prev;
+ }
+
+ /* Write out the record log. */
+ /* Write the head. */
+ record_pic_fputs (fp, "graph: {title: \"GDB process record\"\n");
+
+ /* Write the first node. */
+ record_pic_fputs (fp, "node: {title: \"[BEGIN]\"}\n");
+
+ /* Initialization. */
+ snprintf (prev_node, 256, "[BEGIN]");
+ prev_fi = null_frame_id;
+ prev_caller_fi = null_frame_id;
+ prev_addr = 0;
+ prev_sal.symtab = NULL;
+ prev_nlp_real.addr = 0;
+ prev_nlp = &prev_nlp_real;
+
+ /* Save the entries to fp and forward execute to the end of
+ record list. */
+ record_list = &record_first;
+ while (1)
+ {
+ if (record_list->type == record_end)
+ {
+ int frame_diff = 0;
+ CORE_ADDR addr = regcache_read_pc (regcache);
+
+ /* Check if the ADDR is stil in the same line with the
+ prev cycle. */
+ if (prev_sal.symtab
+ && addr >= prev_sal.pc && addr < prev_sal.end)
+ goto exec;
+ sal = find_pc_line (addr, 0);
+
+ if (record_pic_hide_nosource && !sal.symtab)
+ goto exec;
+
+ /* Check if the inferior is in same frame with prev cycle.
+ Check both the current fi and caller fi because the last
+ addr of function is different with current function. */
+ reinit_frame_cache ();
+ fi = get_frame_id (get_current_frame ());
+ caller_fi = frame_unwind_caller_id (get_current_frame ());
+ if (!frame_id_eq (prev_fi, fi)
+ && !frame_id_eq (prev_caller_fi, caller_fi))
+ frame_diff = 1;
+
+ if (set_record_pic_type == record_pic_line || frame_diff)
+ {
+ int is_return = 0;
+ struct node_list *nlp = NULL;
+ struct edge_list *elp = NULL;
+ char node[256];
+ struct minimal_symbol *function;
+
+ /* Get the node addr. */
+ if (set_record_pic_type == record_pic_function)
+ {
+ /* Get the start addr of function. */
+ addr = get_pc_function_start (addr);
+ if (addr == 0)
+ {
+ if (record_pic_hide_nofunction)
+ goto exec;
+ addr = regcache_read_pc (regcache);
+ }
+ }
+ else
+ {
+ /* Get the start addr of line. */
+ if (sal.symtab)
+ addr = sal.pc;
+ }
+
+ function = lookup_minimal_symbol_by_pc (addr);
+ if (!function && record_pic_hide_nofunction)
+ goto exec;
+
+ if (frame_id_eq (fi, prev_caller_fi))
+ is_return = 1;
+
+ if (record_pic_hide_same)
+ {
+ /* Check if addr in node_list. */
+ for (nlp = node_list; nlp; nlp = nlp->next)
+ {
+ if (nlp->addr == addr)
+ {
+ if (!is_return
+ || set_record_pic_type != record_pic_function)
+ nlp->count ++;
+ break;
+ }
+ }
+
+ /* Check if prev_addr and addr in edge_list. */
+ if (nlp)
+ {
+ for (elp = edge_list; elp; elp = elp->next)
+ {
+ if (elp->s->addr == prev_addr &&
elp->t->addr == addr)
+ {
+ elp->count ++;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!nlp)
+ {
+ struct node_list nl;
+
+ nl.addr = addr;
+ if (frame_diff && sal.symtab)
+ nl.showall = 1;
+ else
+ nl.showall = 0;
+ nl.symtab = sal.symtab;
+ nl.line = sal.line;
+ nl.function = function;
+
+ if (record_pic_hide_same)
+ {
+ nlp = xmalloc (sizeof (struct node_list));
+ *nlp = nl;
+ nlp->count = 1;
+
+ /* Add node to node_list. */
+ nlp->next = NULL;
+ if (node_list_tail)
+ node_list_tail->next = nlp;
+ if (node_list == NULL)
+ node_list = nlp;
+ node_list_tail = nlp;
+ }
+ else
+ {
+ /* Draw the node. */
+ record_pic_node (node, 256, gdbarch,
+ set_record_pic_type, &nl);
+ snprintf (line, 256, "%s i:%s", node,
+ pulongest (record_list->u.end.insn_num));
+ strcpy (node, line);
+ snprintf (line, 256, "node: {title: \"%s\"}\n", node);
+ record_pic_fputs (fp, line);
+ }
+ }
+
+ if (!elp)
+ {
+ struct edge_list el;
+
+ el.is_return = is_return;
+ el.frame_diff = frame_diff;
+
+ if (record_pic_hide_same)
+ {
+ elp = xmalloc (sizeof (struct edge_list));
+ *elp = el;
+ elp->s = prev_nlp;
+ elp->t = nlp;
+ elp->count = 1;
+
+ /* Add edge to edge_list. */
+ elp->next = NULL;
+ if (edge_list_tail)
+ edge_list_tail->next = elp;
+ if (edge_list == NULL)
+ edge_list = elp;
+ edge_list_tail = elp;
+ }
+ else
+ {
+ /* Draw the edge. */
+ record_pic_edge (line, 256, &el, node, prev_node);
+ record_pic_fputs (fp, line);
+ record_pic_fputs (fp, " }\n");
+ }
+ }
+
+ if (record_pic_hide_same)
+ prev_nlp = nlp;
+ else
+ snprintf (prev_node, 256, "%s", node);
+ prev_addr = addr;
+ }
+
+ prev_sal = sal;
+ prev_fi = fi;
+ prev_caller_fi = caller_fi;
+ }
+
+exec:
+ /* Execute entry. */
+ record_exec_insn (regcache, gdbarch, record_list);
+
+ if (record_list->next)
+ record_list = record_list->next;
+ else
+ break;
+ }
+
+ if (record_pic_hide_same)
+ {
+ struct node_list *nlp = NULL;
+ struct edge_list *elp = NULL;
+ char node[256];
+
+ for (nlp = node_list; nlp; nlp = nlp->next)
+ {
+ /* Draw the node. */
+ record_pic_node (node, 256, gdbarch, set_record_pic_type, nlp);
+ snprintf (line, 256, "node: {title: \"%s c:%d\"}\n", node,
+ nlp->count);
+ record_pic_fputs (fp, line);
+ }
+
+ record_pic_node (node, 256, gdbarch, set_record_pic_type, edge_list->t);
+ snprintf (line, 256,
+ "nearedge: {color:red sourcename: \"[BEGIN]\" targetname:
\"%s c:%d\"}\n",
+ node, edge_list->count);
+ record_pic_fputs (fp, line);
+ for (elp = edge_list->next; elp; elp = elp->next)
+ {
+ /* Draw the edge. */
+ record_pic_node (prev_node, 256, gdbarch, set_record_pic_type,
+ elp->s);
+ snprintf (line, 256, "%s c:%d", prev_node, elp->s->count);
+ strcpy (prev_node, line);
+ record_pic_node (node, 256, gdbarch, set_record_pic_type,
+ elp->t);
+ snprintf (line, 256, "%s c:%d", node, elp->t->count);
+ strcpy (node, line);
+ record_pic_edge (line, 256, elp, node, prev_node);
+ record_pic_fputs (fp, line);
+ snprintf (line, 256, " label: \"c:%d\"}\n", elp->count);
+ record_pic_fputs (fp, line);
+ }
+ }
+
+ /* Write the last node. */
+ record_pic_fputs (fp, "node: {title: \"[END]\"}\n");
+ snprintf (line, 256,
+ "nearedge: {color:red sourcename: \"%s\" targetname: \"[END]\" }\n",
+ prev_node);
+ record_pic_fputs (fp, line);
+
+ /* Write the tail. */
+ record_pic_fputs (fp, "}\n");
+
+ /* Reverse execute to cur_record_list. */
+ while (1)
+ {
+ /* Check for beginning and end of log. */
+ if (record_list == cur_record_list)
+ break;
+
+ record_exec_insn (regcache, gdbarch, record_list);
+
+ if (record_list->prev)
+ record_list = record_list->prev;
+ }
+
+ do_cleanups (set_cleanups);
+ do_cleanups (old_cleanups);
+
+ /* Succeeded. */
+ printf_filtered (_("Saved file %s with execution log.\n"),
+ recfilename);
+}
+
/* record_goto_insn -- rewind the record log (forward or backward,
depending on DIR) to the given entry, changing the program state
correspondingly. */
@@ -2730,4 +3211,60 @@ record/replay buffer. Zero means unlimi
Restore the program to its state at instruction number N.\n\
Argument is instruction number, as shown by 'info record'."),
&record_cmdlist);
+
+ /* For "record pic" command. */
+ c = add_cmd ("pic", class_obscure, cmd_record_pic,
+ _("Save the execution log to a vcg file.\n\
+Argument is optional filename.\n\
+Default filename is 'gdb_record_<process_id>.vcg'."),
+ &record_cmdlist);
+ set_cmd_completer (c, filename_completer);
+ add_prefix_cmd ("pic", class_support, set_record_pic_command,
+ _("Set record pic options"), &set_record_pic_cmdlist,
+ "set record pic ", 0, &set_record_cmdlist);
+ add_prefix_cmd ("pic", class_support, show_record_pic_command,
+ _("Show record pic options"), &show_record_pic_cmdlist,
+ "show record pic ", 0, &show_record_cmdlist);
+ add_setshow_enum_cmd ("type", no_class,
+ record_pic_enum, &set_record_pic_type, _("\
+Set the type of record pic command saved file."), _("\
+Show the type of record pic command saved file."), _("\
+When LINE, show each line of the inferior.\n\
+When FUNCTION, just show the each function."),
+ NULL, NULL,
+ &set_record_pic_cmdlist, &show_record_pic_cmdlist);
+ add_setshow_boolean_cmd ("hide-nofunction", no_class,
+ &record_pic_hide_nofunction, _("\
+Set whether record pic command hide the node that don't have function
name."), _("\
+Show whether record pic command hide the node that don't have
function name."), _("\
+Default is ON.\n\
+When ON, record pic command will hide the node that don't have\n\
+function name.\n\
+When OFF, record pic command will show the node that don't have\n\
+function name."),
+ NULL, NULL,
+ &set_record_pic_cmdlist, &show_record_pic_cmdlist);
+ add_setshow_boolean_cmd ("hide-nosource", no_class,
+ &record_pic_hide_nosource, _("\
+Set whether record pic command hide the node that don't have source
message."), _("\
+Show whether record pic command hide the node that don't have source
message."), _("\
+Default is ON.\n\
+When ON, record pic command will hide the node that don't have\n\
+function name.\n\
+When OFF, record pic command will show the node that don't have\n\
+function name."),
+ NULL, NULL,
+ &set_record_pic_cmdlist, &show_record_pic_cmdlist);
+ add_setshow_boolean_cmd ("hide-same", no_class,
+ &record_pic_hide_same, _("\
+Set whether record pic command hide the node that was shown in before."), _("\
+Show whether record pic command hide the node that was shown in before."), _("\
+Default is ON.\n\
+When ON, record pic command will hide the node that was shown in before.\n\
+It will show the execute count number of this line in format \"c:number\".\n\
+When OFF, record pic command will show the node that was shown in before.\n\
+It will show the instruction number in format \"i:number\"\n\
+that \"record goto\" support."),
+ NULL, NULL,
+ &set_record_pic_cmdlist, &show_record_pic_cmdlist);
}