From: Michael Snyder Date: Mon, 30 May 2005 22:33:44 +0000 (+0000) Subject: 2005-05-30 Michael Snyder X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=9392aabee136669605b0c532cd75902679249e7f;p=thirdparty%2Fbinutils-gdb.git 2005-05-30 Michael Snyder Add support for writing tracepoint/checkpoint data to a file. * breakpoint.c (bpstat_stop_status): Add hook for interpreting breakpoints as tracepoints, enabling gdb to handle them directly by a "default" fallback method (writing data to a file). * tracepoint.c (default_tracepoint_method): New user-settable mode variable -- tells gdb to use a "default" (fallback) method for collecting tracepoint data. (trace_start_command, trace_stop_command, trace_status_command): Add "default" mode. (tracepoint_event_p, default_do_tracepoints_command): New functions, entry points into default tracepoint collection method. (checkpoint_open, checkpoint_close): Open / close checkpoint file. (checkpoint_command): Drop a checkpoint into checkpoint file. (emit_checkpoint_method1, emit_checkpoint_method2, emit_checkpoint_method3, emit_checkpoint_method4): Experimental methods for writing checkpoint/tracepoint data to a file. (checkpoint_emit_memrange, checkpoint_emit_gregs, checkpoint_emit_stack, checkpoint_emit_id, etc.): Support functions for writing checkpoint/tracepoint data. (get_tracepoint_by_address): New function. (validate_actionline): Add "$stack" pseudo-object for collection. (tracepoint_top_of_stack, tracepoint_size_of_stack): New user-settable variables. (encode_actions): Interpret "$stack" pseudo-object, collect stack. * tracepoint.h (default_trace_method, tracepoint_event_p): Exported interface for default tracepoint method. --- diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 98a42e3adfd..436925a75a2 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,40 @@ +2005-05-30 Michael Snyder + + Add support for writing tracepoint/checkpoint data to a file. + + * breakpoint.c (bpstat_stop_status): Add hook for interpreting + breakpoints as tracepoints, enabling gdb to handle them directly + by a "default" fallback method (writing data to a file). + + * tracepoint.c (default_tracepoint_method): New user-settable + mode variable -- tells gdb to use a "default" (fallback) method + for collecting tracepoint data. + (trace_start_command, trace_stop_command, trace_status_command): + Add "default" mode. + + (tracepoint_event_p, default_do_tracepoints_command): New + functions, entry points into default tracepoint collection method. + + (checkpoint_open, checkpoint_close): Open / close checkpoint file. + (checkpoint_command): Drop a checkpoint into checkpoint file. + + (emit_checkpoint_method1, emit_checkpoint_method2, + emit_checkpoint_method3, emit_checkpoint_method4): Experimental + methods for writing checkpoint/tracepoint data to a file. + + (checkpoint_emit_memrange, checkpoint_emit_gregs, + checkpoint_emit_stack, checkpoint_emit_id, etc.): + Support functions for writing checkpoint/tracepoint data. + + (get_tracepoint_by_address): New function. + (validate_actionline): Add "$stack" pseudo-object for collection. + (tracepoint_top_of_stack, tracepoint_size_of_stack): + New user-settable variables. + (encode_actions): Interpret "$stack" pseudo-object, collect stack. + + * tracepoint.h (default_trace_method, tracepoint_event_p): + Exported interface for default tracepoint method. + 2005-05-29 Eli Zaretskii * config/djgpp/fnchange.lst: Add mappings for linux-ppc-low.c and diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index c8e15d91554..cb11c6a3d8e 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -26,6 +26,7 @@ #include "symtab.h" #include "frame.h" #include "breakpoint.h" +#include "tracepoint.h" /* for default tracepoint method */ #include "gdbtypes.h" #include "expression.h" #include "gdbcore.h" @@ -2841,6 +2842,14 @@ bpstat_stop_status (CORE_ADDR bp_addr, ptid_t ptid, int stopped_by_watchpoint) real_breakpoint = 1; } + /* tracepoint */ + /* OK -- if we put the action here, it means we will + check for a tracepoint if a watchpoint has triggered, + or if we have a breakpoint at the current PC. By + checking now, we will NOT honor the breakpoint's + condition if any. We would do that later. */ + + if (frame_id_p (b->frame_id) && !frame_id_eq (b->frame_id, get_frame_id (get_current_frame ()))) bs->stop = 0; @@ -2880,20 +2889,37 @@ bpstat_stop_status (CORE_ADDR bp_addr, ptid_t ptid, int stopped_by_watchpoint) } else { - /* We will stop here */ - if (b->disposition == disp_disable) - b->enable_state = bp_disabled; - if (b->silent) - bs->print = 0; - bs->commands = b->commands; - if (bs->commands && - (strcmp ("silent", bs->commands->line) == 0 - || (xdb_commands && strcmp ("Q", bs->commands->line) == 0))) + /* tracepoint */ + /* If we put the check here, we can honor the breakpoint's + condition and/or ignore-count and/or thread, if any. */ + + if (default_trace_method && tracepoint_event_p ()) { - bs->commands = bs->commands->next; + /* This breakpoint has been handled as a tracepoint. + Don't stop. */ + bs->stop = 0; bs->print = 0; + /* hit_count -- should we consider this a hit? */ + } + else + { + /* Not tracepoint -- back to your regularly scheduled + breakpoint handling. */ + /* We will stop here */ + if (b->disposition == disp_disable) + b->enable_state = bp_disabled; + if (b->silent) + bs->print = 0; + bs->commands = b->commands; + if (bs->commands && + (strcmp ("silent", bs->commands->line) == 0 + || (xdb_commands && strcmp ("Q", bs->commands->line) == 0))) + { + bs->commands = bs->commands->next; + bs->print = 0; + } + bs->commands = copy_command_lines (bs->commands); } - bs->commands = copy_command_lines (bs->commands); } } /* Print nothing for this entry if we dont stop or if we dont print. */ diff --git a/gdb/infrun.c b/gdb/infrun.c index f0a3a5d61a1..5149cd02da7 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -2062,7 +2062,7 @@ process_event_stop_test: fprintf_unfiltered (gdb_stdlog, "infrun: BPSTATE_WHAT_STOP_NOISY\n"); stop_print_frame = 1; - /* We are about to nuke the step_resume_breakpointt via the + /* We are about to nuke the step_resume_breakpoint via the cleanup chain, so no need to worry about it here. */ stop_stepping (ecs); diff --git a/gdb/tracepoint.c b/gdb/tracepoint.c index bf4a2c519fa..6039e367cae 100644 --- a/gdb/tracepoint.c +++ b/gdb/tracepoint.c @@ -99,8 +99,20 @@ extern void output_command (char *, int); $trace_line : source line of trace frame currently being debugged. $trace_file : source file of trace frame currently being debugged. $tracepoint : tracepoint number of trace frame currently being debugged. + + A number of the methods in this module assume "target remote", + and some of them contain code that belongs in remote.c and + ought to be accessed thru the target vector (so that non-remote + targets could implement them too). + + Therefore this module defines a user-settable mode variable + "default-tracepoint-method", which will override the original + methods and cause a simple-minded fall-back method to be used, + which should work on most if not all targets. + */ +int default_trace_method; /* ======= Important global variables: ======= */ @@ -601,6 +613,22 @@ tracepoint_operation (struct tracepoint *t, int from_tty, } } +/* Utility: Look up a tracepoint in the list, by address. + Returns the first match that's enabled. */ + +struct tracepoint * +get_tracepoint_by_address (CORE_ADDR address) +{ + struct tracepoint *t; + + ALL_TRACEPOINTS (t) + if (t->enabled_p) + if (t->address == address) + return t; + + return NULL; +} + /* Utility: parse a tracepoint number and look it up in the list. If MULTI_P is true, there might be a range of tracepoints in ARG. if OPTIONAL_P is true, then if the argument is missing, the most @@ -963,7 +991,8 @@ validate_actionline (char **line, struct tracepoint *t) { if ((0 == strncasecmp ("reg", p + 1, 3)) || (0 == strncasecmp ("arg", p + 1, 3)) || - (0 == strncasecmp ("loc", p + 1, 3))) + (0 == strncasecmp ("loc", p + 1, 3)) || + (0 == strncasecmp ("stack", p + 1, 5))) { p = strchr (p, ','); continue; @@ -1500,6 +1529,9 @@ free_actions_list (char **actions_list) xfree (actions_list); } +static CORE_ADDR tracepoint_top_of_stack; +static unsigned int tracepoint_size_of_stack = 256; + /* Render all actions into gdb protocol. */ static void encode_actions (struct tracepoint *t, char ***tdp_actions, @@ -1573,6 +1605,28 @@ encode_actions (struct tracepoint *t, char ***tdp_actions, 'L'); action_exp = strchr (action_exp, ','); /* more? */ } + else if (0 == strncasecmp ("$stack", action_exp, 6)) + { + CORE_ADDR lo, hi; + int stacklen; + + /* FIXME: frame pointer? stack_grows_down? */ + lo = read_sp (); + if (tracepoint_top_of_stack != 0) + hi = tracepoint_top_of_stack; + else + hi = lo + tracepoint_size_of_stack; + + stacklen = hi - lo; + + if (tracepoint_top_of_stack != 0 && + stacklen > 10000) /* Arbitrary sanity check. */ + error ("Stack too big: sp = 0x%08lx, TOS = 0x%08lx, check TOS.", + (unsigned long) lo, (unsigned long) hi); + + add_memrange (collect, -1, lo, stacklen); + action_exp = strchr (action_exp, ','); /* more? */ + } else { unsigned long addr, len; @@ -1752,7 +1806,13 @@ trace_start_command (char *args, int from_tty) dont_repeat (); /* Like "run", dangerous to repeat accidentally. */ - if (target_is_remote ()) + if (default_trace_method) + { + /* Default implementation. */ + if (from_tty && info_verbose) + printf_filtered ("Activating trace experiment, default method.\n"); + } + else if (target_is_remote ()) { putpkt ("QTinit"); remote_get_noisy_reply (target_buf, sizeof (target_buf)); @@ -1830,34 +1890,47 @@ trace_start_command (char *args, int from_tty) remote_get_noisy_reply (target_buf, sizeof (target_buf)); if (strcmp (target_buf, "OK")) error (_("Bogus reply from target: %s"), target_buf); - set_traceframe_num (-1); /* All old traceframes invalidated. */ - set_tracepoint_num (-1); - set_traceframe_context (-1); - trace_running_p = 1; - if (deprecated_trace_start_stop_hook) - deprecated_trace_start_stop_hook (1, from_tty); - } else - error (_("Trace can only be run on remote targets.")); + { + error (_("Target does not implement this command (tstart).")); + } + + /* All methods. */ + set_traceframe_num (-1); /* All old traceframes invalidated. */ + set_tracepoint_num (-1); + set_traceframe_context (-1); + trace_running_p = 1; + if (deprecated_trace_start_stop_hook) + deprecated_trace_start_stop_hook (1, from_tty); } /* tstop command */ static void trace_stop_command (char *args, int from_tty) { - if (target_is_remote ()) + if (default_trace_method) + { + /* Default implementation. */ + if (from_tty && info_verbose) + printf_filtered ("Deactivating trace experiment, default method.\n"); + } + else if (target_is_remote ()) { putpkt ("QTStop"); remote_get_noisy_reply (target_buf, sizeof (target_buf)); if (strcmp (target_buf, "OK")) error (_("Bogus reply from target: %s"), target_buf); - trace_running_p = 0; - if (deprecated_trace_start_stop_hook) - deprecated_trace_start_stop_hook (0, from_tty); } else - error (_("Trace can only be run on remote targets.")); + { + error (_("Target does not implement this command (tstop).")); + } + + /* All methods. */ + trace_running_p = 0; + if (deprecated_trace_start_stop_hook) + deprecated_trace_start_stop_hook (0, from_tty); } unsigned long trace_running_p; @@ -1866,7 +1939,11 @@ unsigned long trace_running_p; static void trace_status_command (char *args, int from_tty) { - if (target_is_remote ()) + if (default_trace_method) + { + printf_filtered ("Trace is %s.\n", trace_running_p ? "on" : "off"); + } + else if (target_is_remote ()) { putpkt ("qTStatus"); remote_get_noisy_reply (target_buf, sizeof (target_buf)); @@ -1879,7 +1956,9 @@ trace_status_command (char *args, int from_tty) trace_running_p = (target_buf[1] == '1'); } else - error (_("Trace can only be run on remote targets.")); + { + error (_("Target does not implement this command (tstatus).")); + } } /* Worker function for the various flavors of the tfind command. */ @@ -2013,6 +2092,9 @@ trace_find_command (char *args, int from_tty) { /* this should only be called with a numeric argument */ int frameno = -1; + if (default_trace_method) + error ("default trace find"); + if (target_is_remote ()) { if (deprecated_trace_find_hook) @@ -2536,6 +2618,9 @@ trace_dump_command (char *args, int from_tty) int stepping_actions = 0; int stepping_frame = 0; + if (default_trace_method) + error ("default trace dump"); + if (!target_is_remote ()) { error (_("Trace can only be run on remote targets.")); @@ -2669,6 +2754,425 @@ get_traceframe_number (void) return traceframe_number; } +static FILE *checkpoint_file; +static int tracepoint_method; + +static void +emit_checkpoint_method1 (struct tracepoint *t) +{ + /* This extremely concise method emits only a few essential registers. + Think of it as an execution trace. */ + if (checkpoint_file) + { + /* Method 1: Emit minimal subset of registers. */ + fputs ("T00", checkpoint_file); + /* FIXME: print 'em like addresses. */ + if (PC_REGNUM >= 0) + { + fputs (int_string ((LONGEST) PC_REGNUM, 16, 0, 0, 0), + checkpoint_file); + fputc (':', checkpoint_file); + fputs (paddr (read_pc ()), checkpoint_file); + fputc (';', checkpoint_file); + } + if (SP_REGNUM >= 0) + { + fputs (int_string ((LONGEST) SP_REGNUM, 16, 0, 0, 0), + checkpoint_file); + fputc (':', checkpoint_file); + fputs (paddr (read_sp ()), checkpoint_file); + fputc (';', checkpoint_file); + } + if (DEPRECATED_FP_REGNUM >= 0) + { + fputs (int_string ((LONGEST) DEPRECATED_FP_REGNUM, 16, 0, 0, 0), + checkpoint_file); + fputc (':', checkpoint_file); + fputs (paddr ((CORE_ADDR) read_register (DEPRECATED_FP_REGNUM)), + checkpoint_file); + fputc (';', checkpoint_file); + } + if (PS_REGNUM >= 0) + { + fputs (int_string ((LONGEST) PS_REGNUM, 16, 0, 0, 0), + checkpoint_file); + fputc (':', checkpoint_file); + fputs (paddr ((CORE_ADDR) read_register (PS_REGNUM)), + checkpoint_file); + fputc (';', checkpoint_file); + } +#define I386_EBP_REGNUM 5 /* mumble, mutter... */ + +#if defined (I386_EBP_REGNUM) + fputs (int_string ((LONGEST) I386_EBP_REGNUM, 16, 0, 0, 0), + checkpoint_file); + fputc (':', checkpoint_file); + fputs (paddr ((CORE_ADDR) read_register (I386_EBP_REGNUM)), + checkpoint_file); + fputc (';', checkpoint_file); +#endif + fputc ('\n', checkpoint_file); + } +} + +static void +checkpoint_emit_id (int id, FILE *file) +{ + fprintf (file, "\nCHECKPOINT ID %d\n", id); +} + +static void +checkpoint_emit_sequential_id (FILE *file) +{ + static int cpnum; + checkpoint_emit_id (cpnum++, file); +} + +static void +checkpoint_emit_gregs (FILE *file) +{ + /* FIXME: assume the g packet includes regs 0 to NUM_REGS-1. */ + unsigned char binbuf[32]; + char ascbuf[128]; + int i; + + for (i = 0; i < NUM_REGS; i++) + { + if (!register_cached (i)) + target_fetch_registers (i); + regcache_raw_collect (current_regcache, i, binbuf); + *(mem2hex (binbuf, ascbuf, register_size (current_gdbarch, i))) = '\0'; + fputs (ascbuf, file); + } + fputc ('\n', file); +} + +static void +checkpoint_emit_memrange (CORE_ADDR lo, CORE_ADDR hi, FILE *file) +{ + unsigned char *buf; + unsigned int i, len = hi - lo; + + fprintf (file, "S3%02x%s", (len + 5) > 255 ? 255 : (len + 5), paddr (lo)); + + /* FIXME: cleanup. */ + buf = xmalloc (len); + target_read_memory (lo, buf, len); + + /* FIXME: byte order? */ + for (i = 0; i < len; i++) + { + if ((i > 0) && ((i % 256) == 0)) + fprintf (file, "xx\nS3%02x%s", + (len + 5 - i) > 255 ? 255 : (len + 5 - i), paddr (lo + i)); + fprintf (file, "%02x", buf[i]); + } + xfree (buf); + + /* We don't really care about the checksum. */ + fputs ("xx\n", file); +} + +static void +checkpoint_emit_stack (FILE *file) +{ + CORE_ADDR lo, hi; + int stacklen; + + /* FIXME: frame pointer? stack_grows_down? */ + lo = read_sp (); + if (tracepoint_top_of_stack != 0) + hi = tracepoint_top_of_stack; + else + hi = lo + tracepoint_size_of_stack; + + stacklen = hi - lo; + + if (tracepoint_top_of_stack != 0 && + stacklen > 10000) /* Arbitrary sanity check. */ + error ("Stack too big: sp = 0x%08lx, TOS = 0x%08lx, check TOS.", + (unsigned long) lo, (unsigned long) hi); + + checkpoint_emit_memrange (lo, hi, file); +} + +static void +emit_checkpoint_method2 (struct tracepoint *t) +{ + /* This relatively concise method emits the standard "g packet" + register set. It's still mostly an execution trace, but any + variables in registers will be captured too. */ + + checkpoint_emit_sequential_id (checkpoint_file); + fputs ("REGISTERS\n", checkpoint_file); + checkpoint_emit_gregs (checkpoint_file); +} + +static void +emit_checkpoint_method3 (struct tracepoint *t) +{ + /* This medium-complexity method emits the standard "g packet" + register set, plus a configurable chunk of the machine stack. + User may either specify the size of the chunk (default 512), + or the top of the stack. By this means, we hopefully capture + all local variables and arguments, and can do backtrace. */ + + checkpoint_emit_sequential_id (checkpoint_file); + fputs ("REGISTERS\n", checkpoint_file); + checkpoint_emit_gregs (checkpoint_file); + + /* And here's where memory goes. */ + fputs ("MEMORY\n", checkpoint_file); + checkpoint_emit_stack (checkpoint_file); +} + +static void +emit_checkpoint_method4 (struct tracepoint *t) +{ + /* This method will not immediately lend itself to the "drop" + command, because it wants to interpret the "actions" of the + actual tracepoint. "Drop" doesn't have an actual tracepoint + associated with it (unles we give it an argument... Hmmm!) */ + + char **tdp_actions, **stepping_actions; + int memory_flag = 0; + int i; + + if (checkpoint_file) + { + encode_actions (t, &tdp_actions, &stepping_actions); + if (tdp_actions == NULL) + { + /* Fixme -- no actions -- should just lay down a + "method 1" tracepoint, but rda doesn't know how + to read those yet, so fall back to method2. */ + emit_checkpoint_method2 (t); + return; + } + + checkpoint_emit_sequential_id (checkpoint_file); + for (i = 0; tdp_actions[i]; i++) + { + CORE_ADDR offset, base; + int regnum, len; + char *ptr; + + if (tdp_actions[i][0] == 'R') + { + /* We want some regs. Let's just take 'em all. */ + fputs ("REGISTERS\n", checkpoint_file); + checkpoint_emit_gregs (checkpoint_file); + } + do_memrange: + if (tdp_actions[i][0] == 'M') + { + /* A mem range. */ + if (memory_flag == 0) + { + fputs ("MEMORY\n", checkpoint_file); + memory_flag++; + } + if (strncmp (tdp_actions[i] + 1, "FFFF", 4) == 0) + { + /* Absolute memory range. */ + if ((ptr = strchr (tdp_actions[i], ',')) != NULL) + { + /* FIXME strtoull? */ + base = strtoul (++ptr, NULL, 16); + if ((ptr = strchr (ptr, ',')) != NULL) + { + len = strtoul (++ptr, NULL, 16); + checkpoint_emit_memrange (base, base + len, + checkpoint_file); + } + } + } + else + { + /* Register-based memory range. */ + regnum = strtol (tdp_actions[i] + 1, NULL, 16); + base = read_register (regnum); + if ((ptr = strchr (tdp_actions[i], ',')) != NULL) + { + offset = strtoul (++ptr, NULL, 16); + if ((ptr = strchr (ptr, ',')) != NULL) + { + len = strtol (++ptr, NULL, 16); + checkpoint_emit_memrange (base + offset, + base + offset + len, + checkpoint_file); + } + } + } + if ((ptr = strchr (ptr, 'M')) != NULL) + { + tdp_actions[i] = ptr; + goto do_memrange; + } + } + } + } +} + +static void +tracepoint_set_tos_command (char *args, int from_tty) +{ + if (args == NULL || *args == '\0') + error ("Argument required: address of TOS"); + + /* Fixme -- expression. */ + tracepoint_top_of_stack = parse_and_eval_address (args); +} + +static void +tracepoint_set_size_of_stack_command (char *args, int from_tty) +{ + if (args == NULL || *args == '\0') + error ("Argument required: size of desired stack memrange."); + + /* Fixme -- expression. */ + tracepoint_size_of_stack = parse_and_eval_address (args); +} + +static void +default_do_tracepoints_command (char *args, int from_tty) +{ + CORE_ADDR stop_pc; + struct tracepoint *t; + + if (checkpoint_file == NULL) + error ("You must open a checkpoint file first. Type help open-checkpoint."); + + if (tracepoint_chain == NULL) + error ("No tracepoints defined."); + + if (!target_has_execution) + error ("Target not running."); + + if (from_tty && info_verbose) + printf_filtered ("Do default tracepoint actions.\n"); + + /* OK, find a tracepoint that matches the current pc. + FIXME handle while-stepping. */ + stop_pc = read_pc (); + ALL_TRACEPOINTS (t) + if (t->address == stop_pc) + { + if (from_tty && info_verbose) + printf_filtered ("Will collect data for tracepoint %d\n", t->number); + switch (tracepoint_method) + { + case 1: emit_checkpoint_method1 (t); break; + case 2: emit_checkpoint_method2 (t); break; + case 3: emit_checkpoint_method3 (t); break; + case 4: emit_checkpoint_method4 (t); break; + default: emit_checkpoint_method4 (t); break; + } + } +} + +/* boolean tracepoint_event_p -- + + Decides whether to handle the current stop event as a + tracepoint event. If so, handles the event (collects + the tracepoint data) and returns true. GDB will not stop. + + Otherwise the event must be handled by someone else, + eg. as a breakpoint event. */ + +int +tracepoint_event_p () +{ + struct tracepoint *t; + + /* Can't be ours if default trace method is not on. */ + if (!default_trace_method) + return 0; + + /* Can't be ours if a trace experiment is not running. */ + if (!trace_running_p) + return 0; + + /* Can't be ours if we don't have an open tracepoint file. */ + if (checkpoint_file == NULL) + return 0; + + /* Can't be ours if there's no (enabled) tracepoint here. */ + if ((t = get_tracepoint_by_address (read_pc ())) == NULL) + return 0; + + /* OK, it's ours, let's handle it. */ + switch (tracepoint_method) + { + case 1: emit_checkpoint_method1 (t); break; + case 2: emit_checkpoint_method2 (t); break; + case 3: emit_checkpoint_method3 (t); break; + case 4: emit_checkpoint_method4 (t); break; + default: emit_checkpoint_method4 (t); break; + } + /* Tell caller we've handled it. */ + return 1; +} + +/* Open file for checkpoints (tracepoint frames). */ +static void +checkpoint_open (char *args, int from_tty) +{ + if (args == NULL || *args == '\0') + error ("Argument required: checkpoint file name."); + + if ((checkpoint_file = fopen (args, "w")) == NULL) + error ("Could not open checkpoint file %s for output.", args); + + fprintf (checkpoint_file, "CHECKPOINT FILE\n"); + if (from_tty) + fprintf_filtered (gdb_stdout, "File '%s' open for checkpoints.\n", + args); +} + +/* Close file for checkpoints (tracepoint frames). */ +static void +checkpoint_close (char *unused, int from_tty) +{ + if (checkpoint_file == NULL) + error ("No checkpoint file is open."); + + fclose (checkpoint_file); + if (from_tty) + fprintf_filtered (gdb_stdout, "Checkpoint file closed.\n"); +} + +/* "Drop" a checkpoint into the checkpoint file. */ +static void +checkpoint_command (char *args, int from_tty) +{ + if (checkpoint_file) + { + switch (tracepoint_method) + { + case 1: emit_checkpoint_method1 (NULL); break; + case 2: emit_checkpoint_method2 (NULL); break; + case 3: emit_checkpoint_method3 (NULL); break; + case 4: + if (args && *args) + { + struct tracepoint *t = get_tracepoint_by_number (&args, 0, 0); + + if (t) + { + emit_checkpoint_method4 (t); + break; + } + } + error ("Must specify a tracepoint ID for method 4."); + break; + default: emit_checkpoint_method3 (NULL); break; + } + } + else + error ("You must open a checkpoint file."); +} + /* module initialization */ void @@ -2676,6 +3180,56 @@ _initialize_tracepoint (void) { struct cmd_list_element *c; + add_setshow_boolean_cmd ("default-tracepoint-method", class_trace, + &default_trace_method, _("\ +Set whether gdb will use a brute-force tracepoint method."), _("\ +Show whether gdb will use a brute-force tracepoint method."), _("\ +When this is set, tracepoint data will actually be collected\n\ +by gdb, and not by the target. Obviously this method is far\n\ +more intrusive and time consuming than the ideal."), + NULL, NULL, &setlist, &showlist); + + add_setshow_integer_cmd ("tracepoint-method", class_trace, + &tracepoint_method, _("\ +Set the method for saving a tracepoint."), _("\ +Show the method for saving a tracepoint."), _("\ +Method 1, save only a minimal subset of registers.\n\ +Method 2, save all of the registers (but not pseudo-registers).\n\ +Method 3, save all registers and the machine stack\n\ + (see tos and size-of-stack).\n\ +Method 4, use tracepoints to determine what to save.\n\ +Method 5 etc. TBD."), + NULL, NULL, &setlist, &showlist); + + add_com ("default-do-tracepoints", class_trace, + default_do_tracepoints_command, "Collect tracepoint data."); + + c = add_com ("open-checkpoint", class_trace, checkpoint_open, "\ +Open output file for checkpoints.\n\ +Argument is filename."); + set_cmd_completer (c, filename_completer); + + c = add_com ("close-checkpoint", class_trace, checkpoint_close, "\ +Close checkpoint file.\n\ +No arguments, since only one checkpoint file may be open at a time."); + set_cmd_completer (c, noop_completer); + + c = add_com ("drop-checkpoint", class_trace, checkpoint_command, "\ +Drop a checkpoint.\n\ +Best not ask what good it will do..."); + set_cmd_completer (c, noop_completer); + + c = add_com ("tos", class_trace, tracepoint_set_tos_command, "\ +Set TOS so that checkpoints can save the stack."); + set_cmd_completer (c, noop_completer); + + c = add_com ("size-of-stack", class_trace, + tracepoint_set_size_of_stack_command, "\ +Set size of stack to be saved at a checkpoint."); + set_cmd_completer (c, noop_completer); + + /* End checkpoint stuff. */ + tracepoint_chain = 0; tracepoint_count = 0; traceframe_number = -1; diff --git a/gdb/tracepoint.h b/gdb/tracepoint.h index fc7a705a341..ab57895f322 100644 --- a/gdb/tracepoint.h +++ b/gdb/tracepoint.h @@ -21,6 +21,14 @@ #if !defined (TRACEPOINT_H) #define TRACEPOINT_H 1 +/* Exported interface: */ + +extern int default_trace_method; +extern int tracepoint_event_p (void); + +/* Most of what follows is not meant for export. + They're just forward declarations for internal use in tracepoint.c. */ + /* The data structure for an action: */ struct action_line {