From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 22083 invoked by alias); 25 Jun 2010 06:22:43 -0000 Received: (qmail 22064 invoked by uid 22791); 25 Jun 2010 06:22:39 -0000 X-SWARE-Spam-Status: No, hits=-0.9 required=5.0 tests=AWL,BAYES_00,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,KAM_STOCKGEN,RCVD_IN_DNSWL_NONE,TW_CP,TW_EG,TW_FL,TW_VC X-Spam-Check-By: sourceware.org Received: from mail-px0-f169.google.com (HELO mail-px0-f169.google.com) (209.85.212.169) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Fri, 25 Jun 2010 06:22:30 +0000 Received: by pxi17 with SMTP id 17so4374128pxi.0 for ; Thu, 24 Jun 2010 23:22:29 -0700 (PDT) Received: by 10.142.247.33 with SMTP id u33mr250356wfh.44.1277446949181; Thu, 24 Jun 2010 23:22:29 -0700 (PDT) MIME-Version: 1.0 Received: by 10.143.32.8 with HTTP; Thu, 24 Jun 2010 23:22:09 -0700 (PDT) In-Reply-To: References: <4C181DF2.7030604@vmware.com> From: Hui Zhu Date: Fri, 25 Jun 2010 06:22:00 -0000 Message-ID: Subject: Re: [PATCH] record.c: make prec can save the execution log to a pic file To: gdb-patches ml Content-Type: text/plain; charset=ISO-8859-1 X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2010-06/txt/msg00568.txt.bz2 Update follow cvs-head and make the vcg file more better. Thanks, Hui 2010-06-25 Hui Zhu * 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. (function_list, node_list, edge_list): New struct. (function_list, 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 | 610 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 610 insertions(+) --- a/record.c +++ b/record.c @@ -2549,6 +2549,555 @@ 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 function_list +{ + struct function_list *next; + CORE_ADDR addr; + int fid; +}; +struct node_list +{ + struct node_list *next; + int count; + CORE_ADDR addr; + int showall; + struct symtab *symtab; + int line; + struct minimal_symbol *function; + int fid; +}; +struct edge_list +{ + struct edge_list *next; + int count; + struct node_list *s; + struct node_list *t; + int frame_diff; + int is_return; +}; +struct function_list *function_list = NULL; +struct node_list *node_list = NULL; +struct edge_list *edge_list = NULL; + +static void +record_pic_cleanups (void *data) +{ + FILE *fp = data; + struct function_list *fl, *fl2; + struct node_list *nl, *nl2; + struct edge_list *el, *el2; + + fl = function_list; + while (fl) + { + fl2 = fl; + fl = fl->next; + xfree (fl2); + } + function_list = NULL; + + 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, + "edge: {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 function_list *function_list_tail, *function_list_prev; + 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; + int fid_count = 1; + + /* 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]\" vertical_order:0}\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_sal.pc = 0; + prev_sal.end = 0; + prev_nlp_real.addr = 0; + prev_nlp = &prev_nlp_real; + + /* Create first entry for function_list. */ + function_list = xmalloc (sizeof (struct function_list)); + function_list->next = NULL; + function_list->addr = 0; + function_list->fid = -1; + function_list_tail = function_list; + function_list_prev = function_list; + + /* 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; + CORE_ADDR function_addr; + struct function_list *flp; + + 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; + + /* Get the fid of the nl. */ + if (set_record_pic_type != record_pic_function) + function_addr = get_pc_function_start (addr); + else + function_addr = addr; + if (function_list_prev->addr == function_addr) + nl.fid = function_list_prev->fid; + else + { + for (flp = function_list; flp; flp = flp->next) + { + if (flp->addr == function_addr) + { + nl.fid = flp->fid; + break; + } + } + if (flp == NULL) + { + /* Creat a new entry to function_list. */ + nl.fid = fid_count ++; + flp = xmalloc (sizeof (struct function_list)); + flp->addr = function_addr; + flp->fid = nl.fid; + flp->next = NULL; + function_list_tail->next = flp; + function_list_tail = flp; + } + function_list_prev = flp; + } + + 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\" " + "vertical_order: %d}\n", + node, nl.fid); + 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\" " + "vertical_order: %d}\n", node, + nlp->count, nlp->fid); + record_pic_fputs (fp, line); + } + + record_pic_node (node, 256, gdbarch, set_record_pic_type, edge_list->t); + snprintf (line, 256, + "edge: {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. */ + snprintf (line, 256, "node: {title: \"[END]\" vertical_order: %d}\n", + fid_count); + record_pic_fputs (fp, line); + snprintf (line, 256, + "edge: {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. */ @@ -2745,4 +3294,65 @@ Default is OFF.\n\ When ON, query if PREC cannot record memory change of next instruction."), NULL, NULL, &set_record_cmdlist, &show_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_.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 the nodes that record pic command saved."), _("\ +Show the type of the nodes that record pic command saved."), _("\ +When LINE, each node of vcg file that command record pic saved\n\ +will be a line of the inferior.\n\ +When FUNCTION, each node of vcg file that command record pic saved\n\ +will be a function of the inferior."), + 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 nodes that don't have the function name."), _("\ +Show whether record pic command hide the nodes that don't have the function name."), _("\ +Default is ON.\n\ +When ON, record pic command will hide the nodes that don't have\n\ +the function name.\n\ +When OFF, record pic command will show the nodes that don't have\n\ +the 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 nodes that don't have the source message."), _("\ +Show whether record pic command hide the nodes that don't have the source message."), _("\ +Default is ON.\n\ +When ON, record pic command will hide the nodes that don't have\n\ +the source message.\n\ +When OFF, record pic command will show the nodes that don't have\n\ +the source message."), + NULL, NULL, + &set_record_pic_cmdlist, &show_record_pic_cmdlist); + add_setshow_boolean_cmd ("hide-sameaddr", no_class, + &record_pic_hide_same, _("\ +Set whether record pic command hide the nodes that have the same address node in vcg file."), _("\ +Show whether record pic command hide the nodes that have the same address node in vcg file."), _("\ +Default is ON.\n\ +When ON, record pic command will hide the nodes that have\n\ +the same address node in vcg file.\n\ +And record pic will show the execute count number of this line\n\ +in format \"c:number\".\n\ +When OFF, record pic command will show the nodes that have\n\ +the same address node in vcg file.\n\ +And record pic show the instruction number in format \"i:number\"\n\ +that \"record goto\" support."), + NULL, NULL, + &set_record_pic_cmdlist, &show_record_pic_cmdlist); }