|  | /* Tracing functionality for remote targets in custom GDB protocol | 
|  |  | 
|  | Copyright (C) 1997-2022 Free Software Foundation, Inc. | 
|  |  | 
|  | This file is part of GDB. | 
|  |  | 
|  | 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 3 of the License, or | 
|  | (at your option) any later version. | 
|  |  | 
|  | This program is distributed in the hope that it will be useful, | 
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | GNU General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License | 
|  | along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  | #include "defs.h" | 
|  | #include "arch-utils.h" | 
|  | #include "symtab.h" | 
|  | #include "frame.h" | 
|  | #include "gdbtypes.h" | 
|  | #include "expression.h" | 
|  | #include "gdbcmd.h" | 
|  | #include "value.h" | 
|  | #include "target.h" | 
|  | #include "target-dcache.h" | 
|  | #include "language.h" | 
|  | #include "inferior.h" | 
|  | #include "breakpoint.h" | 
|  | #include "tracepoint.h" | 
|  | #include "linespec.h" | 
|  | #include "regcache.h" | 
|  | #include "completer.h" | 
|  | #include "block.h" | 
|  | #include "dictionary.h" | 
|  | #include "observable.h" | 
|  | #include "user-regs.h" | 
|  | #include "valprint.h" | 
|  | #include "gdbcore.h" | 
|  | #include "objfiles.h" | 
|  | #include "filenames.h" | 
|  | #include "gdbthread.h" | 
|  | #include "stack.h" | 
|  | #include "remote.h" | 
|  | #include "source.h" | 
|  | #include "ax.h" | 
|  | #include "ax-gdb.h" | 
|  | #include "memrange.h" | 
|  | #include "cli/cli-utils.h" | 
|  | #include "probe.h" | 
|  | #include "gdbsupport/filestuff.h" | 
|  | #include "gdbsupport/rsp-low.h" | 
|  | #include "tracefile.h" | 
|  | #include "location.h" | 
|  | #include <algorithm> | 
|  | #include "cli/cli-style.h" | 
|  | #include "expop.h" | 
|  | #include "gdbsupport/buildargv.h" | 
|  |  | 
|  | #include <unistd.h> | 
|  |  | 
|  | /* Maximum length of an agent aexpression. | 
|  | This accounts for the fact that packets are limited to 400 bytes | 
|  | (which includes everything -- including the checksum), and assumes | 
|  | the worst case of maximum length for each of the pieces of a | 
|  | continuation packet. | 
|  |  | 
|  | NOTE: expressions get mem2hex'ed otherwise this would be twice as | 
|  | large.  (400 - 31)/2 == 184 */ | 
|  | #define MAX_AGENT_EXPR_LEN	184 | 
|  |  | 
|  | /* | 
|  | Tracepoint.c: | 
|  |  | 
|  | This module defines the following debugger commands: | 
|  | trace            : set a tracepoint on a function, line, or address. | 
|  | info trace       : list all debugger-defined tracepoints. | 
|  | delete trace     : delete one or more tracepoints. | 
|  | enable trace     : enable one or more tracepoints. | 
|  | disable trace    : disable one or more tracepoints. | 
|  | actions          : specify actions to be taken at a tracepoint. | 
|  | passcount        : specify a pass count for a tracepoint. | 
|  | tstart           : start a trace experiment. | 
|  | tstop            : stop a trace experiment. | 
|  | tstatus          : query the status of a trace experiment. | 
|  | tfind            : find a trace frame in the trace buffer. | 
|  | tdump            : print everything collected at the current tracepoint. | 
|  | save-tracepoints : write tracepoint setup into a file. | 
|  |  | 
|  | This module defines the following user-visible debugger variables: | 
|  | $trace_frame : sequence number of trace frame currently being debugged. | 
|  | $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. | 
|  | */ | 
|  |  | 
|  |  | 
|  | /* ======= Important global variables: ======= */ | 
|  |  | 
|  | /* The list of all trace state variables.  We don't retain pointers to | 
|  | any of these for any reason - API is by name or number only - so it | 
|  | works to have a vector of objects.  */ | 
|  |  | 
|  | static std::vector<trace_state_variable> tvariables; | 
|  |  | 
|  | /* The next integer to assign to a variable.  */ | 
|  |  | 
|  | static int next_tsv_number = 1; | 
|  |  | 
|  | /* Number of last traceframe collected.  */ | 
|  | static int traceframe_number; | 
|  |  | 
|  | /* Tracepoint for last traceframe collected.  */ | 
|  | static int tracepoint_number; | 
|  |  | 
|  | /* The traceframe info of the current traceframe.  NULL if we haven't | 
|  | yet attempted to fetch it, or if the target does not support | 
|  | fetching this object, or if we're not inspecting a traceframe | 
|  | presently.  */ | 
|  | static traceframe_info_up current_traceframe_info; | 
|  |  | 
|  | /* Tracing command lists.  */ | 
|  | static struct cmd_list_element *tfindlist; | 
|  |  | 
|  | /* List of expressions to collect by default at each tracepoint hit.  */ | 
|  | std::string default_collect; | 
|  |  | 
|  | static bool disconnected_tracing; | 
|  |  | 
|  | /* This variable controls whether we ask the target for a linear or | 
|  | circular trace buffer.  */ | 
|  |  | 
|  | static bool circular_trace_buffer; | 
|  |  | 
|  | /* This variable is the requested trace buffer size, or -1 to indicate | 
|  | that we don't care and leave it up to the target to set a size.  */ | 
|  |  | 
|  | static int trace_buffer_size = -1; | 
|  |  | 
|  | /* Textual notes applying to the current and/or future trace runs.  */ | 
|  |  | 
|  | static std::string trace_user; | 
|  |  | 
|  | /* Textual notes applying to the current and/or future trace runs.  */ | 
|  |  | 
|  | static std::string trace_notes; | 
|  |  | 
|  | /* Textual notes applying to the stopping of a trace.  */ | 
|  |  | 
|  | static std::string trace_stop_notes; | 
|  |  | 
|  | /* support routines */ | 
|  |  | 
|  | struct collection_list; | 
|  | static char *mem2hex (gdb_byte *, char *, int); | 
|  |  | 
|  | static counted_command_line all_tracepoint_actions (struct breakpoint *); | 
|  |  | 
|  | static struct trace_status trace_status; | 
|  |  | 
|  | const char *stop_reason_names[] = { | 
|  | "tunknown", | 
|  | "tnotrun", | 
|  | "tstop", | 
|  | "tfull", | 
|  | "tdisconnected", | 
|  | "tpasscount", | 
|  | "terror" | 
|  | }; | 
|  |  | 
|  | struct trace_status * | 
|  | current_trace_status (void) | 
|  | { | 
|  | return &trace_status; | 
|  | } | 
|  |  | 
|  | /* Free and clear the traceframe info cache of the current | 
|  | traceframe.  */ | 
|  |  | 
|  | static void | 
|  | clear_traceframe_info (void) | 
|  | { | 
|  | current_traceframe_info = NULL; | 
|  | } | 
|  |  | 
|  | /* Set traceframe number to NUM.  */ | 
|  | static void | 
|  | set_traceframe_num (int num) | 
|  | { | 
|  | traceframe_number = num; | 
|  | set_internalvar_integer (lookup_internalvar ("trace_frame"), num); | 
|  | } | 
|  |  | 
|  | /* Set tracepoint number to NUM.  */ | 
|  | static void | 
|  | set_tracepoint_num (int num) | 
|  | { | 
|  | tracepoint_number = num; | 
|  | set_internalvar_integer (lookup_internalvar ("tracepoint"), num); | 
|  | } | 
|  |  | 
|  | /* Set externally visible debug variables for querying/printing | 
|  | the traceframe context (line, function, file).  */ | 
|  |  | 
|  | static void | 
|  | set_traceframe_context (frame_info_ptr trace_frame) | 
|  | { | 
|  | CORE_ADDR trace_pc; | 
|  | struct symbol *traceframe_fun; | 
|  | symtab_and_line traceframe_sal; | 
|  |  | 
|  | /* Save as globals for internal use.  */ | 
|  | if (trace_frame != NULL | 
|  | && get_frame_pc_if_available (trace_frame, &trace_pc)) | 
|  | { | 
|  | traceframe_sal = find_pc_line (trace_pc, 0); | 
|  | traceframe_fun = find_pc_function (trace_pc); | 
|  |  | 
|  | /* Save linenumber as "$trace_line", a debugger variable visible to | 
|  | users.  */ | 
|  | set_internalvar_integer (lookup_internalvar ("trace_line"), | 
|  | traceframe_sal.line); | 
|  | } | 
|  | else | 
|  | { | 
|  | traceframe_fun = NULL; | 
|  | set_internalvar_integer (lookup_internalvar ("trace_line"), -1); | 
|  | } | 
|  |  | 
|  | /* Save func name as "$trace_func", a debugger variable visible to | 
|  | users.  */ | 
|  | if (traceframe_fun == NULL | 
|  | || traceframe_fun->linkage_name () == NULL) | 
|  | clear_internalvar (lookup_internalvar ("trace_func")); | 
|  | else | 
|  | set_internalvar_string (lookup_internalvar ("trace_func"), | 
|  | traceframe_fun->linkage_name ()); | 
|  |  | 
|  | /* Save file name as "$trace_file", a debugger variable visible to | 
|  | users.  */ | 
|  | if (traceframe_sal.symtab == NULL) | 
|  | clear_internalvar (lookup_internalvar ("trace_file")); | 
|  | else | 
|  | set_internalvar_string (lookup_internalvar ("trace_file"), | 
|  | symtab_to_filename_for_display (traceframe_sal.symtab)); | 
|  | } | 
|  |  | 
|  | /* Create a new trace state variable with the given name.  */ | 
|  |  | 
|  | struct trace_state_variable * | 
|  | create_trace_state_variable (const char *name) | 
|  | { | 
|  | tvariables.emplace_back (name, next_tsv_number++); | 
|  | return &tvariables.back (); | 
|  | } | 
|  |  | 
|  | /* Look for a trace state variable of the given name.  */ | 
|  |  | 
|  | struct trace_state_variable * | 
|  | find_trace_state_variable (const char *name) | 
|  | { | 
|  | for (trace_state_variable &tsv : tvariables) | 
|  | if (tsv.name == name) | 
|  | return &tsv; | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /* Look for a trace state variable of the given number.  Return NULL if | 
|  | not found.  */ | 
|  |  | 
|  | struct trace_state_variable * | 
|  | find_trace_state_variable_by_number (int number) | 
|  | { | 
|  | for (trace_state_variable &tsv : tvariables) | 
|  | if (tsv.number == number) | 
|  | return &tsv; | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | static void | 
|  | delete_trace_state_variable (const char *name) | 
|  | { | 
|  | for (auto it = tvariables.begin (); it != tvariables.end (); it++) | 
|  | if (it->name == name) | 
|  | { | 
|  | gdb::observers::tsv_deleted.notify (&*it); | 
|  | tvariables.erase (it); | 
|  | return; | 
|  | } | 
|  |  | 
|  | warning (_("No trace variable named \"$%s\", not deleting"), name); | 
|  | } | 
|  |  | 
|  | /* Throws an error if NAME is not valid syntax for a trace state | 
|  | variable's name.  */ | 
|  |  | 
|  | void | 
|  | validate_trace_state_variable_name (const char *name) | 
|  | { | 
|  | const char *p; | 
|  |  | 
|  | if (*name == '\0') | 
|  | error (_("Must supply a non-empty variable name")); | 
|  |  | 
|  | /* All digits in the name is reserved for value history | 
|  | references.  */ | 
|  | for (p = name; isdigit (*p); p++) | 
|  | ; | 
|  | if (*p == '\0') | 
|  | error (_("$%s is not a valid trace state variable name"), name); | 
|  |  | 
|  | for (p = name; isalnum (*p) || *p == '_'; p++) | 
|  | ; | 
|  | if (*p != '\0') | 
|  | error (_("$%s is not a valid trace state variable name"), name); | 
|  | } | 
|  |  | 
|  | /* The 'tvariable' command collects a name and optional expression to | 
|  | evaluate into an initial value.  */ | 
|  |  | 
|  | static void | 
|  | trace_variable_command (const char *args, int from_tty) | 
|  | { | 
|  | LONGEST initval = 0; | 
|  | struct trace_state_variable *tsv; | 
|  | const char *name_start, *p; | 
|  |  | 
|  | if (!args || !*args) | 
|  | error_no_arg (_("Syntax is $NAME [ = EXPR ]")); | 
|  |  | 
|  | /* Only allow two syntaxes; "$name" and "$name=value".  */ | 
|  | p = skip_spaces (args); | 
|  |  | 
|  | if (*p++ != '$') | 
|  | error (_("Name of trace variable should start with '$'")); | 
|  |  | 
|  | name_start = p; | 
|  | while (isalnum (*p) || *p == '_') | 
|  | p++; | 
|  | std::string name (name_start, p - name_start); | 
|  |  | 
|  | p = skip_spaces (p); | 
|  | if (*p != '=' && *p != '\0') | 
|  | error (_("Syntax must be $NAME [ = EXPR ]")); | 
|  |  | 
|  | validate_trace_state_variable_name (name.c_str ()); | 
|  |  | 
|  | if (*p == '=') | 
|  | initval = value_as_long (parse_and_eval (++p)); | 
|  |  | 
|  | /* If the variable already exists, just change its initial value.  */ | 
|  | tsv = find_trace_state_variable (name.c_str ()); | 
|  | if (tsv) | 
|  | { | 
|  | if (tsv->initial_value != initval) | 
|  | { | 
|  | tsv->initial_value = initval; | 
|  | gdb::observers::tsv_modified.notify (tsv); | 
|  | } | 
|  | gdb_printf (_("Trace state variable $%s " | 
|  | "now has initial value %s.\n"), | 
|  | tsv->name.c_str (), plongest (tsv->initial_value)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* Create a new variable.  */ | 
|  | tsv = create_trace_state_variable (name.c_str ()); | 
|  | tsv->initial_value = initval; | 
|  |  | 
|  | gdb::observers::tsv_created.notify (tsv); | 
|  |  | 
|  | gdb_printf (_("Trace state variable $%s " | 
|  | "created, with initial value %s.\n"), | 
|  | tsv->name.c_str (), plongest (tsv->initial_value)); | 
|  | } | 
|  |  | 
|  | static void | 
|  | delete_trace_variable_command (const char *args, int from_tty) | 
|  | { | 
|  | if (args == NULL) | 
|  | { | 
|  | if (query (_("Delete all trace state variables? "))) | 
|  | tvariables.clear (); | 
|  | dont_repeat (); | 
|  | gdb::observers::tsv_deleted.notify (NULL); | 
|  | return; | 
|  | } | 
|  |  | 
|  | gdb_argv argv (args); | 
|  |  | 
|  | for (char *arg : argv) | 
|  | { | 
|  | if (*arg == '$') | 
|  | delete_trace_state_variable (arg + 1); | 
|  | else | 
|  | warning (_("Name \"%s\" not prefixed with '$', ignoring"), arg); | 
|  | } | 
|  |  | 
|  | dont_repeat (); | 
|  | } | 
|  |  | 
|  | void | 
|  | tvariables_info_1 (void) | 
|  | { | 
|  | struct ui_out *uiout = current_uiout; | 
|  |  | 
|  | /* Try to acquire values from the target.  */ | 
|  | for (trace_state_variable &tsv : tvariables) | 
|  | tsv.value_known | 
|  | = target_get_trace_state_variable_value (tsv.number, &tsv.value); | 
|  |  | 
|  | { | 
|  | ui_out_emit_table table_emitter (uiout, 3, tvariables.size (), | 
|  | "trace-variables"); | 
|  | uiout->table_header (15, ui_left, "name", "Name"); | 
|  | uiout->table_header (11, ui_left, "initial", "Initial"); | 
|  | uiout->table_header (11, ui_left, "current", "Current"); | 
|  |  | 
|  | uiout->table_body (); | 
|  |  | 
|  | for (const trace_state_variable &tsv : tvariables) | 
|  | { | 
|  | const char *c; | 
|  |  | 
|  | ui_out_emit_tuple tuple_emitter (uiout, "variable"); | 
|  |  | 
|  | uiout->field_string ("name", std::string ("$") + tsv.name); | 
|  | uiout->field_string ("initial", plongest (tsv.initial_value)); | 
|  |  | 
|  | ui_file_style style; | 
|  | if (tsv.value_known) | 
|  | c = plongest (tsv.value); | 
|  | else if (uiout->is_mi_like_p ()) | 
|  | /* For MI, we prefer not to use magic string constants, but rather | 
|  | omit the field completely.  The difference between unknown and | 
|  | undefined does not seem important enough to represent.  */ | 
|  | c = NULL; | 
|  | else if (current_trace_status ()->running || traceframe_number >= 0) | 
|  | { | 
|  | /* The value is/was defined, but we don't have it.  */ | 
|  | c = "<unknown>"; | 
|  | style = metadata_style.style (); | 
|  | } | 
|  | else | 
|  | { | 
|  | /* It is not meaningful to ask about the value.  */ | 
|  | c = "<undefined>"; | 
|  | style = metadata_style.style (); | 
|  | } | 
|  | if (c) | 
|  | uiout->field_string ("current", c, style); | 
|  | uiout->text ("\n"); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (tvariables.empty ()) | 
|  | uiout->text (_("No trace state variables.\n")); | 
|  | } | 
|  |  | 
|  | /* List all the trace state variables.  */ | 
|  |  | 
|  | static void | 
|  | info_tvariables_command (const char *args, int from_tty) | 
|  | { | 
|  | tvariables_info_1 (); | 
|  | } | 
|  |  | 
|  | /* Stash definitions of tsvs into the given file.  */ | 
|  |  | 
|  | void | 
|  | save_trace_state_variables (struct ui_file *fp) | 
|  | { | 
|  | for (const trace_state_variable &tsv : tvariables) | 
|  | { | 
|  | gdb_printf (fp, "tvariable $%s", tsv.name.c_str ()); | 
|  | if (tsv.initial_value) | 
|  | gdb_printf (fp, " = %s", plongest (tsv.initial_value)); | 
|  | gdb_printf (fp, "\n"); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* ACTIONS functions: */ | 
|  |  | 
|  | /* The three functions: | 
|  | collect_pseudocommand, | 
|  | while_stepping_pseudocommand, and | 
|  | end_actions_pseudocommand | 
|  | are placeholders for "commands" that are actually ONLY to be used | 
|  | within a tracepoint action list.  If the actual function is ever called, | 
|  | it means that somebody issued the "command" at the top level, | 
|  | which is always an error.  */ | 
|  |  | 
|  | static void | 
|  | end_actions_pseudocommand (const char *args, int from_tty) | 
|  | { | 
|  | error (_("This command cannot be used at the top level.")); | 
|  | } | 
|  |  | 
|  | static void | 
|  | while_stepping_pseudocommand (const char *args, int from_tty) | 
|  | { | 
|  | error (_("This command can only be used in a tracepoint actions list.")); | 
|  | } | 
|  |  | 
|  | static void | 
|  | collect_pseudocommand (const char *args, int from_tty) | 
|  | { | 
|  | error (_("This command can only be used in a tracepoint actions list.")); | 
|  | } | 
|  |  | 
|  | static void | 
|  | teval_pseudocommand (const char *args, int from_tty) | 
|  | { | 
|  | error (_("This command can only be used in a tracepoint actions list.")); | 
|  | } | 
|  |  | 
|  | /* Parse any collection options, such as /s for strings.  */ | 
|  |  | 
|  | const char * | 
|  | decode_agent_options (const char *exp, int *trace_string) | 
|  | { | 
|  | struct value_print_options opts; | 
|  |  | 
|  | *trace_string = 0; | 
|  |  | 
|  | if (*exp != '/') | 
|  | return exp; | 
|  |  | 
|  | /* Call this to borrow the print elements default for collection | 
|  | size.  */ | 
|  | get_user_print_options (&opts); | 
|  |  | 
|  | exp++; | 
|  | if (*exp == 's') | 
|  | { | 
|  | if (target_supports_string_tracing ()) | 
|  | { | 
|  | /* Allow an optional decimal number giving an explicit maximum | 
|  | string length, defaulting it to the "print elements" value; | 
|  | so "collect/s80 mystr" gets at most 80 bytes of string.  */ | 
|  | *trace_string = opts.print_max; | 
|  | exp++; | 
|  | if (*exp >= '0' && *exp <= '9') | 
|  | *trace_string = atoi (exp); | 
|  | while (*exp >= '0' && *exp <= '9') | 
|  | exp++; | 
|  | } | 
|  | else | 
|  | error (_("Target does not support \"/s\" option for string tracing.")); | 
|  | } | 
|  | else | 
|  | error (_("Undefined collection format \"%c\"."), *exp); | 
|  |  | 
|  | exp = skip_spaces (exp); | 
|  |  | 
|  | return exp; | 
|  | } | 
|  |  | 
|  | /* Enter a list of actions for a tracepoint.  */ | 
|  | static void | 
|  | actions_command (const char *args, int from_tty) | 
|  | { | 
|  | struct tracepoint *t; | 
|  |  | 
|  | t = get_tracepoint_by_number (&args, NULL); | 
|  | if (t) | 
|  | { | 
|  | std::string tmpbuf = | 
|  | string_printf ("Enter actions for tracepoint %d, one per line.", | 
|  | t->number); | 
|  |  | 
|  | counted_command_line l = read_command_lines (tmpbuf.c_str (), | 
|  | from_tty, 1, | 
|  | [=] (const char *line) | 
|  | { | 
|  | validate_actionline (line, t); | 
|  | }); | 
|  | breakpoint_set_commands (t, std::move (l)); | 
|  | } | 
|  | /* else just return */ | 
|  | } | 
|  |  | 
|  | /* Report the results of checking the agent expression, as errors or | 
|  | internal errors.  */ | 
|  |  | 
|  | static void | 
|  | report_agent_reqs_errors (struct agent_expr *aexpr) | 
|  | { | 
|  | /* All of the "flaws" are serious bytecode generation issues that | 
|  | should never occur.  */ | 
|  | if (aexpr->flaw != agent_flaw_none) | 
|  | internal_error (_("expression is malformed")); | 
|  |  | 
|  | /* If analysis shows a stack underflow, GDB must have done something | 
|  | badly wrong in its bytecode generation.  */ | 
|  | if (aexpr->min_height < 0) | 
|  | internal_error (_("expression has min height < 0")); | 
|  |  | 
|  | /* Issue this error if the stack is predicted to get too deep.  The | 
|  | limit is rather arbitrary; a better scheme might be for the | 
|  | target to report how much stack it will have available.  The | 
|  | depth roughly corresponds to parenthesization, so a limit of 20 | 
|  | amounts to 20 levels of expression nesting, which is actually | 
|  | a pretty big hairy expression.  */ | 
|  | if (aexpr->max_height > 20) | 
|  | error (_("Expression is too complicated.")); | 
|  | } | 
|  |  | 
|  | /* Call ax_reqs on AEXPR and raise an error if something is wrong.  */ | 
|  |  | 
|  | static void | 
|  | finalize_tracepoint_aexpr (struct agent_expr *aexpr) | 
|  | { | 
|  | ax_reqs (aexpr); | 
|  |  | 
|  | if (aexpr->len > MAX_AGENT_EXPR_LEN) | 
|  | error (_("Expression is too complicated.")); | 
|  |  | 
|  | report_agent_reqs_errors (aexpr); | 
|  | } | 
|  |  | 
|  | /* worker function */ | 
|  | void | 
|  | validate_actionline (const char *line, struct breakpoint *b) | 
|  | { | 
|  | struct cmd_list_element *c; | 
|  | const char *tmp_p; | 
|  | const char *p; | 
|  | struct tracepoint *t = (struct tracepoint *) b; | 
|  |  | 
|  | /* If EOF is typed, *line is NULL.  */ | 
|  | if (line == NULL) | 
|  | return; | 
|  |  | 
|  | p = skip_spaces (line); | 
|  |  | 
|  | /* Symbol lookup etc.  */ | 
|  | if (*p == '\0')	/* empty line: just prompt for another line.  */ | 
|  | return; | 
|  |  | 
|  | if (*p == '#')		/* comment line */ | 
|  | return; | 
|  |  | 
|  | c = lookup_cmd (&p, cmdlist, "", NULL, -1, 1); | 
|  | if (c == 0) | 
|  | error (_("`%s' is not a tracepoint action, or is ambiguous."), p); | 
|  |  | 
|  | if (cmd_simple_func_eq (c, collect_pseudocommand)) | 
|  | { | 
|  | int trace_string = 0; | 
|  |  | 
|  | if (*p == '/') | 
|  | p = decode_agent_options (p, &trace_string); | 
|  |  | 
|  | do | 
|  | {			/* Repeat over a comma-separated list.  */ | 
|  | QUIT;			/* Allow user to bail out with ^C.  */ | 
|  | p = skip_spaces (p); | 
|  |  | 
|  | if (*p == '$')	/* Look for special pseudo-symbols.  */ | 
|  | { | 
|  | if (0 == strncasecmp ("reg", p + 1, 3) | 
|  | || 0 == strncasecmp ("arg", p + 1, 3) | 
|  | || 0 == strncasecmp ("loc", p + 1, 3) | 
|  | || 0 == strncasecmp ("_ret", p + 1, 4) | 
|  | || 0 == strncasecmp ("_sdata", p + 1, 6)) | 
|  | { | 
|  | p = strchr (p, ','); | 
|  | continue; | 
|  | } | 
|  | /* else fall thru, treat p as an expression and parse it!  */ | 
|  | } | 
|  | tmp_p = p; | 
|  | for (bp_location *loc : t->locations ()) | 
|  | { | 
|  | p = tmp_p; | 
|  | expression_up exp = parse_exp_1 (&p, loc->address, | 
|  | block_for_pc (loc->address), 1); | 
|  |  | 
|  | if (exp->first_opcode () == OP_VAR_VALUE) | 
|  | { | 
|  | symbol *sym; | 
|  | expr::var_value_operation *vvop | 
|  | = (gdb::checked_static_cast<expr::var_value_operation *> | 
|  | (exp->op.get ())); | 
|  | sym = vvop->get_symbol (); | 
|  |  | 
|  | if (sym->aclass () == LOC_CONST) | 
|  | { | 
|  | error (_("constant `%s' (value %s) " | 
|  | "will not be collected."), | 
|  | sym->print_name (), | 
|  | plongest (sym->value_longest ())); | 
|  | } | 
|  | else if (sym->aclass () == LOC_OPTIMIZED_OUT) | 
|  | { | 
|  | error (_("`%s' is optimized away " | 
|  | "and cannot be collected."), | 
|  | sym->print_name ()); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* We have something to collect, make sure that the expr to | 
|  | bytecode translator can handle it and that it's not too | 
|  | long.  */ | 
|  | agent_expr_up aexpr = gen_trace_for_expr (loc->address, | 
|  | exp.get (), | 
|  | trace_string); | 
|  |  | 
|  | finalize_tracepoint_aexpr (aexpr.get ()); | 
|  | } | 
|  | } | 
|  | while (p && *p++ == ','); | 
|  | } | 
|  |  | 
|  | else if (cmd_simple_func_eq (c, teval_pseudocommand)) | 
|  | { | 
|  | do | 
|  | {			/* Repeat over a comma-separated list.  */ | 
|  | QUIT;			/* Allow user to bail out with ^C.  */ | 
|  | p = skip_spaces (p); | 
|  |  | 
|  | tmp_p = p; | 
|  | for (bp_location *loc : t->locations ()) | 
|  | { | 
|  | p = tmp_p; | 
|  |  | 
|  | /* Only expressions are allowed for this action.  */ | 
|  | expression_up exp = parse_exp_1 (&p, loc->address, | 
|  | block_for_pc (loc->address), 1); | 
|  |  | 
|  | /* We have something to evaluate, make sure that the expr to | 
|  | bytecode translator can handle it and that it's not too | 
|  | long.  */ | 
|  | agent_expr_up aexpr = gen_eval_for_expr (loc->address, exp.get ()); | 
|  |  | 
|  | finalize_tracepoint_aexpr (aexpr.get ()); | 
|  | } | 
|  | } | 
|  | while (p && *p++ == ','); | 
|  | } | 
|  |  | 
|  | else if (cmd_simple_func_eq (c, while_stepping_pseudocommand)) | 
|  | { | 
|  | char *endp; | 
|  |  | 
|  | p = skip_spaces (p); | 
|  | t->step_count = strtol (p, &endp, 0); | 
|  | if (endp == p || t->step_count == 0) | 
|  | error (_("while-stepping step count `%s' is malformed."), line); | 
|  | p = endp; | 
|  | } | 
|  |  | 
|  | else if (cmd_simple_func_eq (c, end_actions_pseudocommand)) | 
|  | ; | 
|  |  | 
|  | else | 
|  | error (_("`%s' is not a supported tracepoint action."), line); | 
|  | } | 
|  |  | 
|  | enum { | 
|  | memrange_absolute = -1 | 
|  | }; | 
|  |  | 
|  | /* MEMRANGE functions: */ | 
|  |  | 
|  | /* Compare memranges for std::sort.  */ | 
|  |  | 
|  | static bool | 
|  | memrange_comp (const memrange &a, const memrange &b) | 
|  | { | 
|  | if (a.type == b.type) | 
|  | { | 
|  | if (a.type == memrange_absolute) | 
|  | return (bfd_vma) a.start < (bfd_vma) b.start; | 
|  | else | 
|  | return a.start < b.start; | 
|  | } | 
|  |  | 
|  | return a.type < b.type; | 
|  | } | 
|  |  | 
|  | /* Sort the memrange list using std::sort, and merge adjacent memranges.  */ | 
|  |  | 
|  | static void | 
|  | memrange_sortmerge (std::vector<memrange> &memranges) | 
|  | { | 
|  | if (!memranges.empty ()) | 
|  | { | 
|  | int a, b; | 
|  |  | 
|  | std::sort (memranges.begin (), memranges.end (), memrange_comp); | 
|  |  | 
|  | for (a = 0, b = 1; b < memranges.size (); b++) | 
|  | { | 
|  | /* If memrange b overlaps or is adjacent to memrange a, | 
|  | merge them.  */ | 
|  | if (memranges[a].type == memranges[b].type | 
|  | && memranges[b].start <= memranges[a].end) | 
|  | { | 
|  | if (memranges[b].end > memranges[a].end) | 
|  | memranges[a].end = memranges[b].end; | 
|  | continue;		/* next b, same a */ | 
|  | } | 
|  | a++;			/* next a */ | 
|  | if (a != b) | 
|  | memranges[a] = memranges[b]; | 
|  | } | 
|  | memranges.resize (a + 1); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Add remote register number REGNO to the collection list mask.  */ | 
|  |  | 
|  | void | 
|  | collection_list::add_remote_register (unsigned int regno) | 
|  | { | 
|  | if (info_verbose) | 
|  | gdb_printf ("collect register %d\n", regno); | 
|  |  | 
|  | m_regs_mask.at (regno / 8) |= 1 << (regno % 8); | 
|  | } | 
|  |  | 
|  | /* Add all the registers from the mask in AEXPR to the mask in the | 
|  | collection list.  Registers in the AEXPR mask are already remote | 
|  | register numbers.  */ | 
|  |  | 
|  | void | 
|  | collection_list::add_ax_registers (struct agent_expr *aexpr) | 
|  | { | 
|  | if (aexpr->reg_mask_len > 0) | 
|  | { | 
|  | for (int ndx1 = 0; ndx1 < aexpr->reg_mask_len; ndx1++) | 
|  | { | 
|  | QUIT;	/* Allow user to bail out with ^C.  */ | 
|  | if (aexpr->reg_mask[ndx1] != 0) | 
|  | { | 
|  | /* Assume chars have 8 bits.  */ | 
|  | for (int ndx2 = 0; ndx2 < 8; ndx2++) | 
|  | if (aexpr->reg_mask[ndx1] & (1 << ndx2)) | 
|  | /* It's used -- record it.  */ | 
|  | add_remote_register (ndx1 * 8 + ndx2); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* If REGNO is raw, add its corresponding remote register number to | 
|  | the mask.  If REGNO is a pseudo-register, figure out the necessary | 
|  | registers using a temporary agent expression, and add it to the | 
|  | list if it needs more than just a mask.  */ | 
|  |  | 
|  | void | 
|  | collection_list::add_local_register (struct gdbarch *gdbarch, | 
|  | unsigned int regno, | 
|  | CORE_ADDR scope) | 
|  | { | 
|  | if (regno < gdbarch_num_regs (gdbarch)) | 
|  | { | 
|  | int remote_regno = gdbarch_remote_register_number (gdbarch, regno); | 
|  |  | 
|  | if (remote_regno < 0) | 
|  | error (_("Can't collect register %d"), regno); | 
|  |  | 
|  | add_remote_register (remote_regno); | 
|  | } | 
|  | else | 
|  | { | 
|  | agent_expr_up aexpr (new agent_expr (gdbarch, scope)); | 
|  |  | 
|  | ax_reg_mask (aexpr.get (), regno); | 
|  |  | 
|  | finalize_tracepoint_aexpr (aexpr.get ()); | 
|  |  | 
|  | add_ax_registers (aexpr.get ()); | 
|  |  | 
|  | /* Usually ax_reg_mask for a pseudo-regiser only sets the | 
|  | corresponding raw registers in the ax mask, but if this isn't | 
|  | the case add the expression that is generated to the | 
|  | collection list.  */ | 
|  | if (aexpr->len > 0) | 
|  | add_aexpr (std::move (aexpr)); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Add a memrange to a collection list.  */ | 
|  |  | 
|  | void | 
|  | collection_list::add_memrange (struct gdbarch *gdbarch, | 
|  | int type, bfd_signed_vma base, | 
|  | unsigned long len, CORE_ADDR scope) | 
|  | { | 
|  | if (info_verbose) | 
|  | gdb_printf ("(%d,%s,%ld)\n", type, paddress (gdbarch, base), len); | 
|  |  | 
|  | /* type: memrange_absolute == memory, other n == basereg */ | 
|  | /* base: addr if memory, offset if reg relative.  */ | 
|  | /* len: we actually save end (base + len) for convenience */ | 
|  | m_memranges.emplace_back (type, base, base + len); | 
|  |  | 
|  | if (type != memrange_absolute)    /* Better collect the base register!  */ | 
|  | add_local_register (gdbarch, type, scope); | 
|  | } | 
|  |  | 
|  | /* Add a symbol to a collection list.  */ | 
|  |  | 
|  | void | 
|  | collection_list::collect_symbol (struct symbol *sym, | 
|  | struct gdbarch *gdbarch, | 
|  | long frame_regno, long frame_offset, | 
|  | CORE_ADDR scope, | 
|  | int trace_string) | 
|  | { | 
|  | unsigned long len; | 
|  | unsigned int reg; | 
|  | bfd_signed_vma offset; | 
|  | int treat_as_expr = 0; | 
|  |  | 
|  | len = check_typedef (sym->type ())->length (); | 
|  | switch (sym->aclass ()) | 
|  | { | 
|  | default: | 
|  | gdb_printf ("%s: don't know symbol class %d\n", | 
|  | sym->print_name (), sym->aclass ()); | 
|  | break; | 
|  | case LOC_CONST: | 
|  | gdb_printf ("constant %s (value %s) will not be collected.\n", | 
|  | sym->print_name (), plongest (sym->value_longest ())); | 
|  | break; | 
|  | case LOC_STATIC: | 
|  | offset = sym->value_address (); | 
|  | if (info_verbose) | 
|  | { | 
|  | gdb_printf ("LOC_STATIC %s: collect %ld bytes at %s.\n", | 
|  | sym->print_name (), len, | 
|  | paddress (gdbarch, offset)); | 
|  | } | 
|  | /* A struct may be a C++ class with static fields, go to general | 
|  | expression handling.  */ | 
|  | if (sym->type ()->code () == TYPE_CODE_STRUCT) | 
|  | treat_as_expr = 1; | 
|  | else | 
|  | add_memrange (gdbarch, memrange_absolute, offset, len, scope); | 
|  | break; | 
|  | case LOC_REGISTER: | 
|  | reg = SYMBOL_REGISTER_OPS (sym)->register_number (sym, gdbarch); | 
|  | if (info_verbose) | 
|  | gdb_printf ("LOC_REG[parm] %s: ", sym->print_name ()); | 
|  | add_local_register (gdbarch, reg, scope); | 
|  | /* Check for doubles stored in two registers.  */ | 
|  | /* FIXME: how about larger types stored in 3 or more regs?  */ | 
|  | if (sym->type ()->code () == TYPE_CODE_FLT && | 
|  | len > register_size (gdbarch, reg)) | 
|  | add_local_register (gdbarch, reg + 1, scope); | 
|  | break; | 
|  | case LOC_REF_ARG: | 
|  | gdb_printf ("Sorry, don't know how to do LOC_REF_ARG yet.\n"); | 
|  | gdb_printf ("       (will not collect %s)\n", sym->print_name ()); | 
|  | break; | 
|  | case LOC_ARG: | 
|  | reg = frame_regno; | 
|  | offset = frame_offset + sym->value_longest (); | 
|  | if (info_verbose) | 
|  | { | 
|  | gdb_printf ("LOC_LOCAL %s: Collect %ld bytes at offset %s" | 
|  | " from frame ptr reg %d\n", sym->print_name (), len, | 
|  | paddress (gdbarch, offset), reg); | 
|  | } | 
|  | add_memrange (gdbarch, reg, offset, len, scope); | 
|  | break; | 
|  | case LOC_REGPARM_ADDR: | 
|  | reg = sym->value_longest (); | 
|  | offset = 0; | 
|  | if (info_verbose) | 
|  | { | 
|  | gdb_printf ("LOC_REGPARM_ADDR %s: Collect %ld bytes at offset %s" | 
|  | " from reg %d\n", sym->print_name (), len, | 
|  | paddress (gdbarch, offset), reg); | 
|  | } | 
|  | add_memrange (gdbarch, reg, offset, len, scope); | 
|  | break; | 
|  | case LOC_LOCAL: | 
|  | reg = frame_regno; | 
|  | offset = frame_offset + sym->value_longest (); | 
|  | if (info_verbose) | 
|  | { | 
|  | gdb_printf ("LOC_LOCAL %s: Collect %ld bytes at offset %s" | 
|  | " from frame ptr reg %d\n", sym->print_name (), len, | 
|  | paddress (gdbarch, offset), reg); | 
|  | } | 
|  | add_memrange (gdbarch, reg, offset, len, scope); | 
|  | break; | 
|  |  | 
|  | case LOC_UNRESOLVED: | 
|  | treat_as_expr = 1; | 
|  | break; | 
|  |  | 
|  | case LOC_OPTIMIZED_OUT: | 
|  | gdb_printf ("%s has been optimized out of existence.\n", | 
|  | sym->print_name ()); | 
|  | break; | 
|  |  | 
|  | case LOC_COMPUTED: | 
|  | treat_as_expr = 1; | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* Expressions are the most general case.  */ | 
|  | if (treat_as_expr) | 
|  | { | 
|  | agent_expr_up aexpr = gen_trace_for_var (scope, gdbarch, | 
|  | sym, trace_string); | 
|  |  | 
|  | /* It can happen that the symbol is recorded as a computed | 
|  | location, but it's been optimized away and doesn't actually | 
|  | have a location expression.  */ | 
|  | if (!aexpr) | 
|  | { | 
|  | gdb_printf ("%s has been optimized out of existence.\n", | 
|  | sym->print_name ()); | 
|  | return; | 
|  | } | 
|  |  | 
|  | finalize_tracepoint_aexpr (aexpr.get ()); | 
|  |  | 
|  | /* Take care of the registers.  */ | 
|  | add_ax_registers (aexpr.get ()); | 
|  |  | 
|  | add_aexpr (std::move (aexpr)); | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | collection_list::add_wholly_collected (const char *print_name) | 
|  | { | 
|  | m_wholly_collected.push_back (print_name); | 
|  | } | 
|  |  | 
|  | /* Add all locals (or args) symbols to collection list.  */ | 
|  |  | 
|  | void | 
|  | collection_list::add_local_symbols (struct gdbarch *gdbarch, CORE_ADDR pc, | 
|  | long frame_regno, long frame_offset, int type, | 
|  | int trace_string) | 
|  | { | 
|  | const struct block *block; | 
|  | int count = 0; | 
|  |  | 
|  | auto do_collect_symbol = [&] (const char *print_name, | 
|  | struct symbol *sym) | 
|  | { | 
|  | collect_symbol (sym, gdbarch, frame_regno, | 
|  | frame_offset, pc, trace_string); | 
|  | count++; | 
|  | add_wholly_collected (print_name); | 
|  | }; | 
|  |  | 
|  | if (type == 'L') | 
|  | { | 
|  | block = block_for_pc (pc); | 
|  | if (block == NULL) | 
|  | { | 
|  | warning (_("Can't collect locals; " | 
|  | "no symbol table info available.\n")); | 
|  | return; | 
|  | } | 
|  |  | 
|  | iterate_over_block_local_vars (block, do_collect_symbol); | 
|  | if (count == 0) | 
|  | warning (_("No locals found in scope.")); | 
|  | } | 
|  | else | 
|  | { | 
|  | CORE_ADDR fn_pc = get_pc_function_start (pc); | 
|  | block = block_for_pc (fn_pc); | 
|  | if (block == NULL) | 
|  | { | 
|  | warning (_("Can't collect args; no symbol table info available.")); | 
|  | return; | 
|  | } | 
|  |  | 
|  | iterate_over_block_arg_vars (block, do_collect_symbol); | 
|  | if (count == 0) | 
|  | warning (_("No args found in scope.")); | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | collection_list::add_static_trace_data () | 
|  | { | 
|  | if (info_verbose) | 
|  | gdb_printf ("collect static trace data\n"); | 
|  | m_strace_data = true; | 
|  | } | 
|  |  | 
|  | collection_list::collection_list () | 
|  | : m_strace_data (false) | 
|  | { | 
|  | int max_remote_regno = 0; | 
|  | for (int i = 0; i < gdbarch_num_regs (target_gdbarch ()); i++) | 
|  | { | 
|  | int remote_regno = (gdbarch_remote_register_number | 
|  | (target_gdbarch (), i)); | 
|  |  | 
|  | if (remote_regno >= 0 && remote_regno > max_remote_regno) | 
|  | max_remote_regno = remote_regno; | 
|  | } | 
|  |  | 
|  | m_regs_mask.resize ((max_remote_regno / 8) + 1); | 
|  |  | 
|  | m_memranges.reserve (128); | 
|  | m_aexprs.reserve (128); | 
|  | } | 
|  |  | 
|  | /* Reduce a collection list to string form (for gdb protocol).  */ | 
|  |  | 
|  | std::vector<std::string> | 
|  | collection_list::stringify () | 
|  | { | 
|  | gdb::char_vector temp_buf (2048); | 
|  |  | 
|  | int count; | 
|  | char *end; | 
|  | long i; | 
|  | std::vector<std::string> str_list; | 
|  |  | 
|  | if (m_strace_data) | 
|  | { | 
|  | if (info_verbose) | 
|  | gdb_printf ("\nCollecting static trace data\n"); | 
|  | end = temp_buf.data (); | 
|  | *end++ = 'L'; | 
|  | str_list.emplace_back (temp_buf.data (), end - temp_buf.data ()); | 
|  | } | 
|  |  | 
|  | for (i = m_regs_mask.size () - 1; i > 0; i--) | 
|  | if (m_regs_mask[i] != 0)    /* Skip leading zeroes in regs_mask.  */ | 
|  | break; | 
|  | if (m_regs_mask[i] != 0)	/* Prepare to send regs_mask to the stub.  */ | 
|  | { | 
|  | if (info_verbose) | 
|  | gdb_printf ("\nCollecting registers (mask): 0x"); | 
|  |  | 
|  | /* One char for 'R', one for the null terminator and two per | 
|  | mask byte.  */ | 
|  | std::size_t new_size = (i + 1) * 2 + 2; | 
|  | if (new_size > temp_buf.size ()) | 
|  | temp_buf.resize (new_size); | 
|  |  | 
|  | end = temp_buf.data (); | 
|  | *end++ = 'R'; | 
|  | for (; i >= 0; i--) | 
|  | { | 
|  | QUIT;			/* Allow user to bail out with ^C.  */ | 
|  | if (info_verbose) | 
|  | gdb_printf ("%02X", m_regs_mask[i]); | 
|  |  | 
|  | end = pack_hex_byte (end, m_regs_mask[i]); | 
|  | } | 
|  | *end = '\0'; | 
|  |  | 
|  | str_list.emplace_back (temp_buf.data ()); | 
|  | } | 
|  | if (info_verbose) | 
|  | gdb_printf ("\n"); | 
|  | if (!m_memranges.empty () && info_verbose) | 
|  | gdb_printf ("Collecting memranges: \n"); | 
|  | for (i = 0, count = 0, end = temp_buf.data (); | 
|  | i < m_memranges.size (); i++) | 
|  | { | 
|  | QUIT;			/* Allow user to bail out with ^C.  */ | 
|  | if (info_verbose) | 
|  | { | 
|  | gdb_printf ("(%d, %s, %ld)\n", | 
|  | m_memranges[i].type, | 
|  | paddress (target_gdbarch (), | 
|  | m_memranges[i].start), | 
|  | (long) (m_memranges[i].end | 
|  | - m_memranges[i].start)); | 
|  | } | 
|  | if (count + 27 > MAX_AGENT_EXPR_LEN) | 
|  | { | 
|  | str_list.emplace_back (temp_buf.data (), count); | 
|  | count = 0; | 
|  | end = temp_buf.data (); | 
|  | } | 
|  |  | 
|  | { | 
|  | bfd_signed_vma length | 
|  | = m_memranges[i].end - m_memranges[i].start; | 
|  |  | 
|  | /* The "%X" conversion specifier expects an unsigned argument, | 
|  | so passing -1 (memrange_absolute) to it directly gives you | 
|  | "FFFFFFFF" (or more, depending on sizeof (unsigned)). | 
|  | Special-case it.  */ | 
|  | if (m_memranges[i].type == memrange_absolute) | 
|  | sprintf (end, "M-1,%s,%lX", phex_nz (m_memranges[i].start, 0), | 
|  | (long) length); | 
|  | else | 
|  | sprintf (end, "M%X,%s,%lX", m_memranges[i].type, | 
|  | phex_nz (m_memranges[i].start, 0), (long) length); | 
|  | } | 
|  |  | 
|  | count += strlen (end); | 
|  | end = temp_buf.data () + count; | 
|  | } | 
|  |  | 
|  | for (i = 0; i < m_aexprs.size (); i++) | 
|  | { | 
|  | QUIT;			/* Allow user to bail out with ^C.  */ | 
|  | if ((count + 10 + 2 * m_aexprs[i]->len) > MAX_AGENT_EXPR_LEN) | 
|  | { | 
|  | str_list.emplace_back (temp_buf.data (), count); | 
|  | count = 0; | 
|  | end = temp_buf.data (); | 
|  | } | 
|  | sprintf (end, "X%08X,", m_aexprs[i]->len); | 
|  | end += 10;		/* 'X' + 8 hex digits + ',' */ | 
|  | count += 10; | 
|  |  | 
|  | end = mem2hex (m_aexprs[i]->buf, end, m_aexprs[i]->len); | 
|  | count += 2 * m_aexprs[i]->len; | 
|  | } | 
|  |  | 
|  | if (count != 0) | 
|  | { | 
|  | str_list.emplace_back (temp_buf.data (), count); | 
|  | count = 0; | 
|  | end = temp_buf.data (); | 
|  | } | 
|  |  | 
|  | return str_list; | 
|  | } | 
|  |  | 
|  | /* Add the expression STR to M_COMPUTED.  */ | 
|  |  | 
|  | void | 
|  | collection_list::append_exp (std::string &&str) | 
|  | { | 
|  | m_computed.push_back (std::move (str)); | 
|  | } | 
|  |  | 
|  | void | 
|  | collection_list::finish () | 
|  | { | 
|  | memrange_sortmerge (m_memranges); | 
|  | } | 
|  |  | 
|  | static void | 
|  | encode_actions_1 (struct command_line *action, | 
|  | struct bp_location *tloc, | 
|  | int frame_reg, | 
|  | LONGEST frame_offset, | 
|  | struct collection_list *collect, | 
|  | struct collection_list *stepping_list) | 
|  | { | 
|  | const char *action_exp; | 
|  | int i; | 
|  | struct value *tempval; | 
|  | struct cmd_list_element *cmd; | 
|  |  | 
|  | for (; action; action = action->next) | 
|  | { | 
|  | QUIT;			/* Allow user to bail out with ^C.  */ | 
|  | action_exp = action->line; | 
|  | action_exp = skip_spaces (action_exp); | 
|  |  | 
|  | cmd = lookup_cmd (&action_exp, cmdlist, "", NULL, -1, 1); | 
|  | if (cmd == 0) | 
|  | error (_("Bad action list item: %s"), action_exp); | 
|  |  | 
|  | if (cmd_simple_func_eq (cmd, collect_pseudocommand)) | 
|  | { | 
|  | int trace_string = 0; | 
|  |  | 
|  | if (*action_exp == '/') | 
|  | action_exp = decode_agent_options (action_exp, &trace_string); | 
|  |  | 
|  | do | 
|  | {			/* Repeat over a comma-separated list.  */ | 
|  | QUIT;		/* Allow user to bail out with ^C.  */ | 
|  | action_exp = skip_spaces (action_exp); | 
|  |  | 
|  | if (0 == strncasecmp ("$reg", action_exp, 4)) | 
|  | { | 
|  | for (i = 0; i < gdbarch_num_regs (target_gdbarch ()); | 
|  | i++) | 
|  | { | 
|  | int remote_regno = (gdbarch_remote_register_number | 
|  | (target_gdbarch (), i)); | 
|  |  | 
|  | /* Ignore arch regnos without a corresponding | 
|  | remote regno.  This can happen for regnos not | 
|  | in the tdesc.  */ | 
|  | if (remote_regno >= 0) | 
|  | collect->add_remote_register (remote_regno); | 
|  | } | 
|  | action_exp = strchr (action_exp, ',');	/* more? */ | 
|  | } | 
|  | else if (0 == strncasecmp ("$arg", action_exp, 4)) | 
|  | { | 
|  | collect->add_local_symbols (target_gdbarch (), | 
|  | tloc->address, | 
|  | frame_reg, | 
|  | frame_offset, | 
|  | 'A', | 
|  | trace_string); | 
|  | action_exp = strchr (action_exp, ',');	/* more? */ | 
|  | } | 
|  | else if (0 == strncasecmp ("$loc", action_exp, 4)) | 
|  | { | 
|  | collect->add_local_symbols (target_gdbarch (), | 
|  | tloc->address, | 
|  | frame_reg, | 
|  | frame_offset, | 
|  | 'L', | 
|  | trace_string); | 
|  | action_exp = strchr (action_exp, ',');	/* more? */ | 
|  | } | 
|  | else if (0 == strncasecmp ("$_ret", action_exp, 5)) | 
|  | { | 
|  | agent_expr_up aexpr | 
|  | = gen_trace_for_return_address (tloc->address, | 
|  | target_gdbarch (), | 
|  | trace_string); | 
|  |  | 
|  | finalize_tracepoint_aexpr (aexpr.get ()); | 
|  |  | 
|  | /* take care of the registers */ | 
|  | collect->add_ax_registers (aexpr.get ()); | 
|  |  | 
|  | collect->add_aexpr (std::move (aexpr)); | 
|  | action_exp = strchr (action_exp, ',');	/* more? */ | 
|  | } | 
|  | else if (0 == strncasecmp ("$_sdata", action_exp, 7)) | 
|  | { | 
|  | collect->add_static_trace_data (); | 
|  | action_exp = strchr (action_exp, ',');	/* more? */ | 
|  | } | 
|  | else | 
|  | { | 
|  | unsigned long addr; | 
|  |  | 
|  | const char *exp_start = action_exp; | 
|  | expression_up exp = parse_exp_1 (&action_exp, tloc->address, | 
|  | block_for_pc (tloc->address), | 
|  | 1); | 
|  |  | 
|  | switch (exp->first_opcode ()) | 
|  | { | 
|  | case OP_REGISTER: | 
|  | { | 
|  | expr::register_operation *regop | 
|  | = (gdb::checked_static_cast<expr::register_operation *> | 
|  | (exp->op.get ())); | 
|  | const char *name = regop->get_name (); | 
|  |  | 
|  | i = user_reg_map_name_to_regnum (target_gdbarch (), | 
|  | name, strlen (name)); | 
|  | if (i == -1) | 
|  | internal_error (_("Register $%s not available"), | 
|  | name); | 
|  | if (info_verbose) | 
|  | gdb_printf ("OP_REGISTER: "); | 
|  | collect->add_local_register (target_gdbarch (), | 
|  | i, tloc->address); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case UNOP_MEMVAL: | 
|  | { | 
|  | /* Safe because we know it's a simple expression.  */ | 
|  | tempval = evaluate_expression (exp.get ()); | 
|  | addr = value_address (tempval); | 
|  | expr::unop_memval_operation *memop | 
|  | = (gdb::checked_static_cast<expr::unop_memval_operation *> | 
|  | (exp->op.get ())); | 
|  | struct type *type = memop->get_type (); | 
|  | /* Initialize the TYPE_LENGTH if it is a typedef.  */ | 
|  | check_typedef (type); | 
|  | collect->add_memrange (target_gdbarch (), | 
|  | memrange_absolute, addr, | 
|  | type->length (), | 
|  | tloc->address); | 
|  | collect->append_exp (std::string (exp_start, | 
|  | action_exp)); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case OP_VAR_VALUE: | 
|  | { | 
|  | expr::var_value_operation *vvo | 
|  | = (gdb::checked_static_cast<expr::var_value_operation *> | 
|  | (exp->op.get ())); | 
|  | struct symbol *sym = vvo->get_symbol (); | 
|  | const char *name = sym->natural_name (); | 
|  |  | 
|  | collect->collect_symbol (sym, | 
|  | target_gdbarch (), | 
|  | frame_reg, | 
|  | frame_offset, | 
|  | tloc->address, | 
|  | trace_string); | 
|  | collect->add_wholly_collected (name); | 
|  | } | 
|  | break; | 
|  |  | 
|  | default:	/* Full-fledged expression.  */ | 
|  | agent_expr_up aexpr = gen_trace_for_expr (tloc->address, | 
|  | exp.get (), | 
|  | trace_string); | 
|  |  | 
|  | finalize_tracepoint_aexpr (aexpr.get ()); | 
|  |  | 
|  | /* Take care of the registers.  */ | 
|  | collect->add_ax_registers (aexpr.get ()); | 
|  |  | 
|  | collect->add_aexpr (std::move (aexpr)); | 
|  | collect->append_exp (std::string (exp_start, | 
|  | action_exp)); | 
|  | break; | 
|  | }		/* switch */ | 
|  | }		/* do */ | 
|  | } | 
|  | while (action_exp && *action_exp++ == ','); | 
|  | }			/* if */ | 
|  | else if (cmd_simple_func_eq (cmd, teval_pseudocommand)) | 
|  | { | 
|  | do | 
|  | {			/* Repeat over a comma-separated list.  */ | 
|  | QUIT;		/* Allow user to bail out with ^C.  */ | 
|  | action_exp = skip_spaces (action_exp); | 
|  |  | 
|  | { | 
|  | expression_up exp = parse_exp_1 (&action_exp, tloc->address, | 
|  | block_for_pc (tloc->address), | 
|  | 1); | 
|  |  | 
|  | agent_expr_up aexpr = gen_eval_for_expr (tloc->address, | 
|  | exp.get ()); | 
|  |  | 
|  | finalize_tracepoint_aexpr (aexpr.get ()); | 
|  |  | 
|  | /* Even though we're not officially collecting, add | 
|  | to the collect list anyway.  */ | 
|  | collect->add_aexpr (std::move (aexpr)); | 
|  | }		/* do */ | 
|  | } | 
|  | while (action_exp && *action_exp++ == ','); | 
|  | }			/* if */ | 
|  | else if (cmd_simple_func_eq (cmd, while_stepping_pseudocommand)) | 
|  | { | 
|  | /* We check against nested while-stepping when setting | 
|  | breakpoint action, so no way to run into nested | 
|  | here.  */ | 
|  | gdb_assert (stepping_list); | 
|  |  | 
|  | encode_actions_1 (action->body_list_0.get (), tloc, frame_reg, | 
|  | frame_offset, stepping_list, NULL); | 
|  | } | 
|  | else | 
|  | error (_("Invalid tracepoint command '%s'"), action->line); | 
|  | }				/* for */ | 
|  | } | 
|  |  | 
|  | /* Encode actions of tracepoint TLOC->owner and fill TRACEPOINT_LIST | 
|  | and STEPPING_LIST.  */ | 
|  |  | 
|  | void | 
|  | encode_actions (struct bp_location *tloc, | 
|  | struct collection_list *tracepoint_list, | 
|  | struct collection_list *stepping_list) | 
|  | { | 
|  | int frame_reg; | 
|  | LONGEST frame_offset; | 
|  |  | 
|  | gdbarch_virtual_frame_pointer (tloc->gdbarch, | 
|  | tloc->address, &frame_reg, &frame_offset); | 
|  |  | 
|  | counted_command_line actions = all_tracepoint_actions (tloc->owner); | 
|  | encode_actions_1 (actions.get (), tloc, frame_reg, frame_offset, | 
|  | tracepoint_list, stepping_list); | 
|  | encode_actions_1 (breakpoint_commands (tloc->owner), tloc, | 
|  | frame_reg, frame_offset, tracepoint_list, stepping_list); | 
|  |  | 
|  | tracepoint_list->finish (); | 
|  | stepping_list->finish (); | 
|  | } | 
|  |  | 
|  | /* Render all actions into gdb protocol.  */ | 
|  |  | 
|  | void | 
|  | encode_actions_rsp (struct bp_location *tloc, | 
|  | std::vector<std::string> *tdp_actions, | 
|  | std::vector<std::string> *stepping_actions) | 
|  | { | 
|  | struct collection_list tracepoint_list, stepping_list; | 
|  |  | 
|  | encode_actions (tloc, &tracepoint_list, &stepping_list); | 
|  |  | 
|  | *tdp_actions = tracepoint_list.stringify (); | 
|  | *stepping_actions = stepping_list.stringify (); | 
|  | } | 
|  |  | 
|  | void | 
|  | collection_list::add_aexpr (agent_expr_up aexpr) | 
|  | { | 
|  | m_aexprs.push_back (std::move (aexpr)); | 
|  | } | 
|  |  | 
|  | static void | 
|  | process_tracepoint_on_disconnect (void) | 
|  | { | 
|  | int has_pending_p = 0; | 
|  |  | 
|  | /* Check whether we still have pending tracepoint.  If we have, warn the | 
|  | user that pending tracepoint will no longer work.  */ | 
|  | for (breakpoint *b : all_tracepoints ()) | 
|  | { | 
|  | if (b->loc == NULL) | 
|  | { | 
|  | has_pending_p = 1; | 
|  | break; | 
|  | } | 
|  | else | 
|  | { | 
|  | for (bp_location *loc1 : b->locations ()) | 
|  | { | 
|  | if (loc1->shlib_disabled) | 
|  | { | 
|  | has_pending_p = 1; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (has_pending_p) | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (has_pending_p) | 
|  | warning (_("Pending tracepoints will not be resolved while" | 
|  | " GDB is disconnected\n")); | 
|  | } | 
|  |  | 
|  | /* Reset local state of tracing.  */ | 
|  |  | 
|  | void | 
|  | trace_reset_local_state (void) | 
|  | { | 
|  | set_traceframe_num (-1); | 
|  | set_tracepoint_num (-1); | 
|  | set_traceframe_context (NULL); | 
|  | clear_traceframe_info (); | 
|  | } | 
|  |  | 
|  | void | 
|  | start_tracing (const char *notes) | 
|  | { | 
|  | int any_enabled = 0, num_to_download = 0; | 
|  | int ret; | 
|  |  | 
|  | auto tracepoint_range = all_tracepoints (); | 
|  |  | 
|  | /* No point in tracing without any tracepoints...  */ | 
|  | if (tracepoint_range.begin () == tracepoint_range.end ()) | 
|  | error (_("No tracepoints defined, not starting trace")); | 
|  |  | 
|  | for (breakpoint *b : tracepoint_range) | 
|  | { | 
|  | if (b->enable_state == bp_enabled) | 
|  | any_enabled = 1; | 
|  |  | 
|  | if ((b->type == bp_fast_tracepoint | 
|  | ? may_insert_fast_tracepoints | 
|  | : may_insert_tracepoints)) | 
|  | ++num_to_download; | 
|  | else | 
|  | warning (_("May not insert %stracepoints, skipping tracepoint %d"), | 
|  | (b->type == bp_fast_tracepoint ? "fast " : ""), b->number); | 
|  | } | 
|  |  | 
|  | if (!any_enabled) | 
|  | { | 
|  | if (target_supports_enable_disable_tracepoint ()) | 
|  | warning (_("No tracepoints enabled")); | 
|  | else | 
|  | { | 
|  | /* No point in tracing with only disabled tracepoints that | 
|  | cannot be re-enabled.  */ | 
|  | error (_("No tracepoints enabled, not starting trace")); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (num_to_download <= 0) | 
|  | error (_("No tracepoints that may be downloaded, not starting trace")); | 
|  |  | 
|  | target_trace_init (); | 
|  |  | 
|  | for (breakpoint *b : tracepoint_range) | 
|  | { | 
|  | struct tracepoint *t = (struct tracepoint *) b; | 
|  | int bp_location_downloaded = 0; | 
|  |  | 
|  | /* Clear `inserted' flag.  */ | 
|  | for (bp_location *loc : b->locations ()) | 
|  | loc->inserted = 0; | 
|  |  | 
|  | if ((b->type == bp_fast_tracepoint | 
|  | ? !may_insert_fast_tracepoints | 
|  | : !may_insert_tracepoints)) | 
|  | continue; | 
|  |  | 
|  | t->number_on_target = 0; | 
|  |  | 
|  | for (bp_location *loc : b->locations ()) | 
|  | { | 
|  | /* Since tracepoint locations are never duplicated, `inserted' | 
|  | flag should be zero.  */ | 
|  | gdb_assert (!loc->inserted); | 
|  |  | 
|  | target_download_tracepoint (loc); | 
|  |  | 
|  | loc->inserted = 1; | 
|  | bp_location_downloaded = 1; | 
|  | } | 
|  |  | 
|  | t->number_on_target = b->number; | 
|  |  | 
|  | for (bp_location *loc : b->locations ()) | 
|  | if (loc->probe.prob != NULL) | 
|  | loc->probe.prob->set_semaphore (loc->probe.objfile, | 
|  | loc->gdbarch); | 
|  |  | 
|  | if (bp_location_downloaded) | 
|  | gdb::observers::breakpoint_modified.notify (b); | 
|  | } | 
|  |  | 
|  | /* Send down all the trace state variables too.  */ | 
|  | for (const trace_state_variable &tsv : tvariables) | 
|  | target_download_trace_state_variable (tsv); | 
|  |  | 
|  | /* Tell target to treat text-like sections as transparent.  */ | 
|  | target_trace_set_readonly_regions (); | 
|  | /* Set some mode flags.  */ | 
|  | target_set_disconnected_tracing (disconnected_tracing); | 
|  | target_set_circular_trace_buffer (circular_trace_buffer); | 
|  | target_set_trace_buffer_size (trace_buffer_size); | 
|  |  | 
|  | if (!notes) | 
|  | notes = trace_notes.c_str (); | 
|  |  | 
|  | ret = target_set_trace_notes (trace_user.c_str (), notes, NULL); | 
|  |  | 
|  | if (!ret && (!trace_user.empty () || notes)) | 
|  | warning (_("Target does not support trace user/notes, info ignored")); | 
|  |  | 
|  | /* Now insert traps and begin collecting data.  */ | 
|  | target_trace_start (); | 
|  |  | 
|  | /* Reset our local state.  */ | 
|  | trace_reset_local_state (); | 
|  | current_trace_status()->running = 1; | 
|  | } | 
|  |  | 
|  | /* The tstart command requests the target to start a new trace run. | 
|  | The command passes any arguments it has to the target verbatim, as | 
|  | an optional "trace note".  This is useful as for instance a warning | 
|  | to other users if the trace runs disconnected, and you don't want | 
|  | anybody else messing with the target.  */ | 
|  |  | 
|  | static void | 
|  | tstart_command (const char *args, int from_tty) | 
|  | { | 
|  | dont_repeat ();	/* Like "run", dangerous to repeat accidentally.  */ | 
|  |  | 
|  | if (current_trace_status ()->running) | 
|  | { | 
|  | if (from_tty | 
|  | && !query (_("A trace is running already.  Start a new run? "))) | 
|  | error (_("New trace run not started.")); | 
|  | } | 
|  |  | 
|  | start_tracing (args); | 
|  | } | 
|  |  | 
|  | /* The tstop command stops the tracing run.  The command passes any | 
|  | supplied arguments to the target verbatim as a "stop note"; if the | 
|  | target supports trace notes, then it will be reported back as part | 
|  | of the trace run's status.  */ | 
|  |  | 
|  | static void | 
|  | tstop_command (const char *args, int from_tty) | 
|  | { | 
|  | if (!current_trace_status ()->running) | 
|  | error (_("Trace is not running.")); | 
|  |  | 
|  | stop_tracing (args); | 
|  | } | 
|  |  | 
|  | void | 
|  | stop_tracing (const char *note) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | target_trace_stop (); | 
|  |  | 
|  | for (breakpoint *t : all_tracepoints ()) | 
|  | { | 
|  | if ((t->type == bp_fast_tracepoint | 
|  | ? !may_insert_fast_tracepoints | 
|  | : !may_insert_tracepoints)) | 
|  | continue; | 
|  |  | 
|  | for (bp_location *loc : t->locations ()) | 
|  | { | 
|  | /* GDB can be totally absent in some disconnected trace scenarios, | 
|  | but we don't really care if this semaphore goes out of sync. | 
|  | That's why we are decrementing it here, but not taking care | 
|  | in other places.  */ | 
|  | if (loc->probe.prob != NULL) | 
|  | loc->probe.prob->clear_semaphore (loc->probe.objfile, | 
|  | loc->gdbarch); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!note) | 
|  | note = trace_stop_notes.c_str (); | 
|  |  | 
|  | ret = target_set_trace_notes (NULL, NULL, note); | 
|  |  | 
|  | if (!ret && note) | 
|  | warning (_("Target does not support trace notes, note ignored")); | 
|  |  | 
|  | /* Should change in response to reply?  */ | 
|  | current_trace_status ()->running = 0; | 
|  | } | 
|  |  | 
|  | /* tstatus command */ | 
|  | static void | 
|  | tstatus_command (const char *args, int from_tty) | 
|  | { | 
|  | struct trace_status *ts = current_trace_status (); | 
|  | int status; | 
|  |  | 
|  | status = target_get_trace_status (ts); | 
|  |  | 
|  | if (status == -1) | 
|  | { | 
|  | if (ts->filename != NULL) | 
|  | gdb_printf (_("Using a trace file.\n")); | 
|  | else | 
|  | { | 
|  | gdb_printf (_("Trace can not be run on this target.\n")); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!ts->running_known) | 
|  | { | 
|  | gdb_printf (_("Run/stop status is unknown.\n")); | 
|  | } | 
|  | else if (ts->running) | 
|  | { | 
|  | gdb_printf (_("Trace is running on the target.\n")); | 
|  | } | 
|  | else | 
|  | { | 
|  | switch (ts->stop_reason) | 
|  | { | 
|  | case trace_never_run: | 
|  | gdb_printf (_("No trace has been run on the target.\n")); | 
|  | break; | 
|  | case trace_stop_command: | 
|  | if (ts->stop_desc) | 
|  | gdb_printf (_("Trace stopped by a tstop command (%s).\n"), | 
|  | ts->stop_desc); | 
|  | else | 
|  | gdb_printf (_("Trace stopped by a tstop command.\n")); | 
|  | break; | 
|  | case trace_buffer_full: | 
|  | gdb_printf (_("Trace stopped because the buffer was full.\n")); | 
|  | break; | 
|  | case trace_disconnected: | 
|  | gdb_printf (_("Trace stopped because of disconnection.\n")); | 
|  | break; | 
|  | case tracepoint_passcount: | 
|  | gdb_printf (_("Trace stopped by tracepoint %d.\n"), | 
|  | ts->stopping_tracepoint); | 
|  | break; | 
|  | case tracepoint_error: | 
|  | if (ts->stopping_tracepoint) | 
|  | gdb_printf (_("Trace stopped by an " | 
|  | "error (%s, tracepoint %d).\n"), | 
|  | ts->stop_desc, ts->stopping_tracepoint); | 
|  | else | 
|  | gdb_printf (_("Trace stopped by an error (%s).\n"), | 
|  | ts->stop_desc); | 
|  | break; | 
|  | case trace_stop_reason_unknown: | 
|  | gdb_printf (_("Trace stopped for an unknown reason.\n")); | 
|  | break; | 
|  | default: | 
|  | gdb_printf (_("Trace stopped for some other reason (%d).\n"), | 
|  | ts->stop_reason); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (ts->traceframes_created >= 0 | 
|  | && ts->traceframe_count != ts->traceframes_created) | 
|  | { | 
|  | gdb_printf (_("Buffer contains %d trace " | 
|  | "frames (of %d created total).\n"), | 
|  | ts->traceframe_count, ts->traceframes_created); | 
|  | } | 
|  | else if (ts->traceframe_count >= 0) | 
|  | { | 
|  | gdb_printf (_("Collected %d trace frames.\n"), | 
|  | ts->traceframe_count); | 
|  | } | 
|  |  | 
|  | if (ts->buffer_free >= 0) | 
|  | { | 
|  | if (ts->buffer_size >= 0) | 
|  | { | 
|  | gdb_printf (_("Trace buffer has %d bytes of %d bytes free"), | 
|  | ts->buffer_free, ts->buffer_size); | 
|  | if (ts->buffer_size > 0) | 
|  | gdb_printf (_(" (%d%% full)"), | 
|  | ((int) ((((long long) (ts->buffer_size | 
|  | - ts->buffer_free)) * 100) | 
|  | / ts->buffer_size))); | 
|  | gdb_printf (_(".\n")); | 
|  | } | 
|  | else | 
|  | gdb_printf (_("Trace buffer has %d bytes free.\n"), | 
|  | ts->buffer_free); | 
|  | } | 
|  |  | 
|  | if (ts->disconnected_tracing) | 
|  | gdb_printf (_("Trace will continue if GDB disconnects.\n")); | 
|  | else | 
|  | gdb_printf (_("Trace will stop if GDB disconnects.\n")); | 
|  |  | 
|  | if (ts->circular_buffer) | 
|  | gdb_printf (_("Trace buffer is circular.\n")); | 
|  |  | 
|  | if (ts->user_name && strlen (ts->user_name) > 0) | 
|  | gdb_printf (_("Trace user is %s.\n"), ts->user_name); | 
|  |  | 
|  | if (ts->notes && strlen (ts->notes) > 0) | 
|  | gdb_printf (_("Trace notes: %s.\n"), ts->notes); | 
|  |  | 
|  | /* Now report on what we're doing with tfind.  */ | 
|  | if (traceframe_number >= 0) | 
|  | gdb_printf (_("Looking at trace frame %d, tracepoint %d.\n"), | 
|  | traceframe_number, tracepoint_number); | 
|  | else | 
|  | gdb_printf (_("Not looking at any trace frame.\n")); | 
|  |  | 
|  | /* Report start/stop times if supplied.  */ | 
|  | if (ts->start_time) | 
|  | { | 
|  | if (ts->stop_time) | 
|  | { | 
|  | LONGEST run_time = ts->stop_time - ts->start_time; | 
|  |  | 
|  | /* Reporting a run time is more readable than two long numbers.  */ | 
|  | gdb_printf (_("Trace started at %ld.%06ld secs, stopped %ld.%06ld secs later.\n"), | 
|  | (long int) (ts->start_time / 1000000), | 
|  | (long int) (ts->start_time % 1000000), | 
|  | (long int) (run_time / 1000000), | 
|  | (long int) (run_time % 1000000)); | 
|  | } | 
|  | else | 
|  | gdb_printf (_("Trace started at %ld.%06ld secs.\n"), | 
|  | (long int) (ts->start_time / 1000000), | 
|  | (long int) (ts->start_time % 1000000)); | 
|  | } | 
|  | else if (ts->stop_time) | 
|  | gdb_printf (_("Trace stopped at %ld.%06ld secs.\n"), | 
|  | (long int) (ts->stop_time / 1000000), | 
|  | (long int) (ts->stop_time % 1000000)); | 
|  |  | 
|  | /* Now report any per-tracepoint status available.  */ | 
|  | for (breakpoint *t : all_tracepoints ()) | 
|  | target_get_tracepoint_status (t, NULL); | 
|  | } | 
|  |  | 
|  | /* Report the trace status to uiout, in a way suitable for MI, and not | 
|  | suitable for CLI.  If ON_STOP is true, suppress a few fields that | 
|  | are not meaningful in the -trace-stop response. | 
|  |  | 
|  | The implementation is essentially parallel to trace_status_command, but | 
|  | merging them will result in unreadable code.  */ | 
|  | void | 
|  | trace_status_mi (int on_stop) | 
|  | { | 
|  | struct ui_out *uiout = current_uiout; | 
|  | struct trace_status *ts = current_trace_status (); | 
|  | int status; | 
|  |  | 
|  | status = target_get_trace_status (ts); | 
|  |  | 
|  | if (status == -1 && ts->filename == NULL) | 
|  | { | 
|  | uiout->field_string ("supported", "0"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (ts->filename != NULL) | 
|  | uiout->field_string ("supported", "file"); | 
|  | else if (!on_stop) | 
|  | uiout->field_string ("supported", "1"); | 
|  |  | 
|  | if (ts->filename != NULL) | 
|  | uiout->field_string ("trace-file", ts->filename); | 
|  |  | 
|  | gdb_assert (ts->running_known); | 
|  |  | 
|  | if (ts->running) | 
|  | { | 
|  | uiout->field_string ("running", "1"); | 
|  |  | 
|  | /* Unlike CLI, do not show the state of 'disconnected-tracing' variable. | 
|  | Given that the frontend gets the status either on -trace-stop, or from | 
|  | -trace-status after re-connection, it does not seem like this | 
|  | information is necessary for anything.  It is not necessary for either | 
|  | figuring the vital state of the target nor for navigation of trace | 
|  | frames.  If the frontend wants to show the current state is some | 
|  | configure dialog, it can request the value when such dialog is | 
|  | invoked by the user.  */ | 
|  | } | 
|  | else | 
|  | { | 
|  | const char *stop_reason = NULL; | 
|  | int stopping_tracepoint = -1; | 
|  |  | 
|  | if (!on_stop) | 
|  | uiout->field_string ("running", "0"); | 
|  |  | 
|  | if (ts->stop_reason != trace_stop_reason_unknown) | 
|  | { | 
|  | switch (ts->stop_reason) | 
|  | { | 
|  | case trace_stop_command: | 
|  | stop_reason = "request"; | 
|  | break; | 
|  | case trace_buffer_full: | 
|  | stop_reason = "overflow"; | 
|  | break; | 
|  | case trace_disconnected: | 
|  | stop_reason = "disconnection"; | 
|  | break; | 
|  | case tracepoint_passcount: | 
|  | stop_reason = "passcount"; | 
|  | stopping_tracepoint = ts->stopping_tracepoint; | 
|  | break; | 
|  | case tracepoint_error: | 
|  | stop_reason = "error"; | 
|  | stopping_tracepoint = ts->stopping_tracepoint; | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (stop_reason) | 
|  | { | 
|  | uiout->field_string ("stop-reason", stop_reason); | 
|  | if (stopping_tracepoint != -1) | 
|  | uiout->field_signed ("stopping-tracepoint", | 
|  | stopping_tracepoint); | 
|  | if (ts->stop_reason == tracepoint_error) | 
|  | uiout->field_string ("error-description", | 
|  | ts->stop_desc); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (ts->traceframe_count != -1) | 
|  | uiout->field_signed ("frames", ts->traceframe_count); | 
|  | if (ts->traceframes_created != -1) | 
|  | uiout->field_signed ("frames-created", ts->traceframes_created); | 
|  | if (ts->buffer_size != -1) | 
|  | uiout->field_signed ("buffer-size", ts->buffer_size); | 
|  | if (ts->buffer_free != -1) | 
|  | uiout->field_signed ("buffer-free", ts->buffer_free); | 
|  |  | 
|  | uiout->field_signed ("disconnected",  ts->disconnected_tracing); | 
|  | uiout->field_signed ("circular",  ts->circular_buffer); | 
|  |  | 
|  | uiout->field_string ("user-name", ts->user_name); | 
|  | uiout->field_string ("notes", ts->notes); | 
|  |  | 
|  | { | 
|  | char buf[100]; | 
|  |  | 
|  | xsnprintf (buf, sizeof buf, "%ld.%06ld", | 
|  | (long int) (ts->start_time / 1000000), | 
|  | (long int) (ts->start_time % 1000000)); | 
|  | uiout->field_string ("start-time", buf); | 
|  | xsnprintf (buf, sizeof buf, "%ld.%06ld", | 
|  | (long int) (ts->stop_time / 1000000), | 
|  | (long int) (ts->stop_time % 1000000)); | 
|  | uiout->field_string ("stop-time", buf); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Check if a trace run is ongoing.  If so, and FROM_TTY, query the | 
|  | user if she really wants to detach.  */ | 
|  |  | 
|  | void | 
|  | query_if_trace_running (int from_tty) | 
|  | { | 
|  | if (!from_tty) | 
|  | return; | 
|  |  | 
|  | /* It can happen that the target that was tracing went away on its | 
|  | own, and we didn't notice.  Get a status update, and if the | 
|  | current target doesn't even do tracing, then assume it's not | 
|  | running anymore.  */ | 
|  | if (target_get_trace_status (current_trace_status ()) < 0) | 
|  | current_trace_status ()->running = 0; | 
|  |  | 
|  | /* If running interactively, give the user the option to cancel and | 
|  | then decide what to do differently with the run.  Scripts are | 
|  | just going to disconnect and let the target deal with it, | 
|  | according to how it's been instructed previously via | 
|  | disconnected-tracing.  */ | 
|  | if (current_trace_status ()->running) | 
|  | { | 
|  | process_tracepoint_on_disconnect (); | 
|  |  | 
|  | if (current_trace_status ()->disconnected_tracing) | 
|  | { | 
|  | if (!query (_("Trace is running and will " | 
|  | "continue after detach; detach anyway? "))) | 
|  | error (_("Not confirmed.")); | 
|  | } | 
|  | else | 
|  | { | 
|  | if (!query (_("Trace is running but will " | 
|  | "stop on detach; detach anyway? "))) | 
|  | error (_("Not confirmed.")); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* This function handles the details of what to do about an ongoing | 
|  | tracing run if the user has asked to detach or otherwise disconnect | 
|  | from the target.  */ | 
|  |  | 
|  | void | 
|  | disconnect_tracing (void) | 
|  | { | 
|  | /* Also we want to be out of tfind mode, otherwise things can get | 
|  | confusing upon reconnection.  Just use these calls instead of | 
|  | full tfind_1 behavior because we're in the middle of detaching, | 
|  | and there's no point to updating current stack frame etc.  */ | 
|  | trace_reset_local_state (); | 
|  | } | 
|  |  | 
|  | /* Worker function for the various flavors of the tfind command.  */ | 
|  | void | 
|  | tfind_1 (enum trace_find_type type, int num, | 
|  | CORE_ADDR addr1, CORE_ADDR addr2, | 
|  | int from_tty) | 
|  | { | 
|  | int target_frameno = -1, target_tracept = -1; | 
|  | struct frame_id old_frame_id = null_frame_id; | 
|  | struct tracepoint *tp; | 
|  | struct ui_out *uiout = current_uiout; | 
|  |  | 
|  | /* Only try to get the current stack frame if we have a chance of | 
|  | succeeding.  In particular, if we're trying to get a first trace | 
|  | frame while all threads are running, it's not going to succeed, | 
|  | so leave it with a default value and let the frame comparison | 
|  | below (correctly) decide to print out the source location of the | 
|  | trace frame.  */ | 
|  | if (!(type == tfind_number && num == -1) | 
|  | && (has_stack_frames () || traceframe_number >= 0)) | 
|  | old_frame_id = get_frame_id (get_current_frame ()); | 
|  |  | 
|  | target_frameno = target_trace_find (type, num, addr1, addr2, | 
|  | &target_tracept); | 
|  |  | 
|  | if (type == tfind_number | 
|  | && num == -1 | 
|  | && target_frameno == -1) | 
|  | { | 
|  | /* We told the target to get out of tfind mode, and it did.  */ | 
|  | } | 
|  | else if (target_frameno == -1) | 
|  | { | 
|  | /* A request for a non-existent trace frame has failed. | 
|  | Our response will be different, depending on FROM_TTY: | 
|  |  | 
|  | If FROM_TTY is true, meaning that this command was | 
|  | typed interactively by the user, then give an error | 
|  | and DO NOT change the state of traceframe_number etc. | 
|  |  | 
|  | However if FROM_TTY is false, meaning that we're either | 
|  | in a script, a loop, or a user-defined command, then | 
|  | DON'T give an error, but DO change the state of | 
|  | traceframe_number etc. to invalid. | 
|  |  | 
|  | The rationale is that if you typed the command, you | 
|  | might just have committed a typo or something, and you'd | 
|  | like to NOT lose your current debugging state.  However | 
|  | if you're in a user-defined command or especially in a | 
|  | loop, then you need a way to detect that the command | 
|  | failed WITHOUT aborting.  This allows you to write | 
|  | scripts that search thru the trace buffer until the end, | 
|  | and then continue on to do something else.  */ | 
|  |  | 
|  | if (from_tty) | 
|  | error (_("Target failed to find requested trace frame.")); | 
|  | else | 
|  | { | 
|  | if (info_verbose) | 
|  | gdb_printf ("End of trace buffer.\n"); | 
|  | #if 0 /* dubious now?  */ | 
|  | /* The following will not recurse, since it's | 
|  | special-cased.  */ | 
|  | tfind_command ("-1", from_tty); | 
|  | #endif | 
|  | } | 
|  | } | 
|  |  | 
|  | tp = get_tracepoint_by_number_on_target (target_tracept); | 
|  |  | 
|  | reinit_frame_cache (); | 
|  | target_dcache_invalidate (); | 
|  |  | 
|  | set_tracepoint_num (tp ? tp->number : target_tracept); | 
|  |  | 
|  | if (target_frameno != get_traceframe_number ()) | 
|  | gdb::observers::traceframe_changed.notify (target_frameno, tracepoint_number); | 
|  |  | 
|  | set_current_traceframe (target_frameno); | 
|  |  | 
|  | if (target_frameno == -1) | 
|  | set_traceframe_context (NULL); | 
|  | else | 
|  | set_traceframe_context (get_current_frame ()); | 
|  |  | 
|  | if (traceframe_number >= 0) | 
|  | { | 
|  | /* Use different branches for MI and CLI to make CLI messages | 
|  | i18n-eable.  */ | 
|  | if (uiout->is_mi_like_p ()) | 
|  | { | 
|  | uiout->field_string ("found", "1"); | 
|  | uiout->field_signed ("tracepoint", tracepoint_number); | 
|  | uiout->field_signed ("traceframe", traceframe_number); | 
|  | } | 
|  | else | 
|  | { | 
|  | gdb_printf (_("Found trace frame %d, tracepoint %d\n"), | 
|  | traceframe_number, tracepoint_number); | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | if (uiout->is_mi_like_p ()) | 
|  | uiout->field_string ("found", "0"); | 
|  | else if (type == tfind_number && num == -1) | 
|  | gdb_printf (_("No longer looking at any trace frame\n")); | 
|  | else /* This case may never occur, check.  */ | 
|  | gdb_printf (_("No trace frame found\n")); | 
|  | } | 
|  |  | 
|  | /* If we're in nonstop mode and getting out of looking at trace | 
|  | frames, there won't be any current frame to go back to and | 
|  | display.  */ | 
|  | if (from_tty | 
|  | && (has_stack_frames () || traceframe_number >= 0)) | 
|  | { | 
|  | enum print_what print_what; | 
|  |  | 
|  | /* NOTE: in imitation of the step command, try to determine | 
|  | whether we have made a transition from one function to | 
|  | another.  If so, we'll print the "stack frame" (ie. the new | 
|  | function and it's arguments) -- otherwise we'll just show the | 
|  | new source line.  */ | 
|  |  | 
|  | if (old_frame_id == get_frame_id (get_current_frame ())) | 
|  | print_what = SRC_LINE; | 
|  | else | 
|  | print_what = SRC_AND_LOC; | 
|  |  | 
|  | print_stack_frame (get_selected_frame (NULL), 1, print_what, 1); | 
|  | do_displays (); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Error on looking at traceframes while trace is running.  */ | 
|  |  | 
|  | void | 
|  | check_trace_running (struct trace_status *status) | 
|  | { | 
|  | if (status->running && status->filename == NULL) | 
|  | error (_("May not look at trace frames while trace is running.")); | 
|  | } | 
|  |  | 
|  | /* trace_find_command takes a trace frame number n, | 
|  | sends "QTFrame:<n>" to the target, | 
|  | and accepts a reply that may contain several optional pieces | 
|  | of information: a frame number, a tracepoint number, and an | 
|  | indication of whether this is a trap frame or a stepping frame. | 
|  |  | 
|  | The minimal response is just "OK" (which indicates that the | 
|  | target does not give us a frame number or a tracepoint number). | 
|  | Instead of that, the target may send us a string containing | 
|  | any combination of: | 
|  | F<hexnum>    (gives the selected frame number) | 
|  | T<hexnum>    (gives the selected tracepoint number) | 
|  | */ | 
|  |  | 
|  | /* tfind command */ | 
|  | static void | 
|  | tfind_command_1 (const char *args, int from_tty) | 
|  | { /* This should only be called with a numeric argument.  */ | 
|  | int frameno = -1; | 
|  |  | 
|  | check_trace_running (current_trace_status ()); | 
|  |  | 
|  | if (args == 0 || *args == 0) | 
|  | { /* TFIND with no args means find NEXT trace frame.  */ | 
|  | if (traceframe_number == -1) | 
|  | frameno = 0;	/* "next" is first one.  */ | 
|  | else | 
|  | frameno = traceframe_number + 1; | 
|  | } | 
|  | else if (0 == strcmp (args, "-")) | 
|  | { | 
|  | if (traceframe_number == -1) | 
|  | error (_("not debugging trace buffer")); | 
|  | else if (from_tty && traceframe_number == 0) | 
|  | error (_("already at start of trace buffer")); | 
|  |  | 
|  | frameno = traceframe_number - 1; | 
|  | } | 
|  | /* A hack to work around eval's need for fp to have been collected.  */ | 
|  | else if (0 == strcmp (args, "-1")) | 
|  | frameno = -1; | 
|  | else | 
|  | frameno = parse_and_eval_long (args); | 
|  |  | 
|  | if (frameno < -1) | 
|  | error (_("invalid input (%d is less than zero)"), frameno); | 
|  |  | 
|  | tfind_1 (tfind_number, frameno, 0, 0, from_tty); | 
|  | } | 
|  |  | 
|  | static void | 
|  | tfind_command (const char *args, int from_tty) | 
|  | { | 
|  | tfind_command_1 (args, from_tty); | 
|  | } | 
|  |  | 
|  | /* tfind end */ | 
|  | static void | 
|  | tfind_end_command (const char *args, int from_tty) | 
|  | { | 
|  | tfind_command_1 ("-1", from_tty); | 
|  | } | 
|  |  | 
|  | /* tfind start */ | 
|  | static void | 
|  | tfind_start_command (const char *args, int from_tty) | 
|  | { | 
|  | tfind_command_1 ("0", from_tty); | 
|  | } | 
|  |  | 
|  | /* tfind pc command */ | 
|  | static void | 
|  | tfind_pc_command (const char *args, int from_tty) | 
|  | { | 
|  | CORE_ADDR pc; | 
|  |  | 
|  | check_trace_running (current_trace_status ()); | 
|  |  | 
|  | if (args == 0 || *args == 0) | 
|  | pc = regcache_read_pc (get_current_regcache ()); | 
|  | else | 
|  | pc = parse_and_eval_address (args); | 
|  |  | 
|  | tfind_1 (tfind_pc, 0, pc, 0, from_tty); | 
|  | } | 
|  |  | 
|  | /* tfind tracepoint command */ | 
|  | static void | 
|  | tfind_tracepoint_command (const char *args, int from_tty) | 
|  | { | 
|  | int tdp; | 
|  | struct tracepoint *tp; | 
|  |  | 
|  | check_trace_running (current_trace_status ()); | 
|  |  | 
|  | if (args == 0 || *args == 0) | 
|  | { | 
|  | if (tracepoint_number == -1) | 
|  | error (_("No current tracepoint -- please supply an argument.")); | 
|  | else | 
|  | tdp = tracepoint_number;	/* Default is current TDP.  */ | 
|  | } | 
|  | else | 
|  | tdp = parse_and_eval_long (args); | 
|  |  | 
|  | /* If we have the tracepoint on hand, use the number that the | 
|  | target knows about (which may be different if we disconnected | 
|  | and reconnected).  */ | 
|  | tp = get_tracepoint (tdp); | 
|  | if (tp) | 
|  | tdp = tp->number_on_target; | 
|  |  | 
|  | tfind_1 (tfind_tp, tdp, 0, 0, from_tty); | 
|  | } | 
|  |  | 
|  | /* TFIND LINE command: | 
|  |  | 
|  | This command will take a sourceline for argument, just like BREAK | 
|  | or TRACE (ie. anything that "decode_line_1" can handle). | 
|  |  | 
|  | With no argument, this command will find the next trace frame | 
|  | corresponding to a source line OTHER THAN THE CURRENT ONE.  */ | 
|  |  | 
|  | static void | 
|  | tfind_line_command (const char *args, int from_tty) | 
|  | { | 
|  | check_trace_running (current_trace_status ()); | 
|  |  | 
|  | symtab_and_line sal; | 
|  | if (args == 0 || *args == 0) | 
|  | { | 
|  | sal = find_pc_line (get_frame_pc (get_current_frame ()), 0); | 
|  | } | 
|  | else | 
|  | { | 
|  | std::vector<symtab_and_line> sals | 
|  | = decode_line_with_current_source (args, DECODE_LINE_FUNFIRSTLINE); | 
|  | sal = sals[0]; | 
|  | } | 
|  |  | 
|  | if (sal.symtab == 0) | 
|  | error (_("No line number information available.")); | 
|  |  | 
|  | CORE_ADDR start_pc, end_pc; | 
|  | if (sal.line > 0 && find_line_pc_range (sal, &start_pc, &end_pc)) | 
|  | { | 
|  | if (start_pc == end_pc) | 
|  | { | 
|  | gdb_printf ("Line %d of \"%s\"", | 
|  | sal.line, | 
|  | symtab_to_filename_for_display (sal.symtab)); | 
|  | gdb_stdout->wrap_here (2); | 
|  | gdb_printf (" is at address "); | 
|  | print_address (get_current_arch (), start_pc, gdb_stdout); | 
|  | gdb_stdout->wrap_here (2); | 
|  | gdb_printf (" but contains no code.\n"); | 
|  | sal = find_pc_line (start_pc, 0); | 
|  | if (sal.line > 0 | 
|  | && find_line_pc_range (sal, &start_pc, &end_pc) | 
|  | && start_pc != end_pc) | 
|  | gdb_printf ("Attempting to find line %d instead.\n", | 
|  | sal.line); | 
|  | else | 
|  | error (_("Cannot find a good line.")); | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | /* Is there any case in which we get here, and have an address | 
|  | which the user would want to see?  If we have debugging | 
|  | symbols and no line numbers?  */ | 
|  | error (_("Line number %d is out of range for \"%s\"."), | 
|  | sal.line, symtab_to_filename_for_display (sal.symtab)); | 
|  | } | 
|  |  | 
|  | /* Find within range of stated line.  */ | 
|  | if (args && *args) | 
|  | tfind_1 (tfind_range, 0, start_pc, end_pc - 1, from_tty); | 
|  | else | 
|  | tfind_1 (tfind_outside, 0, start_pc, end_pc - 1, from_tty); | 
|  | } | 
|  |  | 
|  | /* tfind range command */ | 
|  | static void | 
|  | tfind_range_command (const char *args, int from_tty) | 
|  | { | 
|  | static CORE_ADDR start, stop; | 
|  | const char *tmp; | 
|  |  | 
|  | check_trace_running (current_trace_status ()); | 
|  |  | 
|  | if (args == 0 || *args == 0) | 
|  | { /* XXX FIXME: what should default behavior be?  */ | 
|  | gdb_printf ("Usage: tfind range STARTADDR, ENDADDR\n"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (0 != (tmp = strchr (args, ','))) | 
|  | { | 
|  | std::string start_addr (args, tmp); | 
|  | ++tmp; | 
|  | tmp = skip_spaces (tmp); | 
|  | start = parse_and_eval_address (start_addr.c_str ()); | 
|  | stop = parse_and_eval_address (tmp); | 
|  | } | 
|  | else | 
|  | {			/* No explicit end address?  */ | 
|  | start = parse_and_eval_address (args); | 
|  | stop = start + 1;	/* ??? */ | 
|  | } | 
|  |  | 
|  | tfind_1 (tfind_range, 0, start, stop, from_tty); | 
|  | } | 
|  |  | 
|  | /* tfind outside command */ | 
|  | static void | 
|  | tfind_outside_command (const char *args, int from_tty) | 
|  | { | 
|  | CORE_ADDR start, stop; | 
|  | const char *tmp; | 
|  |  | 
|  | if (current_trace_status ()->running | 
|  | && current_trace_status ()->filename == NULL) | 
|  | error (_("May not look at trace frames while trace is running.")); | 
|  |  | 
|  | if (args == 0 || *args == 0) | 
|  | { /* XXX FIXME: what should default behavior be?  */ | 
|  | gdb_printf ("Usage: tfind outside STARTADDR, ENDADDR\n"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (0 != (tmp = strchr (args, ','))) | 
|  | { | 
|  | std::string start_addr (args, tmp); | 
|  | ++tmp; | 
|  | tmp = skip_spaces (tmp); | 
|  | start = parse_and_eval_address (start_addr.c_str ()); | 
|  | stop = parse_and_eval_address (tmp); | 
|  | } | 
|  | else | 
|  | {			/* No explicit end address?  */ | 
|  | start = parse_and_eval_address (args); | 
|  | stop = start + 1;	/* ??? */ | 
|  | } | 
|  |  | 
|  | tfind_1 (tfind_outside, 0, start, stop, from_tty); | 
|  | } | 
|  |  | 
|  | /* info scope command: list the locals for a scope.  */ | 
|  | static void | 
|  | info_scope_command (const char *args_in, int from_tty) | 
|  | { | 
|  | struct symbol *sym; | 
|  | struct bound_minimal_symbol msym; | 
|  | const struct block *block; | 
|  | const char *symname; | 
|  | const char *save_args = args_in; | 
|  | struct block_iterator iter; | 
|  | int j, count = 0; | 
|  | struct gdbarch *gdbarch; | 
|  | int regno; | 
|  | const char *args = args_in; | 
|  |  | 
|  | if (args == 0 || *args == 0) | 
|  | error (_("requires an argument (function, " | 
|  | "line or *addr) to define a scope")); | 
|  |  | 
|  | location_spec_up locspec = string_to_location_spec (&args, | 
|  | current_language); | 
|  | std::vector<symtab_and_line> sals | 
|  | = decode_line_1 (locspec.get (), DECODE_LINE_FUNFIRSTLINE, | 
|  | NULL, NULL, 0); | 
|  | if (sals.empty ()) | 
|  | { | 
|  | /* Presumably decode_line_1 has already warned.  */ | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* Resolve line numbers to PC.  */ | 
|  | resolve_sal_pc (&sals[0]); | 
|  | block = block_for_pc (sals[0].pc); | 
|  |  | 
|  | while (block != 0) | 
|  | { | 
|  | QUIT;			/* Allow user to bail out with ^C.  */ | 
|  | ALL_BLOCK_SYMBOLS (block, iter, sym) | 
|  | { | 
|  | QUIT;			/* Allow user to bail out with ^C.  */ | 
|  | if (count == 0) | 
|  | gdb_printf ("Scope for %s:\n", save_args); | 
|  | count++; | 
|  |  | 
|  | symname = sym->print_name (); | 
|  | if (symname == NULL || *symname == '\0') | 
|  | continue;		/* Probably botched, certainly useless.  */ | 
|  |  | 
|  | gdbarch = sym->arch (); | 
|  |  | 
|  | gdb_printf ("Symbol %s is ", symname); | 
|  |  | 
|  | if (SYMBOL_COMPUTED_OPS (sym) != NULL) | 
|  | SYMBOL_COMPUTED_OPS (sym)->describe_location (sym, | 
|  | block->entry_pc (), | 
|  | gdb_stdout); | 
|  | else | 
|  | { | 
|  | switch (sym->aclass ()) | 
|  | { | 
|  | default: | 
|  | case LOC_UNDEF:	/* Messed up symbol?  */ | 
|  | gdb_printf ("a bogus symbol, class %d.\n", | 
|  | sym->aclass ()); | 
|  | count--;		/* Don't count this one.  */ | 
|  | continue; | 
|  | case LOC_CONST: | 
|  | gdb_printf ("a constant with value %s (%s)", | 
|  | plongest (sym->value_longest ()), | 
|  | hex_string (sym->value_longest ())); | 
|  | break; | 
|  | case LOC_CONST_BYTES: | 
|  | gdb_printf ("constant bytes: "); | 
|  | if (sym->type ()) | 
|  | for (j = 0; j < sym->type ()->length (); j++) | 
|  | gdb_printf (" %02x", (unsigned) sym->value_bytes ()[j]); | 
|  | break; | 
|  | case LOC_STATIC: | 
|  | gdb_printf ("in static storage at address "); | 
|  | gdb_printf ("%s", paddress (gdbarch, sym->value_address ())); | 
|  | break; | 
|  | case LOC_REGISTER: | 
|  | /* GDBARCH is the architecture associated with the objfile | 
|  | the symbol is defined in; the target architecture may be | 
|  | different, and may provide additional registers.  However, | 
|  | we do not know the target architecture at this point. | 
|  | We assume the objfile architecture will contain all the | 
|  | standard registers that occur in debug info in that | 
|  | objfile.  */ | 
|  | regno = SYMBOL_REGISTER_OPS (sym)->register_number (sym, | 
|  | gdbarch); | 
|  |  | 
|  | if (sym->is_argument ()) | 
|  | gdb_printf ("an argument in register $%s", | 
|  | gdbarch_register_name (gdbarch, regno)); | 
|  | else | 
|  | gdb_printf ("a local variable in register $%s", | 
|  | gdbarch_register_name (gdbarch, regno)); | 
|  | break; | 
|  | case LOC_ARG: | 
|  | gdb_printf ("an argument at stack/frame offset %s", | 
|  | plongest (sym->value_longest ())); | 
|  | break; | 
|  | case LOC_LOCAL: | 
|  | gdb_printf ("a local variable at frame offset %s", | 
|  | plongest (sym->value_longest ())); | 
|  | break; | 
|  | case LOC_REF_ARG: | 
|  | gdb_printf ("a reference argument at offset %s", | 
|  | plongest (sym->value_longest ())); | 
|  | break; | 
|  | case LOC_REGPARM_ADDR: | 
|  | /* Note comment at LOC_REGISTER.  */ | 
|  | regno = SYMBOL_REGISTER_OPS (sym)->register_number (sym, | 
|  | gdbarch); | 
|  | gdb_printf ("the address of an argument, in register $%s", | 
|  | gdbarch_register_name (gdbarch, regno)); | 
|  | break; | 
|  | case LOC_TYPEDEF: | 
|  | gdb_printf ("a typedef.\n"); | 
|  | continue; | 
|  | case LOC_LABEL: | 
|  | gdb_printf ("a label at address "); | 
|  | gdb_printf ("%s", paddress (gdbarch, sym->value_address ())); | 
|  | break; | 
|  | case LOC_BLOCK: | 
|  | gdb_printf ("a function at address "); | 
|  | gdb_printf ("%s", | 
|  | paddress (gdbarch, | 
|  | sym->value_block ()->entry_pc ())); | 
|  | break; | 
|  | case LOC_UNRESOLVED: | 
|  | msym = lookup_minimal_symbol (sym->linkage_name (), | 
|  | NULL, NULL); | 
|  | if (msym.minsym == NULL) | 
|  | gdb_printf ("Unresolved Static"); | 
|  | else | 
|  | { | 
|  | gdb_printf ("static storage at address "); | 
|  | gdb_printf ("%s", | 
|  | paddress (gdbarch, msym.value_address ())); | 
|  | } | 
|  | break; | 
|  | case LOC_OPTIMIZED_OUT: | 
|  | gdb_printf ("optimized out.\n"); | 
|  | continue; | 
|  | case LOC_COMPUTED: | 
|  | gdb_assert_not_reached ("LOC_COMPUTED variable missing a method"); | 
|  | } | 
|  | } | 
|  | if (sym->type ()) | 
|  | { | 
|  | struct type *t = check_typedef (sym->type ()); | 
|  |  | 
|  | gdb_printf (", length %s.\n", pulongest (t->length ())); | 
|  | } | 
|  | } | 
|  | if (block->function ()) | 
|  | break; | 
|  | else | 
|  | block = block->superblock (); | 
|  | } | 
|  | if (count <= 0) | 
|  | gdb_printf ("Scope for %s contains no locals or arguments.\n", | 
|  | save_args); | 
|  | } | 
|  |  | 
|  | /* Helper for trace_dump_command.  Dump the action list starting at | 
|  | ACTION.  STEPPING_ACTIONS is true if we're iterating over the | 
|  | actions of the body of a while-stepping action.  STEPPING_FRAME is | 
|  | set if the current traceframe was determined to be a while-stepping | 
|  | traceframe.  */ | 
|  |  | 
|  | static void | 
|  | trace_dump_actions (struct command_line *action, | 
|  | int stepping_actions, int stepping_frame, | 
|  | int from_tty) | 
|  | { | 
|  | const char *action_exp, *next_comma; | 
|  |  | 
|  | for (; action != NULL; action = action->next) | 
|  | { | 
|  | struct cmd_list_element *cmd; | 
|  |  | 
|  | QUIT;			/* Allow user to bail out with ^C.  */ | 
|  | action_exp = action->line; | 
|  | action_exp = skip_spaces (action_exp); | 
|  |  | 
|  | /* The collection actions to be done while stepping are | 
|  | bracketed by the commands "while-stepping" and "end".  */ | 
|  |  | 
|  | if (*action_exp == '#')	/* comment line */ | 
|  | continue; | 
|  |  | 
|  | cmd = lookup_cmd (&action_exp, cmdlist, "", NULL, -1, 1); | 
|  | if (cmd == 0) | 
|  | error (_("Bad action list item: %s"), action_exp); | 
|  |  | 
|  | if (cmd_simple_func_eq (cmd, while_stepping_pseudocommand)) | 
|  | { | 
|  | gdb_assert (action->body_list_1 == nullptr); | 
|  | trace_dump_actions (action->body_list_0.get (), | 
|  | 1, stepping_frame, from_tty); | 
|  | } | 
|  | else if (cmd_simple_func_eq (cmd, collect_pseudocommand)) | 
|  | { | 
|  | /* Display the collected data. | 
|  | For the trap frame, display only what was collected at | 
|  | the trap.  Likewise for stepping frames, display only | 
|  | what was collected while stepping.  This means that the | 
|  | two boolean variables, STEPPING_FRAME and | 
|  | STEPPING_ACTIONS should be equal.  */ | 
|  | if (stepping_frame == stepping_actions) | 
|  | { | 
|  | int trace_string = 0; | 
|  |  | 
|  | if (*action_exp == '/') | 
|  | action_exp = decode_agent_options (action_exp, &trace_string); | 
|  |  | 
|  | do | 
|  | {		/* Repeat over a comma-separated list.  */ | 
|  | QUIT;		/* Allow user to bail out with ^C.  */ | 
|  | if (*action_exp == ',') | 
|  | action_exp++; | 
|  | action_exp = skip_spaces (action_exp); | 
|  |  | 
|  | next_comma = strchr (action_exp, ','); | 
|  |  | 
|  | if (0 == strncasecmp (action_exp, "$reg", 4)) | 
|  | registers_info (NULL, from_tty); | 
|  | else if (0 == strncasecmp (action_exp, "$_ret", 5)) | 
|  | ; | 
|  | else if (0 == strncasecmp (action_exp, "$loc", 4)) | 
|  | info_locals_command (NULL, from_tty); | 
|  | else if (0 == strncasecmp (action_exp, "$arg", 4)) | 
|  | info_args_command (NULL, from_tty); | 
|  | else | 
|  | {		/* variable */ | 
|  | std::string contents; | 
|  | const char *exp = action_exp; | 
|  | if (next_comma != NULL) | 
|  | { | 
|  | size_t len = next_comma - action_exp; | 
|  | contents = std::string (action_exp, len); | 
|  | exp = contents.c_str (); | 
|  | } | 
|  |  | 
|  | gdb_printf ("%s = ", exp); | 
|  | output_command (exp, from_tty); | 
|  | gdb_printf ("\n"); | 
|  | } | 
|  | action_exp = next_comma; | 
|  | } | 
|  | while (action_exp && *action_exp == ','); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Return bp_location of the tracepoint associated with the current | 
|  | traceframe.  Set *STEPPING_FRAME_P to 1 if the current traceframe | 
|  | is a stepping traceframe.  */ | 
|  |  | 
|  | struct bp_location * | 
|  | get_traceframe_location (int *stepping_frame_p) | 
|  | { | 
|  | struct tracepoint *t; | 
|  | struct regcache *regcache; | 
|  |  | 
|  | if (tracepoint_number == -1) | 
|  | error (_("No current trace frame.")); | 
|  |  | 
|  | t = get_tracepoint (tracepoint_number); | 
|  |  | 
|  | if (t == NULL) | 
|  | error (_("No known tracepoint matches 'current' tracepoint #%d."), | 
|  | tracepoint_number); | 
|  |  | 
|  | /* The current frame is a trap frame if the frame PC is equal to the | 
|  | tracepoint PC.  If not, then the current frame was collected | 
|  | during single-stepping.  */ | 
|  | regcache = get_current_regcache (); | 
|  |  | 
|  | /* If the traceframe's address matches any of the tracepoint's | 
|  | locations, assume it is a direct hit rather than a while-stepping | 
|  | frame.  (FIXME this is not reliable, should record each frame's | 
|  | type.)  */ | 
|  | for (bp_location *tloc : t->locations ()) | 
|  | if (tloc->address == regcache_read_pc (regcache)) | 
|  | { | 
|  | *stepping_frame_p = 0; | 
|  | return tloc; | 
|  | } | 
|  |  | 
|  | /* If this is a stepping frame, we don't know which location | 
|  | triggered.  The first is as good (or bad) a guess as any...  */ | 
|  | *stepping_frame_p = 1; | 
|  | return t->loc; | 
|  | } | 
|  |  | 
|  | /* Return the default collect actions of a tracepoint T.  */ | 
|  |  | 
|  | static counted_command_line | 
|  | all_tracepoint_actions (struct breakpoint *t) | 
|  | { | 
|  | counted_command_line actions (nullptr, command_lines_deleter ()); | 
|  |  | 
|  | /* If there are default expressions to collect, make up a collect | 
|  | action and prepend to the action list to encode.  Note that since | 
|  | validation is per-tracepoint (local var "xyz" might be valid for | 
|  | one tracepoint and not another, etc), we make up the action on | 
|  | the fly, and don't cache it.  */ | 
|  | if (!default_collect.empty ()) | 
|  | { | 
|  | gdb::unique_xmalloc_ptr<char> default_collect_line | 
|  | = xstrprintf ("collect %s", default_collect.c_str ()); | 
|  |  | 
|  | validate_actionline (default_collect_line.get (), t); | 
|  | actions.reset (new struct command_line (simple_control, | 
|  | default_collect_line.release ()), | 
|  | command_lines_deleter ()); | 
|  | } | 
|  |  | 
|  | return actions; | 
|  | } | 
|  |  | 
|  | /* The tdump command.  */ | 
|  |  | 
|  | static void | 
|  | tdump_command (const char *args, int from_tty) | 
|  | { | 
|  | int stepping_frame = 0; | 
|  | struct bp_location *loc; | 
|  |  | 
|  | /* This throws an error is not inspecting a trace frame.  */ | 
|  | loc = get_traceframe_location (&stepping_frame); | 
|  |  | 
|  | gdb_printf ("Data collected at tracepoint %d, trace frame %d:\n", | 
|  | tracepoint_number, traceframe_number); | 
|  |  | 
|  | /* This command only makes sense for the current frame, not the | 
|  | selected frame.  */ | 
|  | scoped_restore_current_thread restore_thread; | 
|  |  | 
|  | select_frame (get_current_frame ()); | 
|  |  | 
|  | counted_command_line actions = all_tracepoint_actions (loc->owner); | 
|  |  | 
|  | trace_dump_actions (actions.get (), 0, stepping_frame, from_tty); | 
|  | trace_dump_actions (breakpoint_commands (loc->owner), 0, stepping_frame, | 
|  | from_tty); | 
|  | } | 
|  |  | 
|  | /* Encode a piece of a tracepoint's source-level definition in a form | 
|  | that is suitable for both protocol and saving in files.  */ | 
|  | /* This version does not do multiple encodes for long strings; it should | 
|  | return an offset to the next piece to encode.  FIXME  */ | 
|  |  | 
|  | int | 
|  | encode_source_string (int tpnum, ULONGEST addr, | 
|  | const char *srctype, const char *src, | 
|  | char *buf, int buf_size) | 
|  | { | 
|  | if (80 + strlen (srctype) > buf_size) | 
|  | error (_("Buffer too small for source encoding")); | 
|  | sprintf (buf, "%x:%s:%s:%x:%x:", | 
|  | tpnum, phex_nz (addr, sizeof (addr)), | 
|  | srctype, 0, (int) strlen (src)); | 
|  | if (strlen (buf) + strlen (src) * 2 >= buf_size) | 
|  | error (_("Source string too long for buffer")); | 
|  | bin2hex ((gdb_byte *) src, buf + strlen (buf), strlen (src)); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | /* Tell the target what to do with an ongoing tracing run if GDB | 
|  | disconnects for some reason.  */ | 
|  |  | 
|  | static void | 
|  | set_disconnected_tracing (const char *args, int from_tty, | 
|  | struct cmd_list_element *c) | 
|  | { | 
|  | target_set_disconnected_tracing (disconnected_tracing); | 
|  | } | 
|  |  | 
|  | static void | 
|  | set_circular_trace_buffer (const char *args, int from_tty, | 
|  | struct cmd_list_element *c) | 
|  | { | 
|  | target_set_circular_trace_buffer (circular_trace_buffer); | 
|  | } | 
|  |  | 
|  | static void | 
|  | set_trace_buffer_size (const char *args, int from_tty, | 
|  | struct cmd_list_element *c) | 
|  | { | 
|  | target_set_trace_buffer_size (trace_buffer_size); | 
|  | } | 
|  |  | 
|  | static void | 
|  | set_trace_user (const char *args, int from_tty, | 
|  | struct cmd_list_element *c) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | ret = target_set_trace_notes (trace_user.c_str (), NULL, NULL); | 
|  |  | 
|  | if (!ret) | 
|  | warning (_("Target does not support trace notes, user ignored")); | 
|  | } | 
|  |  | 
|  | static void | 
|  | set_trace_notes (const char *args, int from_tty, | 
|  | struct cmd_list_element *c) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | ret = target_set_trace_notes (NULL, trace_notes.c_str (), NULL); | 
|  |  | 
|  | if (!ret) | 
|  | warning (_("Target does not support trace notes, note ignored")); | 
|  | } | 
|  |  | 
|  | static void | 
|  | set_trace_stop_notes (const char *args, int from_tty, | 
|  | struct cmd_list_element *c) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | ret = target_set_trace_notes (NULL, NULL, trace_stop_notes.c_str ()); | 
|  |  | 
|  | if (!ret) | 
|  | warning (_("Target does not support trace notes, stop note ignored")); | 
|  | } | 
|  |  | 
|  | /* Convert the memory pointed to by mem into hex, placing result in buf. | 
|  | * Return a pointer to the last char put in buf (null) | 
|  | * "stolen" from sparc-stub.c | 
|  | */ | 
|  |  | 
|  | static const char hexchars[] = "0123456789abcdef"; | 
|  |  | 
|  | static char * | 
|  | mem2hex (gdb_byte *mem, char *buf, int count) | 
|  | { | 
|  | gdb_byte ch; | 
|  |  | 
|  | while (count-- > 0) | 
|  | { | 
|  | ch = *mem++; | 
|  |  | 
|  | *buf++ = hexchars[ch >> 4]; | 
|  | *buf++ = hexchars[ch & 0xf]; | 
|  | } | 
|  |  | 
|  | *buf = 0; | 
|  |  | 
|  | return buf; | 
|  | } | 
|  |  | 
|  | int | 
|  | get_traceframe_number (void) | 
|  | { | 
|  | return traceframe_number; | 
|  | } | 
|  |  | 
|  | int | 
|  | get_tracepoint_number (void) | 
|  | { | 
|  | return tracepoint_number; | 
|  | } | 
|  |  | 
|  | /* Make the traceframe NUM be the current trace frame.  Does nothing | 
|  | if NUM is already current.  */ | 
|  |  | 
|  | void | 
|  | set_current_traceframe (int num) | 
|  | { | 
|  | int newnum; | 
|  |  | 
|  | if (traceframe_number == num) | 
|  | { | 
|  | /* Nothing to do.  */ | 
|  | return; | 
|  | } | 
|  |  | 
|  | newnum = target_trace_find (tfind_number, num, 0, 0, NULL); | 
|  |  | 
|  | if (newnum != num) | 
|  | warning (_("could not change traceframe")); | 
|  |  | 
|  | set_traceframe_num (newnum); | 
|  |  | 
|  | /* Changing the traceframe changes our view of registers and of the | 
|  | frame chain.  */ | 
|  | registers_changed (); | 
|  |  | 
|  | clear_traceframe_info (); | 
|  | } | 
|  |  | 
|  | scoped_restore_current_traceframe::scoped_restore_current_traceframe () | 
|  | : m_traceframe_number (traceframe_number) | 
|  | {} | 
|  |  | 
|  | /* Given a number and address, return an uploaded tracepoint with that | 
|  | number, creating if necessary.  */ | 
|  |  | 
|  | struct uploaded_tp * | 
|  | get_uploaded_tp (int num, ULONGEST addr, struct uploaded_tp **utpp) | 
|  | { | 
|  | struct uploaded_tp *utp; | 
|  |  | 
|  | for (utp = *utpp; utp; utp = utp->next) | 
|  | if (utp->number == num && utp->addr == addr) | 
|  | return utp; | 
|  |  | 
|  | utp = new uploaded_tp; | 
|  | utp->number = num; | 
|  | utp->addr = addr; | 
|  | utp->next = *utpp; | 
|  | *utpp = utp; | 
|  |  | 
|  | return utp; | 
|  | } | 
|  |  | 
|  | void | 
|  | free_uploaded_tps (struct uploaded_tp **utpp) | 
|  | { | 
|  | struct uploaded_tp *next_one; | 
|  |  | 
|  | while (*utpp) | 
|  | { | 
|  | next_one = (*utpp)->next; | 
|  | delete *utpp; | 
|  | *utpp = next_one; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Given a number and address, return an uploaded tracepoint with that | 
|  | number, creating if necessary.  */ | 
|  |  | 
|  | struct uploaded_tsv * | 
|  | get_uploaded_tsv (int num, struct uploaded_tsv **utsvp) | 
|  | { | 
|  | struct uploaded_tsv *utsv; | 
|  |  | 
|  | for (utsv = *utsvp; utsv; utsv = utsv->next) | 
|  | if (utsv->number == num) | 
|  | return utsv; | 
|  |  | 
|  | utsv = XCNEW (struct uploaded_tsv); | 
|  | utsv->number = num; | 
|  | utsv->next = *utsvp; | 
|  | *utsvp = utsv; | 
|  |  | 
|  | return utsv; | 
|  | } | 
|  |  | 
|  | void | 
|  | free_uploaded_tsvs (struct uploaded_tsv **utsvp) | 
|  | { | 
|  | struct uploaded_tsv *next_one; | 
|  |  | 
|  | while (*utsvp) | 
|  | { | 
|  | next_one = (*utsvp)->next; | 
|  | xfree (*utsvp); | 
|  | *utsvp = next_one; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* FIXME this function is heuristic and will miss the cases where the | 
|  | conditional is semantically identical but differs in whitespace, | 
|  | such as "x == 0" vs "x==0".  */ | 
|  |  | 
|  | static int | 
|  | cond_string_is_same (char *str1, char *str2) | 
|  | { | 
|  | if (str1 == NULL || str2 == NULL) | 
|  | return (str1 == str2); | 
|  |  | 
|  | return (strcmp (str1, str2) == 0); | 
|  | } | 
|  |  | 
|  | /* Look for an existing tracepoint that seems similar enough to the | 
|  | uploaded one.  Enablement isn't compared, because the user can | 
|  | toggle that freely, and may have done so in anticipation of the | 
|  | next trace run.  Return the location of matched tracepoint.  */ | 
|  |  | 
|  | static struct bp_location * | 
|  | find_matching_tracepoint_location (struct uploaded_tp *utp) | 
|  | { | 
|  | struct bp_location *loc; | 
|  |  | 
|  | for (breakpoint *b : all_tracepoints ()) | 
|  | { | 
|  | struct tracepoint *t = (struct tracepoint *) b; | 
|  |  | 
|  | if (b->type == utp->type | 
|  | && t->step_count == utp->step | 
|  | && t->pass_count == utp->pass | 
|  | && cond_string_is_same (t->cond_string.get (), | 
|  | utp->cond_string.get ()) | 
|  | /* FIXME also test actions.  */ | 
|  | ) | 
|  | { | 
|  | /* Scan the locations for an address match.  */ | 
|  | for (loc = b->loc; loc; loc = loc->next) | 
|  | { | 
|  | if (loc->address == utp->addr) | 
|  | return loc; | 
|  | } | 
|  | } | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /* Given a list of tracepoints uploaded from a target, attempt to | 
|  | match them up with existing tracepoints, and create new ones if not | 
|  | found.  */ | 
|  |  | 
|  | void | 
|  | merge_uploaded_tracepoints (struct uploaded_tp **uploaded_tps) | 
|  | { | 
|  | struct uploaded_tp *utp; | 
|  | /* A set of tracepoints which are modified.  */ | 
|  | std::vector<breakpoint *> modified_tp; | 
|  |  | 
|  | /* Look for GDB tracepoints that match up with our uploaded versions.  */ | 
|  | for (utp = *uploaded_tps; utp; utp = utp->next) | 
|  | { | 
|  | struct bp_location *loc; | 
|  | struct tracepoint *t; | 
|  |  | 
|  | loc = find_matching_tracepoint_location (utp); | 
|  | if (loc) | 
|  | { | 
|  | int found = 0; | 
|  |  | 
|  | /* Mark this location as already inserted.  */ | 
|  | loc->inserted = 1; | 
|  | t = (struct tracepoint *) loc->owner; | 
|  | gdb_printf (_("Assuming tracepoint %d is same " | 
|  | "as target's tracepoint %d at %s.\n"), | 
|  | loc->owner->number, utp->number, | 
|  | paddress (loc->gdbarch, utp->addr)); | 
|  |  | 
|  | /* The tracepoint LOC->owner was modified (the location LOC | 
|  | was marked as inserted in the target).  Save it in | 
|  | MODIFIED_TP if not there yet.  The 'breakpoint-modified' | 
|  | observers will be notified later once for each tracepoint | 
|  | saved in MODIFIED_TP.  */ | 
|  | for (breakpoint *b : modified_tp) | 
|  | if (b == loc->owner) | 
|  | { | 
|  | found = 1; | 
|  | break; | 
|  | } | 
|  | if (!found) | 
|  | modified_tp.push_back (loc->owner); | 
|  | } | 
|  | else | 
|  | { | 
|  | t = create_tracepoint_from_upload (utp); | 
|  | if (t) | 
|  | gdb_printf (_("Created tracepoint %d for " | 
|  | "target's tracepoint %d at %s.\n"), | 
|  | t->number, utp->number, | 
|  | paddress (get_current_arch (), utp->addr)); | 
|  | else | 
|  | gdb_printf (_("Failed to create tracepoint for target's " | 
|  | "tracepoint %d at %s, skipping it.\n"), | 
|  | utp->number, | 
|  | paddress (get_current_arch (), utp->addr)); | 
|  | } | 
|  | /* Whether found or created, record the number used by the | 
|  | target, to help with mapping target tracepoints back to their | 
|  | counterparts here.  */ | 
|  | if (t) | 
|  | t->number_on_target = utp->number; | 
|  | } | 
|  |  | 
|  | /* Notify 'breakpoint-modified' observer that at least one of B's | 
|  | locations was changed.  */ | 
|  | for (breakpoint *b : modified_tp) | 
|  | gdb::observers::breakpoint_modified.notify (b); | 
|  |  | 
|  | free_uploaded_tps (uploaded_tps); | 
|  | } | 
|  |  | 
|  | /* Trace state variables don't have much to identify them beyond their | 
|  | name, so just use that to detect matches.  */ | 
|  |  | 
|  | static struct trace_state_variable * | 
|  | find_matching_tsv (struct uploaded_tsv *utsv) | 
|  | { | 
|  | if (!utsv->name) | 
|  | return NULL; | 
|  |  | 
|  | return find_trace_state_variable (utsv->name); | 
|  | } | 
|  |  | 
|  | static struct trace_state_variable * | 
|  | create_tsv_from_upload (struct uploaded_tsv *utsv) | 
|  | { | 
|  | const char *namebase; | 
|  | std::string buf; | 
|  | int try_num = 0; | 
|  | struct trace_state_variable *tsv; | 
|  |  | 
|  | if (utsv->name) | 
|  | { | 
|  | namebase = utsv->name; | 
|  | buf = namebase; | 
|  | } | 
|  | else | 
|  | { | 
|  | namebase = "__tsv"; | 
|  | buf = string_printf ("%s_%d", namebase, try_num++); | 
|  | } | 
|  |  | 
|  | /* Fish for a name that is not in use.  */ | 
|  | /* (should check against all internal vars?)  */ | 
|  | while (find_trace_state_variable (buf.c_str ())) | 
|  | buf = string_printf ("%s_%d", namebase, try_num++); | 
|  |  | 
|  | /* We have an available name, create the variable.  */ | 
|  | tsv = create_trace_state_variable (buf.c_str ()); | 
|  | tsv->initial_value = utsv->initial_value; | 
|  | tsv->builtin = utsv->builtin; | 
|  |  | 
|  | gdb::observers::tsv_created.notify (tsv); | 
|  |  | 
|  | return tsv; | 
|  | } | 
|  |  | 
|  | /* Given a list of uploaded trace state variables, try to match them | 
|  | up with existing variables, or create additional ones.  */ | 
|  |  | 
|  | void | 
|  | merge_uploaded_trace_state_variables (struct uploaded_tsv **uploaded_tsvs) | 
|  | { | 
|  | struct uploaded_tsv *utsv; | 
|  | int highest; | 
|  |  | 
|  | /* Most likely some numbers will have to be reassigned as part of | 
|  | the merge, so clear them all in anticipation.  */ | 
|  | for (trace_state_variable &tsv : tvariables) | 
|  | tsv.number = 0; | 
|  |  | 
|  | for (utsv = *uploaded_tsvs; utsv; utsv = utsv->next) | 
|  | { | 
|  | struct trace_state_variable *tsv = find_matching_tsv (utsv); | 
|  | if (tsv) | 
|  | { | 
|  | if (info_verbose) | 
|  | gdb_printf (_("Assuming trace state variable $%s " | 
|  | "is same as target's variable %d.\n"), | 
|  | tsv->name.c_str (), utsv->number); | 
|  | } | 
|  | else | 
|  | { | 
|  | tsv = create_tsv_from_upload (utsv); | 
|  | if (info_verbose) | 
|  | gdb_printf (_("Created trace state variable " | 
|  | "$%s for target's variable %d.\n"), | 
|  | tsv->name.c_str (), utsv->number); | 
|  | } | 
|  | /* Give precedence to numberings that come from the target.  */ | 
|  | if (tsv) | 
|  | tsv->number = utsv->number; | 
|  | } | 
|  |  | 
|  | /* Renumber everything that didn't get a target-assigned number.  */ | 
|  | highest = 0; | 
|  | for (const trace_state_variable &tsv : tvariables) | 
|  | highest = std::max (tsv.number, highest); | 
|  |  | 
|  | ++highest; | 
|  | for (trace_state_variable &tsv : tvariables) | 
|  | if (tsv.number == 0) | 
|  | tsv.number = highest++; | 
|  |  | 
|  | free_uploaded_tsvs (uploaded_tsvs); | 
|  | } | 
|  |  | 
|  | /* Parse the part of trace status syntax that is shared between | 
|  | the remote protocol and the trace file reader.  */ | 
|  |  | 
|  | void | 
|  | parse_trace_status (const char *line, struct trace_status *ts) | 
|  | { | 
|  | const char *p = line, *p1, *p2, *p3, *p_temp; | 
|  | int end; | 
|  | ULONGEST val; | 
|  |  | 
|  | ts->running_known = 1; | 
|  | ts->running = (*p++ == '1'); | 
|  | ts->stop_reason = trace_stop_reason_unknown; | 
|  | xfree (ts->stop_desc); | 
|  | ts->stop_desc = NULL; | 
|  | ts->traceframe_count = -1; | 
|  | ts->traceframes_created = -1; | 
|  | ts->buffer_free = -1; | 
|  | ts->buffer_size = -1; | 
|  | ts->disconnected_tracing = 0; | 
|  | ts->circular_buffer = 0; | 
|  | xfree (ts->user_name); | 
|  | ts->user_name = NULL; | 
|  | xfree (ts->notes); | 
|  | ts->notes = NULL; | 
|  | ts->start_time = ts->stop_time = 0; | 
|  |  | 
|  | while (*p++) | 
|  | { | 
|  | p1 = strchr (p, ':'); | 
|  | if (p1 == NULL) | 
|  | error (_("Malformed trace status, at %s\n\ | 
|  | Status line: '%s'\n"), p, line); | 
|  | p3 = strchr (p, ';'); | 
|  | if (p3 == NULL) | 
|  | p3 = p + strlen (p); | 
|  | if (strncmp (p, stop_reason_names[trace_buffer_full], p1 - p) == 0) | 
|  | { | 
|  | p = unpack_varlen_hex (++p1, &val); | 
|  | ts->stop_reason = trace_buffer_full; | 
|  | } | 
|  | else if (strncmp (p, stop_reason_names[trace_never_run], p1 - p) == 0) | 
|  | { | 
|  | p = unpack_varlen_hex (++p1, &val); | 
|  | ts->stop_reason = trace_never_run; | 
|  | } | 
|  | else if (strncmp (p, stop_reason_names[tracepoint_passcount], | 
|  | p1 - p) == 0) | 
|  | { | 
|  | p = unpack_varlen_hex (++p1, &val); | 
|  | ts->stop_reason = tracepoint_passcount; | 
|  | ts->stopping_tracepoint = val; | 
|  | } | 
|  | else if (strncmp (p, stop_reason_names[trace_stop_command], p1 - p) == 0) | 
|  | { | 
|  | p2 = strchr (++p1, ':'); | 
|  | if (!p2 || p2 > p3) | 
|  | { | 
|  | /*older style*/ | 
|  | p2 = p1; | 
|  | } | 
|  | else if (p2 != p1) | 
|  | { | 
|  | ts->stop_desc = (char *) xmalloc (strlen (line)); | 
|  | end = hex2bin (p1, (gdb_byte *) ts->stop_desc, (p2 - p1) / 2); | 
|  | ts->stop_desc[end] = '\0'; | 
|  | } | 
|  | else | 
|  | ts->stop_desc = xstrdup (""); | 
|  |  | 
|  | p = unpack_varlen_hex (++p2, &val); | 
|  | ts->stop_reason = trace_stop_command; | 
|  | } | 
|  | else if (strncmp (p, stop_reason_names[trace_disconnected], p1 - p) == 0) | 
|  | { | 
|  | p = unpack_varlen_hex (++p1, &val); | 
|  | ts->stop_reason = trace_disconnected; | 
|  | } | 
|  | else if (strncmp (p, stop_reason_names[tracepoint_error], p1 - p) == 0) | 
|  | { | 
|  | p2 = strchr (++p1, ':'); | 
|  | if (p2 != p1) | 
|  | { | 
|  | ts->stop_desc = (char *) xmalloc ((p2 - p1) / 2 + 1); | 
|  | end = hex2bin (p1, (gdb_byte *) ts->stop_desc, (p2 - p1) / 2); | 
|  | ts->stop_desc[end] = '\0'; | 
|  | } | 
|  | else | 
|  | ts->stop_desc = xstrdup (""); | 
|  |  | 
|  | p = unpack_varlen_hex (++p2, &val); | 
|  | ts->stopping_tracepoint = val; | 
|  | ts->stop_reason = tracepoint_error; | 
|  | } | 
|  | else if (strncmp (p, "tframes", p1 - p) == 0) | 
|  | { | 
|  | p = unpack_varlen_hex (++p1, &val); | 
|  | ts->traceframe_count = val; | 
|  | } | 
|  | else if (strncmp (p, "tcreated", p1 - p) == 0) | 
|  | { | 
|  | p = unpack_varlen_hex (++p1, &val); | 
|  | ts->traceframes_created = val; | 
|  | } | 
|  | else if (strncmp (p, "tfree", p1 - p) == 0) | 
|  | { | 
|  | p = unpack_varlen_hex (++p1, &val); | 
|  | ts->buffer_free = val; | 
|  | } | 
|  | else if (strncmp (p, "tsize", p1 - p) == 0) | 
|  | { | 
|  | p = unpack_varlen_hex (++p1, &val); | 
|  | ts->buffer_size = val; | 
|  | } | 
|  | else if (strncmp (p, "disconn", p1 - p) == 0) | 
|  | { | 
|  | p = unpack_varlen_hex (++p1, &val); | 
|  | ts->disconnected_tracing = val; | 
|  | } | 
|  | else if (strncmp (p, "circular", p1 - p) == 0) | 
|  | { | 
|  | p = unpack_varlen_hex (++p1, &val); | 
|  | ts->circular_buffer = val; | 
|  | } | 
|  | else if (strncmp (p, "starttime", p1 - p) == 0) | 
|  | { | 
|  | p = unpack_varlen_hex (++p1, &val); | 
|  | ts->start_time = val; | 
|  | } | 
|  | else if (strncmp (p, "stoptime", p1 - p) == 0) | 
|  | { | 
|  | p = unpack_varlen_hex (++p1, &val); | 
|  | ts->stop_time = val; | 
|  | } | 
|  | else if (strncmp (p, "username", p1 - p) == 0) | 
|  | { | 
|  | ++p1; | 
|  | ts->user_name = (char *) xmalloc (strlen (p) / 2); | 
|  | end = hex2bin (p1, (gdb_byte *) ts->user_name, (p3 - p1)  / 2); | 
|  | ts->user_name[end] = '\0'; | 
|  | p = p3; | 
|  | } | 
|  | else if (strncmp (p, "notes", p1 - p) == 0) | 
|  | { | 
|  | ++p1; | 
|  | ts->notes = (char *) xmalloc (strlen (p) / 2); | 
|  | end = hex2bin (p1, (gdb_byte *) ts->notes, (p3 - p1) / 2); | 
|  | ts->notes[end] = '\0'; | 
|  | p = p3; | 
|  | } | 
|  | else | 
|  | { | 
|  | /* Silently skip unknown optional info.  */ | 
|  | p_temp = strchr (p1 + 1, ';'); | 
|  | if (p_temp) | 
|  | p = p_temp; | 
|  | else | 
|  | /* Must be at the end.  */ | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | parse_tracepoint_status (const char *p, struct breakpoint *bp, | 
|  | struct uploaded_tp *utp) | 
|  | { | 
|  | ULONGEST uval; | 
|  | struct tracepoint *tp = (struct tracepoint *) bp; | 
|  |  | 
|  | p = unpack_varlen_hex (p, &uval); | 
|  | if (tp) | 
|  | tp->hit_count += uval; | 
|  | else | 
|  | utp->hit_count += uval; | 
|  | p = unpack_varlen_hex (p + 1, &uval); | 
|  | if (tp) | 
|  | tp->traceframe_usage += uval; | 
|  | else | 
|  | utp->traceframe_usage += uval; | 
|  | /* Ignore any extra, allowing for future extensions.  */ | 
|  | } | 
|  |  | 
|  | /* Given a line of text defining a part of a tracepoint, parse it into | 
|  | an "uploaded tracepoint".  */ | 
|  |  | 
|  | void | 
|  | parse_tracepoint_definition (const char *line, struct uploaded_tp **utpp) | 
|  | { | 
|  | const char *p; | 
|  | char piece; | 
|  | ULONGEST num, addr, step, pass, orig_size, xlen, start; | 
|  | int enabled, end; | 
|  | enum bptype type; | 
|  | const char *srctype; | 
|  | char *buf; | 
|  | struct uploaded_tp *utp = NULL; | 
|  |  | 
|  | p = line; | 
|  | /* Both tracepoint and action definitions start with the same number | 
|  | and address sequence.  */ | 
|  | piece = *p++; | 
|  | p = unpack_varlen_hex (p, &num); | 
|  | p++;  /* skip a colon */ | 
|  | p = unpack_varlen_hex (p, &addr); | 
|  | p++;  /* skip a colon */ | 
|  | if (piece == 'T') | 
|  | { | 
|  | gdb::unique_xmalloc_ptr<char[]> cond; | 
|  |  | 
|  | enabled = (*p++ == 'E'); | 
|  | p++;  /* skip a colon */ | 
|  | p = unpack_varlen_hex (p, &step); | 
|  | p++;  /* skip a colon */ | 
|  | p = unpack_varlen_hex (p, &pass); | 
|  | type = bp_tracepoint; | 
|  | /* Thumb through optional fields.  */ | 
|  | while (*p == ':') | 
|  | { | 
|  | p++;  /* skip a colon */ | 
|  | if (*p == 'F') | 
|  | { | 
|  | type = bp_fast_tracepoint; | 
|  | p++; | 
|  | p = unpack_varlen_hex (p, &orig_size); | 
|  | } | 
|  | else if (*p == 'S') | 
|  | { | 
|  | type = bp_static_tracepoint; | 
|  | p++; | 
|  | } | 
|  | else if (*p == 'X') | 
|  | { | 
|  | p++; | 
|  | p = unpack_varlen_hex (p, &xlen); | 
|  | p++;  /* skip a comma */ | 
|  | cond.reset ((char *) xmalloc (2 * xlen + 1)); | 
|  | strncpy (&cond[0], p, 2 * xlen); | 
|  | cond[2 * xlen] = '\0'; | 
|  | p += 2 * xlen; | 
|  | } | 
|  | else | 
|  | warning (_("Unrecognized char '%c' in tracepoint " | 
|  | "definition, skipping rest"), *p); | 
|  | } | 
|  | utp = get_uploaded_tp (num, addr, utpp); | 
|  | utp->type = type; | 
|  | utp->enabled = enabled; | 
|  | utp->step = step; | 
|  | utp->pass = pass; | 
|  | utp->cond = std::move (cond); | 
|  | } | 
|  | else if (piece == 'A') | 
|  | { | 
|  | utp = get_uploaded_tp (num, addr, utpp); | 
|  | utp->actions.emplace_back (xstrdup (p)); | 
|  | } | 
|  | else if (piece == 'S') | 
|  | { | 
|  | utp = get_uploaded_tp (num, addr, utpp); | 
|  | utp->step_actions.emplace_back (xstrdup (p)); | 
|  | } | 
|  | else if (piece == 'Z') | 
|  | { | 
|  | /* Parse a chunk of source form definition.  */ | 
|  | utp = get_uploaded_tp (num, addr, utpp); | 
|  | srctype = p; | 
|  | p = strchr (p, ':'); | 
|  | p++;  /* skip a colon */ | 
|  | p = unpack_varlen_hex (p, &start); | 
|  | p++;  /* skip a colon */ | 
|  | p = unpack_varlen_hex (p, &xlen); | 
|  | p++;  /* skip a colon */ | 
|  |  | 
|  | buf = (char *) alloca (strlen (line)); | 
|  |  | 
|  | end = hex2bin (p, (gdb_byte *) buf, strlen (p) / 2); | 
|  | buf[end] = '\0'; | 
|  |  | 
|  | if (startswith (srctype, "at:")) | 
|  | utp->at_string.reset (xstrdup (buf)); | 
|  | else if (startswith (srctype, "cond:")) | 
|  | utp->cond_string.reset (xstrdup (buf)); | 
|  | else if (startswith (srctype, "cmd:")) | 
|  | utp->cmd_strings.emplace_back (xstrdup (buf)); | 
|  | } | 
|  | else if (piece == 'V') | 
|  | { | 
|  | utp = get_uploaded_tp (num, addr, utpp); | 
|  |  | 
|  | parse_tracepoint_status (p, NULL, utp); | 
|  | } | 
|  | else | 
|  | { | 
|  | /* Don't error out, the target might be sending us optional | 
|  | info that we don't care about.  */ | 
|  | warning (_("Unrecognized tracepoint piece '%c', ignoring"), piece); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Convert a textual description of a trace state variable into an | 
|  | uploaded object.  */ | 
|  |  | 
|  | void | 
|  | parse_tsv_definition (const char *line, struct uploaded_tsv **utsvp) | 
|  | { | 
|  | const char *p; | 
|  | char *buf; | 
|  | ULONGEST num, initval, builtin; | 
|  | int end; | 
|  | struct uploaded_tsv *utsv = NULL; | 
|  |  | 
|  | buf = (char *) alloca (strlen (line)); | 
|  |  | 
|  | p = line; | 
|  | p = unpack_varlen_hex (p, &num); | 
|  | p++; /* skip a colon */ | 
|  | p = unpack_varlen_hex (p, &initval); | 
|  | p++; /* skip a colon */ | 
|  | p = unpack_varlen_hex (p, &builtin); | 
|  | p++; /* skip a colon */ | 
|  | end = hex2bin (p, (gdb_byte *) buf, strlen (p) / 2); | 
|  | buf[end] = '\0'; | 
|  |  | 
|  | utsv = get_uploaded_tsv (num, utsvp); | 
|  | utsv->initial_value = initval; | 
|  | utsv->builtin = builtin; | 
|  | utsv->name = xstrdup (buf); | 
|  | } | 
|  |  | 
|  | /* Given a line of text defining a static tracepoint marker, parse it | 
|  | into a "static tracepoint marker" object.  Throws an error is | 
|  | parsing fails.  If PP is non-null, it points to one past the end of | 
|  | the parsed marker definition.  */ | 
|  |  | 
|  | void | 
|  | parse_static_tracepoint_marker_definition (const char *line, const char **pp, | 
|  | static_tracepoint_marker *marker) | 
|  | { | 
|  | const char *p, *endp; | 
|  | ULONGEST addr; | 
|  |  | 
|  | p = line; | 
|  | p = unpack_varlen_hex (p, &addr); | 
|  | p++;  /* skip a colon */ | 
|  |  | 
|  | marker->gdbarch = target_gdbarch (); | 
|  | marker->address = (CORE_ADDR) addr; | 
|  |  | 
|  | endp = strchr (p, ':'); | 
|  | if (endp == NULL) | 
|  | error (_("bad marker definition: %s"), line); | 
|  |  | 
|  | marker->str_id = hex2str (p, (endp - p) / 2); | 
|  |  | 
|  | p = endp; | 
|  | p++; /* skip a colon */ | 
|  |  | 
|  | /* This definition may be followed by another one, separated by a comma.  */ | 
|  | int hex_len; | 
|  | endp = strchr (p, ','); | 
|  | if (endp != nullptr) | 
|  | hex_len = endp - p; | 
|  | else | 
|  | hex_len = strlen (p); | 
|  |  | 
|  | marker->extra = hex2str (p, hex_len / 2); | 
|  |  | 
|  | if (pp != nullptr) | 
|  | *pp = p + hex_len; | 
|  | } | 
|  |  | 
|  | /* Print MARKER to gdb_stdout.  */ | 
|  |  | 
|  | static void | 
|  | print_one_static_tracepoint_marker (int count, | 
|  | const static_tracepoint_marker &marker) | 
|  | { | 
|  | struct symbol *sym; | 
|  |  | 
|  | struct ui_out *uiout = current_uiout; | 
|  |  | 
|  | symtab_and_line sal; | 
|  | sal.pc = marker.address; | 
|  |  | 
|  | std::vector<breakpoint *> tracepoints | 
|  | = static_tracepoints_here (marker.address); | 
|  |  | 
|  | ui_out_emit_tuple tuple_emitter (uiout, "marker"); | 
|  |  | 
|  | /* A counter field to help readability.  This is not a stable | 
|  | identifier!  */ | 
|  | uiout->field_signed ("count", count); | 
|  |  | 
|  | uiout->field_string ("marker-id", marker.str_id); | 
|  |  | 
|  | uiout->field_fmt ("enabled", "%c", | 
|  | !tracepoints.empty () ? 'y' : 'n'); | 
|  | uiout->spaces (2); | 
|  |  | 
|  | int wrap_indent = 35; | 
|  | if (gdbarch_addr_bit (marker.gdbarch) <= 32) | 
|  | wrap_indent += 11; | 
|  | else | 
|  | wrap_indent += 19; | 
|  |  | 
|  | const char *extra_field_indent = "         "; | 
|  |  | 
|  | uiout->field_core_addr ("addr", marker.gdbarch, marker.address); | 
|  |  | 
|  | sal = find_pc_line (marker.address, 0); | 
|  | sym = find_pc_sect_function (marker.address, NULL); | 
|  | if (sym) | 
|  | { | 
|  | uiout->text ("in "); | 
|  | uiout->field_string ("func", sym->print_name (), | 
|  | function_name_style.style ()); | 
|  | uiout->wrap_hint (wrap_indent); | 
|  | uiout->text (" at "); | 
|  | } | 
|  | else | 
|  | uiout->field_skip ("func"); | 
|  |  | 
|  | if (sal.symtab != NULL) | 
|  | { | 
|  | uiout->field_string ("file", | 
|  | symtab_to_filename_for_display (sal.symtab), | 
|  | file_name_style.style ()); | 
|  | uiout->text (":"); | 
|  |  | 
|  | if (uiout->is_mi_like_p ()) | 
|  | { | 
|  | const char *fullname = symtab_to_fullname (sal.symtab); | 
|  |  | 
|  | uiout->field_string ("fullname", fullname); | 
|  | } | 
|  | else | 
|  | uiout->field_skip ("fullname"); | 
|  |  | 
|  | uiout->field_signed ("line", sal.line); | 
|  | } | 
|  | else | 
|  | { | 
|  | uiout->field_skip ("fullname"); | 
|  | uiout->field_skip ("line"); | 
|  | } | 
|  |  | 
|  | uiout->text ("\n"); | 
|  | uiout->text (extra_field_indent); | 
|  | uiout->text (_("Data: \"")); | 
|  | uiout->field_string ("extra-data", marker.extra); | 
|  | uiout->text ("\"\n"); | 
|  |  | 
|  | if (!tracepoints.empty ()) | 
|  | { | 
|  | int ix; | 
|  |  | 
|  | { | 
|  | ui_out_emit_tuple inner_tuple_emitter (uiout, "tracepoints-at"); | 
|  |  | 
|  | uiout->text (extra_field_indent); | 
|  | uiout->text (_("Probed by static tracepoints: ")); | 
|  | for (ix = 0; ix < tracepoints.size (); ix++) | 
|  | { | 
|  | if (ix > 0) | 
|  | uiout->text (", "); | 
|  | uiout->text ("#"); | 
|  | uiout->field_signed ("tracepoint-id", tracepoints[ix]->number); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (uiout->is_mi_like_p ()) | 
|  | uiout->field_signed ("number-of-tracepoints", tracepoints.size ()); | 
|  | else | 
|  | uiout->text ("\n"); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | info_static_tracepoint_markers_command (const char *arg, int from_tty) | 
|  | { | 
|  | struct ui_out *uiout = current_uiout; | 
|  | std::vector<static_tracepoint_marker> markers | 
|  | = target_static_tracepoint_markers_by_strid (NULL); | 
|  |  | 
|  | /* We don't have to check target_can_use_agent and agent's capability on | 
|  | static tracepoint here, in order to be compatible with older GDBserver. | 
|  | We don't check USE_AGENT is true or not, because static tracepoints | 
|  | don't work without in-process agent, so we don't bother users to type | 
|  | `set agent on' when to use static tracepoint.  */ | 
|  |  | 
|  | ui_out_emit_table table_emitter (uiout, 5, -1, | 
|  | "StaticTracepointMarkersTable"); | 
|  |  | 
|  | uiout->table_header (7, ui_left, "counter", "Cnt"); | 
|  |  | 
|  | uiout->table_header (40, ui_left, "marker-id", "ID"); | 
|  |  | 
|  | uiout->table_header (3, ui_left, "enabled", "Enb"); | 
|  | if (gdbarch_addr_bit (target_gdbarch ()) <= 32) | 
|  | uiout->table_header (10, ui_left, "addr", "Address"); | 
|  | else | 
|  | uiout->table_header (18, ui_left, "addr", "Address"); | 
|  | uiout->table_header (40, ui_noalign, "what", "What"); | 
|  |  | 
|  | uiout->table_body (); | 
|  |  | 
|  | for (int i = 0; i < markers.size (); i++) | 
|  | print_one_static_tracepoint_marker (i + 1, markers[i]); | 
|  | } | 
|  |  | 
|  | /* The $_sdata convenience variable is a bit special.  We don't know | 
|  | for sure type of the value until we actually have a chance to fetch | 
|  | the data --- the size of the object depends on what has been | 
|  | collected.  We solve this by making $_sdata be an internalvar that | 
|  | creates a new value on access.  */ | 
|  |  | 
|  | /* Return a new value with the correct type for the sdata object of | 
|  | the current trace frame.  Return a void value if there's no object | 
|  | available.  */ | 
|  |  | 
|  | static struct value * | 
|  | sdata_make_value (struct gdbarch *gdbarch, struct internalvar *var, | 
|  | void *ignore) | 
|  | { | 
|  | /* We need to read the whole object before we know its size.  */ | 
|  | gdb::optional<gdb::byte_vector> buf | 
|  | = target_read_alloc (current_inferior ()->top_target (), | 
|  | TARGET_OBJECT_STATIC_TRACE_DATA, | 
|  | NULL); | 
|  | if (buf) | 
|  | { | 
|  | struct value *v; | 
|  | struct type *type; | 
|  |  | 
|  | type = init_vector_type (builtin_type (gdbarch)->builtin_true_char, | 
|  | buf->size ()); | 
|  | v = allocate_value (type); | 
|  | memcpy (value_contents_raw (v).data (), buf->data (), buf->size ()); | 
|  | return v; | 
|  | } | 
|  | else | 
|  | return allocate_value (builtin_type (gdbarch)->builtin_void); | 
|  | } | 
|  |  | 
|  | #if !defined(HAVE_LIBEXPAT) | 
|  |  | 
|  | struct std::unique_ptr<traceframe_info> | 
|  | parse_traceframe_info (const char *tframe_info) | 
|  | { | 
|  | static int have_warned; | 
|  |  | 
|  | if (!have_warned) | 
|  | { | 
|  | have_warned = 1; | 
|  | warning (_("Can not parse XML trace frame info; XML support " | 
|  | "was disabled at compile time")); | 
|  | } | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | #else /* HAVE_LIBEXPAT */ | 
|  |  | 
|  | #include "xml-support.h" | 
|  |  | 
|  | /* Handle the start of a <memory> element.  */ | 
|  |  | 
|  | static void | 
|  | traceframe_info_start_memory (struct gdb_xml_parser *parser, | 
|  | const struct gdb_xml_element *element, | 
|  | void *user_data, | 
|  | std::vector<gdb_xml_value> &attributes) | 
|  | { | 
|  | struct traceframe_info *info = (struct traceframe_info *) user_data; | 
|  | ULONGEST *start_p, *length_p; | 
|  |  | 
|  | start_p | 
|  | = (ULONGEST *) xml_find_attribute (attributes, "start")->value.get (); | 
|  | length_p | 
|  | = (ULONGEST *) xml_find_attribute (attributes, "length")->value.get (); | 
|  |  | 
|  | info->memory.emplace_back (*start_p, *length_p); | 
|  | } | 
|  |  | 
|  | /* Handle the start of a <tvar> element.  */ | 
|  |  | 
|  | static void | 
|  | traceframe_info_start_tvar (struct gdb_xml_parser *parser, | 
|  | const struct gdb_xml_element *element, | 
|  | void *user_data, | 
|  | std::vector<gdb_xml_value> &attributes) | 
|  | { | 
|  | struct traceframe_info *info = (struct traceframe_info *) user_data; | 
|  | const char *id_attrib | 
|  | = (const char *) xml_find_attribute (attributes, "id")->value.get (); | 
|  | int id = gdb_xml_parse_ulongest (parser, id_attrib); | 
|  |  | 
|  | info->tvars.push_back (id); | 
|  | } | 
|  |  | 
|  | /* The allowed elements and attributes for an XML memory map.  */ | 
|  |  | 
|  | static const struct gdb_xml_attribute memory_attributes[] = { | 
|  | { "start", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL }, | 
|  | { "length", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL }, | 
|  | { NULL, GDB_XML_AF_NONE, NULL, NULL } | 
|  | }; | 
|  |  | 
|  | static const struct gdb_xml_attribute tvar_attributes[] = { | 
|  | { "id", GDB_XML_AF_NONE, NULL, NULL }, | 
|  | { NULL, GDB_XML_AF_NONE, NULL, NULL } | 
|  | }; | 
|  |  | 
|  | static const struct gdb_xml_element traceframe_info_children[] = { | 
|  | { "memory", memory_attributes, NULL, | 
|  | GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL, | 
|  | traceframe_info_start_memory, NULL }, | 
|  | { "tvar", tvar_attributes, NULL, | 
|  | GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL, | 
|  | traceframe_info_start_tvar, NULL }, | 
|  | { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL } | 
|  | }; | 
|  |  | 
|  | static const struct gdb_xml_element traceframe_info_elements[] = { | 
|  | { "traceframe-info", NULL, traceframe_info_children, GDB_XML_EF_NONE, | 
|  | NULL, NULL }, | 
|  | { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL } | 
|  | }; | 
|  |  | 
|  | /* Parse a traceframe-info XML document.  */ | 
|  |  | 
|  | traceframe_info_up | 
|  | parse_traceframe_info (const char *tframe_info) | 
|  | { | 
|  | traceframe_info_up result (new traceframe_info); | 
|  |  | 
|  | if (gdb_xml_parse_quick (_("trace frame info"), | 
|  | "traceframe-info.dtd", traceframe_info_elements, | 
|  | tframe_info, result.get ()) == 0) | 
|  | return result; | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | #endif /* HAVE_LIBEXPAT */ | 
|  |  | 
|  | /* Returns the traceframe_info object for the current traceframe. | 
|  | This is where we avoid re-fetching the object from the target if we | 
|  | already have it cached.  */ | 
|  |  | 
|  | struct traceframe_info * | 
|  | get_traceframe_info (void) | 
|  | { | 
|  | if (current_traceframe_info == NULL) | 
|  | current_traceframe_info = target_traceframe_info (); | 
|  |  | 
|  | return current_traceframe_info.get (); | 
|  | } | 
|  |  | 
|  | /* If the target supports the query, return in RESULT the set of | 
|  | collected memory in the current traceframe, found within the LEN | 
|  | bytes range starting at MEMADDR.  Returns true if the target | 
|  | supports the query, otherwise returns false, and RESULT is left | 
|  | undefined.  */ | 
|  |  | 
|  | int | 
|  | traceframe_available_memory (std::vector<mem_range> *result, | 
|  | CORE_ADDR memaddr, ULONGEST len) | 
|  | { | 
|  | struct traceframe_info *info = get_traceframe_info (); | 
|  |  | 
|  | if (info != NULL) | 
|  | { | 
|  | result->clear (); | 
|  |  | 
|  | for (mem_range &r : info->memory) | 
|  | if (mem_ranges_overlap (r.start, r.length, memaddr, len)) | 
|  | { | 
|  | ULONGEST lo1, hi1, lo2, hi2; | 
|  |  | 
|  | lo1 = memaddr; | 
|  | hi1 = memaddr + len; | 
|  |  | 
|  | lo2 = r.start; | 
|  | hi2 = r.start + r.length; | 
|  |  | 
|  | CORE_ADDR start = std::max (lo1, lo2); | 
|  | int length = std::min (hi1, hi2) - start; | 
|  |  | 
|  | result->emplace_back (start, length); | 
|  | } | 
|  |  | 
|  | normalize_mem_ranges (result); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Implementation of `sdata' variable.  */ | 
|  |  | 
|  | static const struct internalvar_funcs sdata_funcs = | 
|  | { | 
|  | sdata_make_value, | 
|  | NULL | 
|  | }; | 
|  |  | 
|  | /* See tracepoint.h.  */ | 
|  | cmd_list_element *while_stepping_cmd_element = nullptr; | 
|  |  | 
|  | /* module initialization */ | 
|  | void _initialize_tracepoint (); | 
|  | void | 
|  | _initialize_tracepoint () | 
|  | { | 
|  | struct cmd_list_element *c; | 
|  |  | 
|  | /* Explicitly create without lookup, since that tries to create a | 
|  | value with a void typed value, and when we get here, gdbarch | 
|  | isn't initialized yet.  At this point, we're quite sure there | 
|  | isn't another convenience variable of the same name.  */ | 
|  | create_internalvar_type_lazy ("_sdata", &sdata_funcs, NULL); | 
|  |  | 
|  | traceframe_number = -1; | 
|  | tracepoint_number = -1; | 
|  |  | 
|  | add_info ("scope", info_scope_command, | 
|  | _("List the variables local to a scope.")); | 
|  |  | 
|  | add_cmd ("tracepoints", class_trace, | 
|  | _("Tracing of program execution without stopping the program."), | 
|  | &cmdlist); | 
|  |  | 
|  | add_com ("tdump", class_trace, tdump_command, | 
|  | _("Print everything collected at the current tracepoint.")); | 
|  |  | 
|  | c = add_com ("tvariable", class_trace, trace_variable_command,_("\ | 
|  | Define a trace state variable.\n\ | 
|  | Argument is a $-prefixed name, optionally followed\n\ | 
|  | by '=' and an expression that sets the initial value\n\ | 
|  | at the start of tracing.")); | 
|  | set_cmd_completer (c, expression_completer); | 
|  |  | 
|  | add_cmd ("tvariable", class_trace, delete_trace_variable_command, _("\ | 
|  | Delete one or more trace state variables.\n\ | 
|  | Arguments are the names of the variables to delete.\n\ | 
|  | If no arguments are supplied, delete all variables."), &deletelist); | 
|  | /* FIXME add a trace variable completer.  */ | 
|  |  | 
|  | add_info ("tvariables", info_tvariables_command, _("\ | 
|  | Status of trace state variables and their values.")); | 
|  |  | 
|  | add_info ("static-tracepoint-markers", | 
|  | info_static_tracepoint_markers_command, _("\ | 
|  | List target static tracepoints markers.")); | 
|  |  | 
|  | add_prefix_cmd ("tfind", class_trace, tfind_command, _("\ | 
|  | Select a trace frame.\n\ | 
|  | No argument means forward by one frame; '-' means backward by one frame."), | 
|  | &tfindlist, 1, &cmdlist); | 
|  |  | 
|  | add_cmd ("outside", class_trace, tfind_outside_command, _("\ | 
|  | Select a trace frame whose PC is outside the given range (exclusive).\n\ | 
|  | Usage: tfind outside ADDR1, ADDR2"), | 
|  | &tfindlist); | 
|  |  | 
|  | add_cmd ("range", class_trace, tfind_range_command, _("\ | 
|  | Select a trace frame whose PC is in the given range (inclusive).\n\ | 
|  | Usage: tfind range ADDR1, ADDR2"), | 
|  | &tfindlist); | 
|  |  | 
|  | add_cmd ("line", class_trace, tfind_line_command, _("\ | 
|  | Select a trace frame by source line.\n\ | 
|  | Argument can be a line number (with optional source file),\n\ | 
|  | a function name, or '*' followed by an address.\n\ | 
|  | Default argument is 'the next source line that was traced'."), | 
|  | &tfindlist); | 
|  |  | 
|  | add_cmd ("tracepoint", class_trace, tfind_tracepoint_command, _("\ | 
|  | Select a trace frame by tracepoint number.\n\ | 
|  | Default is the tracepoint for the current trace frame."), | 
|  | &tfindlist); | 
|  |  | 
|  | add_cmd ("pc", class_trace, tfind_pc_command, _("\ | 
|  | Select a trace frame by PC.\n\ | 
|  | Default is the current PC, or the PC of the current trace frame."), | 
|  | &tfindlist); | 
|  |  | 
|  | cmd_list_element *tfind_end_cmd | 
|  | = add_cmd ("end", class_trace, tfind_end_command, _("\ | 
|  | De-select any trace frame and resume 'live' debugging."), &tfindlist); | 
|  |  | 
|  | add_alias_cmd ("none", tfind_end_cmd, class_trace, 0, &tfindlist); | 
|  |  | 
|  | add_cmd ("start", class_trace, tfind_start_command, | 
|  | _("Select the first trace frame in the trace buffer."), | 
|  | &tfindlist); | 
|  |  | 
|  | add_com ("tstatus", class_trace, tstatus_command, | 
|  | _("Display the status of the current trace data collection.")); | 
|  |  | 
|  | add_com ("tstop", class_trace, tstop_command, _("\ | 
|  | Stop trace data collection.\n\ | 
|  | Usage: tstop [NOTES]...\n\ | 
|  | Any arguments supplied are recorded with the trace as a stop reason and\n\ | 
|  | reported by tstatus (if the target supports trace notes).")); | 
|  |  | 
|  | add_com ("tstart", class_trace, tstart_command, _("\ | 
|  | Start trace data collection.\n\ | 
|  | Usage: tstart [NOTES]...\n\ | 
|  | Any arguments supplied are recorded with the trace as a note and\n\ | 
|  | reported by tstatus (if the target supports trace notes).")); | 
|  |  | 
|  | add_com ("end", class_trace, end_actions_pseudocommand, _("\ | 
|  | Ends a list of commands or actions.\n\ | 
|  | Several GDB commands allow you to enter a list of commands or actions.\n\ | 
|  | Entering \"end\" on a line by itself is the normal way to terminate\n\ | 
|  | such a list.\n\n\ | 
|  | Note: the \"end\" command cannot be used at the gdb prompt.")); | 
|  |  | 
|  | while_stepping_cmd_element = add_com ("while-stepping", class_trace, | 
|  | while_stepping_pseudocommand, _("\ | 
|  | Specify single-stepping behavior at a tracepoint.\n\ | 
|  | Argument is number of instructions to trace in single-step mode\n\ | 
|  | following the tracepoint.  This command is normally followed by\n\ | 
|  | one or more \"collect\" commands, to specify what to collect\n\ | 
|  | while single-stepping.\n\n\ | 
|  | Note: this command can only be used in a tracepoint \"actions\" list.")); | 
|  |  | 
|  | add_com_alias ("ws", while_stepping_cmd_element, class_trace, 0); | 
|  | add_com_alias ("stepping", while_stepping_cmd_element, class_trace, 0); | 
|  |  | 
|  | add_com ("collect", class_trace, collect_pseudocommand, _("\ | 
|  | Specify one or more data items to be collected at a tracepoint.\n\ | 
|  | Accepts a comma-separated list of (one or more) expressions.  GDB will\n\ | 
|  | collect all data (variables, registers) referenced by that expression.\n\ | 
|  | Also accepts the following special arguments:\n\ | 
|  | $regs   -- all registers.\n\ | 
|  | $args   -- all function arguments.\n\ | 
|  | $locals -- all variables local to the block/function scope.\n\ | 
|  | $_sdata -- static tracepoint data (ignored for non-static tracepoints).\n\ | 
|  | Note: this command can only be used in a tracepoint \"actions\" list.")); | 
|  |  | 
|  | add_com ("teval", class_trace, teval_pseudocommand, _("\ | 
|  | Specify one or more expressions to be evaluated at a tracepoint.\n\ | 
|  | Accepts a comma-separated list of (one or more) expressions.\n\ | 
|  | The result of each evaluation will be discarded.\n\ | 
|  | Note: this command can only be used in a tracepoint \"actions\" list.")); | 
|  |  | 
|  | add_com ("actions", class_trace, actions_command, _("\ | 
|  | Specify the actions to be taken at a tracepoint.\n\ | 
|  | Tracepoint actions may include collecting of specified data,\n\ | 
|  | single-stepping, or enabling/disabling other tracepoints,\n\ | 
|  | depending on target's capabilities.")); | 
|  |  | 
|  | add_setshow_string_cmd ("default-collect", class_trace, | 
|  | &default_collect, _("\ | 
|  | Set the list of expressions to collect by default."), _("\ | 
|  | Show the list of expressions to collect by default."), NULL, | 
|  | NULL, NULL, | 
|  | &setlist, &showlist); | 
|  |  | 
|  | add_setshow_boolean_cmd ("disconnected-tracing", no_class, | 
|  | &disconnected_tracing, _("\ | 
|  | Set whether tracing continues after GDB disconnects."), _("\ | 
|  | Show whether tracing continues after GDB disconnects."), _("\ | 
|  | Use this to continue a tracing run even if GDB disconnects\n\ | 
|  | or detaches from the target.  You can reconnect later and look at\n\ | 
|  | trace data collected in the meantime."), | 
|  | set_disconnected_tracing, | 
|  | NULL, | 
|  | &setlist, | 
|  | &showlist); | 
|  |  | 
|  | add_setshow_boolean_cmd ("circular-trace-buffer", no_class, | 
|  | &circular_trace_buffer, _("\ | 
|  | Set target's use of circular trace buffer."), _("\ | 
|  | Show target's use of circular trace buffer."), _("\ | 
|  | Use this to make the trace buffer into a circular buffer,\n\ | 
|  | which will discard traceframes (oldest first) instead of filling\n\ | 
|  | up and stopping the trace run."), | 
|  | set_circular_trace_buffer, | 
|  | NULL, | 
|  | &setlist, | 
|  | &showlist); | 
|  |  | 
|  | add_setshow_zuinteger_unlimited_cmd ("trace-buffer-size", no_class, | 
|  | &trace_buffer_size, _("\ | 
|  | Set requested size of trace buffer."), _("\ | 
|  | Show requested size of trace buffer."), _("\ | 
|  | Use this to choose a size for the trace buffer.  Some targets\n\ | 
|  | may have fixed or limited buffer sizes.  Specifying \"unlimited\" or -1\n\ | 
|  | disables any attempt to set the buffer size and lets the target choose."), | 
|  | set_trace_buffer_size, NULL, | 
|  | &setlist, &showlist); | 
|  |  | 
|  | add_setshow_string_cmd ("trace-user", class_trace, | 
|  | &trace_user, _("\ | 
|  | Set the user name to use for current and future trace runs."), _("\ | 
|  | Show the user name to use for current and future trace runs."), NULL, | 
|  | set_trace_user, NULL, | 
|  | &setlist, &showlist); | 
|  |  | 
|  | add_setshow_string_cmd ("trace-notes", class_trace, | 
|  | &trace_notes, _("\ | 
|  | Set notes string to use for current and future trace runs."), _("\ | 
|  | Show the notes string to use for current and future trace runs."), NULL, | 
|  | set_trace_notes, NULL, | 
|  | &setlist, &showlist); | 
|  |  | 
|  | add_setshow_string_cmd ("trace-stop-notes", class_trace, | 
|  | &trace_stop_notes, _("\ | 
|  | Set notes string to use for future tstop commands."), _("\ | 
|  | Show the notes string to use for future tstop commands."), NULL, | 
|  | set_trace_stop_notes, NULL, | 
|  | &setlist, &showlist); | 
|  | } |