+ cur_tpoint = tracepoints;
+ cur_action = cur_step_action = 0;
+ cur_source_string = NULL;
+
+ if (cur_tpoint)
+ response_tracepoint (packet, cur_tpoint);
+ else
+ strcpy (packet, "l");
+}
+
+/* Return additional pieces of tracepoint definition. Each action and
+ stepping action must go into its own packet, because of packet size
+ limits, and so we use state variables to deliver one piece at a
+ time. */
+
+static void
+cmd_qtsp (char *packet)
+{
+ trace_debug ("Returning subsequent tracepoint definition piece");
+
+ if (!cur_tpoint)
+ {
+ /* This case would normally never occur, but be prepared for
+ GDB misbehavior. */
+ strcpy (packet, "l");
+ }
+ else if (cur_action < cur_tpoint->numactions)
+ {
+ response_action (packet, cur_tpoint,
+ cur_tpoint->actions_str[cur_action], 0);
+ ++cur_action;
+ }
+ else if (cur_step_action < cur_tpoint->num_step_actions)
+ {
+ response_action (packet, cur_tpoint,
+ cur_tpoint->step_actions_str[cur_step_action], 1);
+ ++cur_step_action;
+ }
+ else if ((cur_source_string
+ ? cur_source_string->next
+ : cur_tpoint->source_strings))
+ {
+ if (cur_source_string)
+ cur_source_string = cur_source_string->next;
+ else
+ cur_source_string = cur_tpoint->source_strings;
+ response_source (packet, cur_tpoint, cur_source_string);
+ }
+ else
+ {
+ cur_tpoint = cur_tpoint->next;
+ cur_action = cur_step_action = 0;
+ cur_source_string = NULL;
+ if (cur_tpoint)
+ response_tracepoint (packet, cur_tpoint);
+ else
+ strcpy (packet, "l");
+ }
+}
+
+/* Compose a response that is an imitation of the syntax by which the
+ trace state variable was originally downloaded. */
+
+static void
+response_tsv (char *packet, struct trace_state_variable *tsv)
+{
+ char *buf = (char *) "";
+ int namelen;
+
+ if (tsv->name)
+ {
+ namelen = strlen (tsv->name);
+ buf = alloca (namelen * 2 + 1);
+ bin2hex ((gdb_byte *) tsv->name, buf, namelen);
+ }
+
+ sprintf (packet, "%x:%s:%x:%s", tsv->number, phex_nz (tsv->initial_value, 0),
+ tsv->getter ? 1 : 0, buf);
+}
+
+/* Return the first trace state variable definition, and initialize
+ the state machine that will iterate through all the tsv bits. */
+
+static void
+cmd_qtfv (char *packet)
+{
+ trace_debug ("Returning first trace state variable definition");
+
+ cur_tsv = trace_state_variables;
+
+ if (cur_tsv)
+ response_tsv (packet, cur_tsv);
+ else
+ strcpy (packet, "l");
+}
+
+/* Return additional trace state variable definitions. */
+
+static void
+cmd_qtsv (char *packet)
+{
+ trace_debug ("Returning additional trace state variable definition");
+
+ if (cur_tsv)
+ {
+ cur_tsv = cur_tsv->next;
+ if (cur_tsv)
+ response_tsv (packet, cur_tsv);
+ else
+ strcpy (packet, "l");
+ }
+ else
+ strcpy (packet, "l");
+}
+
+/* Return the first static tracepoint marker, and initialize the state
+ machine that will iterate through all the static tracepoints
+ markers. */
+
+static void
+cmd_qtfstm (char *packet)
+{
+ if (!maybe_write_ipa_ust_not_loaded (packet))
+ run_inferior_command (packet, strlen (packet) + 1);
+}
+
+/* Return additional static tracepoints markers. */
+
+static void
+cmd_qtsstm (char *packet)
+{
+ if (!maybe_write_ipa_ust_not_loaded (packet))
+ run_inferior_command (packet, strlen (packet) + 1);
+}
+
+/* Return the definition of the static tracepoint at a given address.
+ Result packet is the same as qTsST's. */
+
+static void
+cmd_qtstmat (char *packet)
+{
+ if (!maybe_write_ipa_ust_not_loaded (packet))
+ run_inferior_command (packet, strlen (packet) + 1);
+}
+
+/* Helper for gdb_agent_about_to_close.
+ Return non-zero if thread ENTRY is in the same process in DATA. */
+
+static int
+same_process_p (struct inferior_list_entry *entry, void *data)
+{
+ int *pid = data;
+
+ return ptid_get_pid (entry->id) == *pid;
+}
+
+/* Sent the agent a command to close it. */
+
+void
+gdb_agent_about_to_close (int pid)
+{
+ char buf[IPA_CMD_BUF_SIZE];
+
+ if (!maybe_write_ipa_not_loaded (buf))
+ {
+ struct thread_info *saved_thread;
+
+ saved_thread = current_thread;
+
+ /* Find any thread which belongs to process PID. */
+ current_thread = (struct thread_info *)
+ find_inferior (&all_threads, same_process_p, &pid);
+
+ strcpy (buf, "close");
+
+ run_inferior_command (buf, strlen (buf) + 1);
+
+ current_thread = saved_thread;
+ }
+}
+
+/* Return the minimum instruction size needed for fast tracepoints as a
+ hexadecimal number. */
+
+static void
+cmd_qtminftpilen (char *packet)
+{
+ if (current_thread == NULL)
+ {
+ /* Indicate that the minimum length is currently unknown. */
+ strcpy (packet, "0");
+ return;
+ }
+
+ sprintf (packet, "%x", target_get_min_fast_tracepoint_insn_len ());
+}
+
+/* Respond to qTBuffer packet with a block of raw data from the trace
+ buffer. GDB may ask for a lot, but we are allowed to reply with
+ only as much as will fit within packet limits or whatever. */
+
+static void
+cmd_qtbuffer (char *own_buf)
+{
+ ULONGEST offset, num, tot;
+ unsigned char *tbp;
+ char *packet = own_buf;
+
+ packet += strlen ("qTBuffer:");
+
+ packet = unpack_varlen_hex (packet, &offset);
+ ++packet; /* skip a comma */
+ unpack_varlen_hex (packet, &num);
+
+ trace_debug ("Want to get trace buffer, %d bytes at offset 0x%s",
+ (int) num, phex_nz (offset, 0));
+
+ tot = (trace_buffer_hi - trace_buffer_lo) - free_space ();
+
+ /* If we're right at the end, reply specially that we're done. */
+ if (offset == tot)
+ {
+ strcpy (own_buf, "l");
+ return;
+ }
+
+ /* Object to any other out-of-bounds request. */
+ if (offset > tot)
+ {
+ write_enn (own_buf);
+ return;
+ }
+
+ /* Compute the pointer corresponding to the given offset, accounting
+ for wraparound. */
+ tbp = trace_buffer_start + offset;
+ if (tbp >= trace_buffer_wrap)
+ tbp -= (trace_buffer_wrap - trace_buffer_lo);
+
+ /* Trim to the remaining bytes if we're close to the end. */
+ if (num > tot - offset)
+ num = tot - offset;
+
+ /* Trim to available packet size. */
+ if (num >= (PBUFSIZ - 16) / 2 )
+ num = (PBUFSIZ - 16) / 2;
+
+ bin2hex (tbp, own_buf, num);
+}
+
+static void
+cmd_bigqtbuffer_circular (char *own_buf)
+{
+ ULONGEST val;
+ char *packet = own_buf;
+
+ packet += strlen ("QTBuffer:circular:");
+
+ unpack_varlen_hex (packet, &val);
+ circular_trace_buffer = val;
+ trace_debug ("Trace buffer is now %s",
+ circular_trace_buffer ? "circular" : "linear");
+ write_ok (own_buf);
+}
+
+static void
+cmd_bigqtbuffer_size (char *own_buf)
+{
+ ULONGEST val;
+ LONGEST sval;
+ char *packet = own_buf;
+
+ /* Can't change the size during a tracing run. */
+ if (tracing)
+ {
+ write_enn (own_buf);
+ return;
+ }
+
+ packet += strlen ("QTBuffer:size:");
+
+ /* -1 is sent as literal "-1". */
+ if (strcmp (packet, "-1") == 0)
+ sval = DEFAULT_TRACE_BUFFER_SIZE;
+ else
+ {
+ unpack_varlen_hex (packet, &val);
+ sval = (LONGEST) val;
+ }
+
+ init_trace_buffer (sval);
+ trace_debug ("Trace buffer is now %s bytes",
+ plongest (trace_buffer_size));
+ write_ok (own_buf);
+}
+
+static void
+cmd_qtnotes (char *own_buf)
+{
+ size_t nbytes;
+ char *saved, *user, *notes, *stopnote;
+ char *packet = own_buf;
+
+ packet += strlen ("QTNotes:");
+
+ while (*packet)
+ {
+ if (strncmp ("user:", packet, strlen ("user:")) == 0)
+ {
+ packet += strlen ("user:");
+ saved = packet;
+ packet = strchr (packet, ';');
+ nbytes = (packet - saved) / 2;
+ user = xmalloc (nbytes + 1);
+ nbytes = hex2bin (saved, (gdb_byte *) user, nbytes);
+ user[nbytes] = '\0';
+ ++packet; /* skip the semicolon */
+ trace_debug ("User is '%s'", user);
+ xfree (tracing_user_name);
+ tracing_user_name = user;
+ }
+ else if (strncmp ("notes:", packet, strlen ("notes:")) == 0)
+ {
+ packet += strlen ("notes:");
+ saved = packet;
+ packet = strchr (packet, ';');
+ nbytes = (packet - saved) / 2;
+ notes = xmalloc (nbytes + 1);
+ nbytes = hex2bin (saved, (gdb_byte *) notes, nbytes);
+ notes[nbytes] = '\0';
+ ++packet; /* skip the semicolon */
+ trace_debug ("Notes is '%s'", notes);
+ xfree (tracing_notes);
+ tracing_notes = notes;
+ }
+ else if (strncmp ("tstop:", packet, strlen ("tstop:")) == 0)
+ {
+ packet += strlen ("tstop:");
+ saved = packet;
+ packet = strchr (packet, ';');
+ nbytes = (packet - saved) / 2;
+ stopnote = xmalloc (nbytes + 1);
+ nbytes = hex2bin (saved, (gdb_byte *) stopnote, nbytes);
+ stopnote[nbytes] = '\0';
+ ++packet; /* skip the semicolon */
+ trace_debug ("tstop note is '%s'", stopnote);
+ xfree (tracing_stop_note);
+ tracing_stop_note = stopnote;
+ }
+ else
+ break;
+ }
+
+ write_ok (own_buf);
+}
+
+int
+handle_tracepoint_general_set (char *packet)
+{
+ if (strcmp ("QTinit", packet) == 0)
+ {
+ cmd_qtinit (packet);
+ return 1;
+ }
+ else if (strncmp ("QTDP:", packet, strlen ("QTDP:")) == 0)
+ {
+ cmd_qtdp (packet);
+ return 1;
+ }
+ else if (strncmp ("QTDPsrc:", packet, strlen ("QTDPsrc:")) == 0)
+ {
+ cmd_qtdpsrc (packet);
+ return 1;
+ }
+ else if (strncmp ("QTEnable:", packet, strlen ("QTEnable:")) == 0)
+ {
+ cmd_qtenable_disable (packet, 1);
+ return 1;
+ }
+ else if (strncmp ("QTDisable:", packet, strlen ("QTDisable:")) == 0)
+ {
+ cmd_qtenable_disable (packet, 0);
+ return 1;
+ }
+ else if (strncmp ("QTDV:", packet, strlen ("QTDV:")) == 0)
+ {
+ cmd_qtdv (packet);
+ return 1;
+ }
+ else if (strncmp ("QTro:", packet, strlen ("QTro:")) == 0)
+ {
+ cmd_qtro (packet);
+ return 1;
+ }
+ else if (strcmp ("QTStart", packet) == 0)
+ {
+ cmd_qtstart (packet);
+ return 1;
+ }
+ else if (strcmp ("QTStop", packet) == 0)
+ {
+ cmd_qtstop (packet);
+ return 1;
+ }
+ else if (strncmp ("QTDisconnected:", packet,
+ strlen ("QTDisconnected:")) == 0)
+ {
+ cmd_qtdisconnected (packet);
+ return 1;
+ }
+ else if (strncmp ("QTFrame:", packet, strlen ("QTFrame:")) == 0)
+ {
+ cmd_qtframe (packet);
+ return 1;
+ }
+ else if (strncmp ("QTBuffer:circular:", packet, strlen ("QTBuffer:circular:")) == 0)
+ {
+ cmd_bigqtbuffer_circular (packet);
+ return 1;
+ }
+ else if (strncmp ("QTBuffer:size:", packet, strlen ("QTBuffer:size:")) == 0)
+ {
+ cmd_bigqtbuffer_size (packet);
+ return 1;
+ }
+ else if (strncmp ("QTNotes:", packet, strlen ("QTNotes:")) == 0)
+ {
+ cmd_qtnotes (packet);
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+handle_tracepoint_query (char *packet)
+{
+ if (strcmp ("qTStatus", packet) == 0)
+ {
+ cmd_qtstatus (packet);
+ return 1;
+ }
+ else if (strncmp ("qTP:", packet, strlen ("qTP:")) == 0)
+ {
+ cmd_qtp (packet);
+ return 1;
+ }
+ else if (strcmp ("qTfP", packet) == 0)
+ {
+ cmd_qtfp (packet);
+ return 1;
+ }
+ else if (strcmp ("qTsP", packet) == 0)
+ {
+ cmd_qtsp (packet);
+ return 1;
+ }
+ else if (strcmp ("qTfV", packet) == 0)
+ {
+ cmd_qtfv (packet);
+ return 1;
+ }
+ else if (strcmp ("qTsV", packet) == 0)
+ {
+ cmd_qtsv (packet);
+ return 1;
+ }
+ else if (strncmp ("qTV:", packet, strlen ("qTV:")) == 0)
+ {
+ cmd_qtv (packet);
+ return 1;
+ }
+ else if (strncmp ("qTBuffer:", packet, strlen ("qTBuffer:")) == 0)
+ {
+ cmd_qtbuffer (packet);
+ return 1;
+ }
+ else if (strcmp ("qTfSTM", packet) == 0)
+ {
+ cmd_qtfstm (packet);
+ return 1;
+ }
+ else if (strcmp ("qTsSTM", packet) == 0)
+ {
+ cmd_qtsstm (packet);
+ return 1;
+ }
+ else if (strncmp ("qTSTMat:", packet, strlen ("qTSTMat:")) == 0)
+ {
+ cmd_qtstmat (packet);
+ return 1;
+ }
+ else if (strcmp ("qTMinFTPILen", packet) == 0)
+ {
+ cmd_qtminftpilen (packet);
+ return 1;
+ }
+
+ return 0;
+}
+
+#endif
+#ifndef IN_PROCESS_AGENT
+
+/* Call this when thread TINFO has hit the tracepoint defined by
+ TP_NUMBER and TP_ADDRESS, and that tracepoint has a while-stepping
+ action. This adds a while-stepping collecting state item to the
+ threads' collecting state list, so that we can keep track of
+ multiple simultaneous while-stepping actions being collected by the
+ same thread. This can happen in cases like:
+
+ ff0001 INSN1 <-- TP1, while-stepping 10 collect $regs
+ ff0002 INSN2
+ ff0003 INSN3 <-- TP2, collect $regs
+ ff0004 INSN4 <-- TP3, while-stepping 10 collect $regs
+ ff0005 INSN5
+
+ Notice that when instruction INSN5 is reached, the while-stepping
+ actions of both TP1 and TP3 are still being collected, and that TP2
+ had been collected meanwhile. The whole range of ff0001-ff0005
+ should be single-stepped, due to at least TP1's while-stepping
+ action covering the whole range. */
+
+static void
+add_while_stepping_state (struct thread_info *tinfo,
+ int tp_number, CORE_ADDR tp_address)
+{
+ struct wstep_state *wstep;
+
+ wstep = xmalloc (sizeof (*wstep));
+ wstep->next = tinfo->while_stepping;
+
+ wstep->tp_number = tp_number;
+ wstep->tp_address = tp_address;
+ wstep->current_step = 0;
+
+ tinfo->while_stepping = wstep;
+}
+
+/* Release the while-stepping collecting state WSTEP. */
+
+static void
+release_while_stepping_state (struct wstep_state *wstep)
+{
+ free (wstep);
+}
+
+/* Release all while-stepping collecting states currently associated
+ with thread TINFO. */
+
+void
+release_while_stepping_state_list (struct thread_info *tinfo)
+{
+ struct wstep_state *head;
+
+ while (tinfo->while_stepping)
+ {
+ head = tinfo->while_stepping;
+ tinfo->while_stepping = head->next;
+ release_while_stepping_state (head);
+ }
+}
+
+/* If TINFO was handling a 'while-stepping' action, the step has
+ finished, so collect any step data needed, and check if any more
+ steps are required. Return true if the thread was indeed
+ collecting tracepoint data, false otherwise. */
+
+int
+tracepoint_finished_step (struct thread_info *tinfo, CORE_ADDR stop_pc)
+{
+ struct tracepoint *tpoint;
+ struct wstep_state *wstep;
+ struct wstep_state **wstep_link;
+ struct trap_tracepoint_ctx ctx;
+
+ /* Pull in fast tracepoint trace frames from the inferior lib buffer into
+ our buffer. */
+ if (agent_loaded_p ())
+ upload_fast_traceframes ();
+
+ /* Check if we were indeed collecting data for one of more
+ tracepoints with a 'while-stepping' count. */
+ if (tinfo->while_stepping == NULL)
+ return 0;
+
+ if (!tracing)
+ {
+ /* We're not even tracing anymore. Stop this thread from
+ collecting. */
+ release_while_stepping_state_list (tinfo);
+
+ /* The thread had stopped due to a single-step request indeed
+ explained by a tracepoint. */
+ return 1;
+ }
+
+ wstep = tinfo->while_stepping;
+ wstep_link = &tinfo->while_stepping;
+
+ trace_debug ("Thread %s finished a single-step for tracepoint %d at 0x%s",
+ target_pid_to_str (tinfo->entry.id),
+ wstep->tp_number, paddress (wstep->tp_address));
+
+ ctx.base.type = trap_tracepoint;
+ ctx.regcache = get_thread_regcache (tinfo, 1);
+
+ while (wstep != NULL)
+ {
+ tpoint = find_tracepoint (wstep->tp_number, wstep->tp_address);
+ if (tpoint == NULL)
+ {
+ trace_debug ("NO TRACEPOINT %d at 0x%s FOR THREAD %s!",
+ wstep->tp_number, paddress (wstep->tp_address),
+ target_pid_to_str (tinfo->entry.id));
+
+ /* Unlink. */
+ *wstep_link = wstep->next;
+ release_while_stepping_state (wstep);
+ wstep = *wstep_link;
+ continue;
+ }
+
+ /* We've just finished one step. */
+ ++wstep->current_step;
+
+ /* Collect data. */
+ collect_data_at_step ((struct tracepoint_hit_ctx *) &ctx,
+ stop_pc, tpoint, wstep->current_step);
+
+ if (wstep->current_step >= tpoint->step_count)
+ {
+ /* The requested numbers of steps have occurred. */
+ trace_debug ("Thread %s done stepping for tracepoint %d at 0x%s",
+ target_pid_to_str (tinfo->entry.id),
+ wstep->tp_number, paddress (wstep->tp_address));
+
+ /* Unlink the wstep. */
+ *wstep_link = wstep->next;
+ release_while_stepping_state (wstep);
+ wstep = *wstep_link;
+
+ /* Only check the hit count now, which ensure that we do all
+ our stepping before stopping the run. */
+ if (tpoint->pass_count > 0
+ && tpoint->hit_count >= tpoint->pass_count
+ && stopping_tracepoint == NULL)
+ stopping_tracepoint = tpoint;
+ }
+ else
+ {
+ /* Keep single-stepping until the requested numbers of steps
+ have occurred. */
+ wstep_link = &wstep->next;
+ wstep = *wstep_link;
+ }
+
+ if (stopping_tracepoint
+ || trace_buffer_is_full
+ || expr_eval_result != expr_eval_no_error)
+ {
+ stop_tracing ();
+ break;
+ }
+ }
+
+ return 1;
+}
+
+/* Handle any internal tracing control breakpoint hits. That means,
+ pull traceframes from the IPA to our buffer, and syncing both
+ tracing agents when the IPA's tracing stops for some reason. */
+
+int
+handle_tracepoint_bkpts (struct thread_info *tinfo, CORE_ADDR stop_pc)
+{
+ /* Pull in fast tracepoint trace frames from the inferior in-process
+ agent's buffer into our buffer. */
+
+ if (!agent_loaded_p ())
+ return 0;
+
+ upload_fast_traceframes ();
+
+ /* Check if the in-process agent had decided we should stop
+ tracing. */
+ if (stop_pc == ipa_sym_addrs.addr_stop_tracing)
+ {
+ int ipa_trace_buffer_is_full;
+ CORE_ADDR ipa_stopping_tracepoint;
+ int ipa_expr_eval_result;
+ CORE_ADDR ipa_error_tracepoint;
+
+ trace_debug ("lib stopped at stop_tracing");
+
+ read_inferior_integer (ipa_sym_addrs.addr_trace_buffer_is_full,
+ &ipa_trace_buffer_is_full);
+
+ read_inferior_data_pointer (ipa_sym_addrs.addr_stopping_tracepoint,
+ &ipa_stopping_tracepoint);
+ write_inferior_data_pointer (ipa_sym_addrs.addr_stopping_tracepoint, 0);
+
+ read_inferior_data_pointer (ipa_sym_addrs.addr_error_tracepoint,
+ &ipa_error_tracepoint);
+ write_inferior_data_pointer (ipa_sym_addrs.addr_error_tracepoint, 0);
+
+ read_inferior_integer (ipa_sym_addrs.addr_expr_eval_result,
+ &ipa_expr_eval_result);
+ write_inferior_integer (ipa_sym_addrs.addr_expr_eval_result, 0);
+
+ trace_debug ("lib: trace_buffer_is_full: %d, "
+ "stopping_tracepoint: %s, "
+ "ipa_expr_eval_result: %d, "
+ "error_tracepoint: %s, ",
+ ipa_trace_buffer_is_full,
+ paddress (ipa_stopping_tracepoint),
+ ipa_expr_eval_result,
+ paddress (ipa_error_tracepoint));
+
+ if (debug_threads)
+ {
+ if (ipa_trace_buffer_is_full)
+ trace_debug ("lib stopped due to full buffer.");
+ if (ipa_stopping_tracepoint)
+ trace_debug ("lib stopped due to tpoint");
+ if (ipa_stopping_tracepoint)
+ trace_debug ("lib stopped due to error");
+ }
+
+ if (ipa_stopping_tracepoint != 0)
+ {
+ stopping_tracepoint
+ = fast_tracepoint_from_ipa_tpoint_address (ipa_stopping_tracepoint);
+ }
+ else if (ipa_expr_eval_result != expr_eval_no_error)
+ {
+ expr_eval_result = ipa_expr_eval_result;
+ error_tracepoint
+ = fast_tracepoint_from_ipa_tpoint_address (ipa_error_tracepoint);
+ }
+ stop_tracing ();
+ return 1;
+ }
+ else if (stop_pc == ipa_sym_addrs.addr_flush_trace_buffer)
+ {
+ trace_debug ("lib stopped at flush_trace_buffer");
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Return true if TINFO just hit a tracepoint. Collect data if
+ so. */
+
+int
+tracepoint_was_hit (struct thread_info *tinfo, CORE_ADDR stop_pc)
+{
+ struct tracepoint *tpoint;
+ int ret = 0;
+ struct trap_tracepoint_ctx ctx;
+
+ /* Not tracing, don't handle. */
+ if (!tracing)
+ return 0;
+
+ ctx.base.type = trap_tracepoint;
+ ctx.regcache = get_thread_regcache (tinfo, 1);
+
+ for (tpoint = tracepoints; tpoint; tpoint = tpoint->next)
+ {
+ /* Note that we collect fast tracepoints here as well. We'll
+ step over the fast tracepoint jump later, which avoids the
+ double collect. However, we don't collect for static
+ tracepoints here, because UST markers are compiled in program,
+ and probes will be executed in program. So static tracepoints
+ are collected there. */
+ if (tpoint->enabled && stop_pc == tpoint->address
+ && tpoint->type != static_tracepoint)
+ {
+ trace_debug ("Thread %s at address of tracepoint %d at 0x%s",
+ target_pid_to_str (tinfo->entry.id),
+ tpoint->number, paddress (tpoint->address));
+
+ /* Test the condition if present, and collect if true. */
+ if (!tpoint->cond
+ || (condition_true_at_tracepoint
+ ((struct tracepoint_hit_ctx *) &ctx, tpoint)))
+ collect_data_at_tracepoint ((struct tracepoint_hit_ctx *) &ctx,
+ stop_pc, tpoint);
+
+ if (stopping_tracepoint
+ || trace_buffer_is_full
+ || expr_eval_result != expr_eval_no_error)
+ {
+ stop_tracing ();
+ }
+ /* If the tracepoint had a 'while-stepping' action, then set
+ the thread to collect this tracepoint on the following
+ single-steps. */
+ else if (tpoint->step_count > 0)
+ {
+ add_while_stepping_state (tinfo,
+ tpoint->number, tpoint->address);
+ }
+
+ ret = 1;
+ }
+ }
+
+ return ret;
+}
+
+#endif
+
+#if defined IN_PROCESS_AGENT && defined HAVE_UST
+struct ust_marker_data;
+static void collect_ust_data_at_tracepoint (struct tracepoint_hit_ctx *ctx,
+ struct traceframe *tframe);
+#endif
+
+/* Create a trace frame for the hit of the given tracepoint in the
+ given thread. */
+
+static void
+collect_data_at_tracepoint (struct tracepoint_hit_ctx *ctx, CORE_ADDR stop_pc,
+ struct tracepoint *tpoint)
+{
+ struct traceframe *tframe;
+ int acti;
+
+ /* Only count it as a hit when we actually collect data. */
+ tpoint->hit_count++;
+
+ /* If we've exceeded a defined pass count, record the event for
+ later, and finish the collection for this hit. This test is only
+ for nonstepping tracepoints, stepping tracepoints test at the end
+ of their while-stepping loop. */
+ if (tpoint->pass_count > 0
+ && tpoint->hit_count >= tpoint->pass_count
+ && tpoint->step_count == 0
+ && stopping_tracepoint == NULL)
+ stopping_tracepoint = tpoint;
+
+ trace_debug ("Making new traceframe for tracepoint %d at 0x%s, hit %" PRIu64,
+ tpoint->number, paddress (tpoint->address), tpoint->hit_count);
+
+ tframe = add_traceframe (tpoint);
+
+ if (tframe)
+ {
+ for (acti = 0; acti < tpoint->numactions; ++acti)
+ {
+#ifndef IN_PROCESS_AGENT
+ trace_debug ("Tracepoint %d at 0x%s about to do action '%s'",
+ tpoint->number, paddress (tpoint->address),
+ tpoint->actions_str[acti]);
+#endif
+
+ do_action_at_tracepoint (ctx, stop_pc, tpoint, tframe,
+ tpoint->actions[acti]);
+ }
+
+ finish_traceframe (tframe);
+ }
+
+ if (tframe == NULL && tracing)
+ trace_buffer_is_full = 1;
+}
+
+#ifndef IN_PROCESS_AGENT
+
+static void
+collect_data_at_step (struct tracepoint_hit_ctx *ctx,
+ CORE_ADDR stop_pc,
+ struct tracepoint *tpoint, int current_step)
+{
+ struct traceframe *tframe;
+ int acti;
+
+ trace_debug ("Making new step traceframe for "
+ "tracepoint %d at 0x%s, step %d of %" PRIu64 ", hit %" PRIu64,
+ tpoint->number, paddress (tpoint->address),
+ current_step, tpoint->step_count,
+ tpoint->hit_count);
+
+ tframe = add_traceframe (tpoint);
+
+ if (tframe)
+ {
+ for (acti = 0; acti < tpoint->num_step_actions; ++acti)
+ {
+ trace_debug ("Tracepoint %d at 0x%s about to do step action '%s'",
+ tpoint->number, paddress (tpoint->address),
+ tpoint->step_actions_str[acti]);
+
+ do_action_at_tracepoint (ctx, stop_pc, tpoint, tframe,
+ tpoint->step_actions[acti]);
+ }
+
+ finish_traceframe (tframe);
+ }
+
+ if (tframe == NULL && tracing)
+ trace_buffer_is_full = 1;
+}
+
+#endif
+
+#ifdef IN_PROCESS_AGENT
+/* The target description used by the IPA. Given that the IPA library
+ is built for a specific architecture that is loaded into the
+ inferior, there only needs to be one such description per
+ build. */
+const struct target_desc *ipa_tdesc;
+#endif
+
+static struct regcache *
+get_context_regcache (struct tracepoint_hit_ctx *ctx)
+{
+ struct regcache *regcache = NULL;
+
+#ifdef IN_PROCESS_AGENT
+ if (ctx->type == fast_tracepoint)
+ {
+ struct fast_tracepoint_ctx *fctx = (struct fast_tracepoint_ctx *) ctx;
+ if (!fctx->regcache_initted)
+ {
+ fctx->regcache_initted = 1;
+ init_register_cache (&fctx->regcache, ipa_tdesc, fctx->regspace);
+ supply_regblock (&fctx->regcache, NULL);
+ supply_fast_tracepoint_registers (&fctx->regcache, fctx->regs);
+ }
+ regcache = &fctx->regcache;
+ }
+#ifdef HAVE_UST
+ if (ctx->type == static_tracepoint)
+ {
+ struct static_tracepoint_ctx *sctx
+ = (struct static_tracepoint_ctx *) ctx;
+
+ if (!sctx->regcache_initted)
+ {
+ sctx->regcache_initted = 1;
+ init_register_cache (&sctx->regcache, ipa_tdesc, sctx->regspace);
+ supply_regblock (&sctx->regcache, NULL);
+ /* Pass down the tracepoint address, because REGS doesn't
+ include the PC, but we know what it must have been. */
+ supply_static_tracepoint_registers (&sctx->regcache,
+ (const unsigned char *)
+ sctx->regs,
+ sctx->tpoint->address);
+ }
+ regcache = &sctx->regcache;
+ }
+#endif
+#else
+ if (ctx->type == trap_tracepoint)
+ {
+ struct trap_tracepoint_ctx *tctx = (struct trap_tracepoint_ctx *) ctx;
+ regcache = tctx->regcache;
+ }
+#endif
+
+ gdb_assert (regcache != NULL);
+
+ return regcache;
+}
+
+static void
+do_action_at_tracepoint (struct tracepoint_hit_ctx *ctx,
+ CORE_ADDR stop_pc,
+ struct tracepoint *tpoint,
+ struct traceframe *tframe,
+ struct tracepoint_action *taction)
+{
+ enum eval_result_type err;
+
+ switch (taction->type)
+ {
+ case 'M':
+ {
+ struct collect_memory_action *maction;
+ struct eval_agent_expr_context ax_ctx;
+
+ maction = (struct collect_memory_action *) taction;
+ ax_ctx.regcache = NULL;
+ ax_ctx.tframe = tframe;
+ ax_ctx.tpoint = tpoint;
+
+ trace_debug ("Want to collect %s bytes at 0x%s (basereg %d)",
+ pulongest (maction->len),
+ paddress (maction->addr), maction->basereg);
+ /* (should use basereg) */
+ agent_mem_read (&ax_ctx, NULL, (CORE_ADDR) maction->addr,
+ maction->len);
+ break;
+ }
+ case 'R':
+ {
+ unsigned char *regspace;
+ struct regcache tregcache;
+ struct regcache *context_regcache;
+ int regcache_size;
+
+ trace_debug ("Want to collect registers");
+
+ context_regcache = get_context_regcache (ctx);
+ regcache_size = register_cache_size (context_regcache->tdesc);
+
+ /* Collect all registers for now. */
+ regspace = add_traceframe_block (tframe, tpoint, 1 + regcache_size);
+ if (regspace == NULL)
+ {
+ trace_debug ("Trace buffer block allocation failed, skipping");
+ break;
+ }
+ /* Identify a register block. */
+ *regspace = 'R';
+
+ /* Wrap the regblock in a register cache (in the stack, we
+ don't want to malloc here). */
+ init_register_cache (&tregcache, context_regcache->tdesc,
+ regspace + 1);
+
+ /* Copy the register data to the regblock. */
+ regcache_cpy (&tregcache, context_regcache);
+
+#ifndef IN_PROCESS_AGENT
+ /* On some platforms, trap-based tracepoints will have the PC
+ pointing to the next instruction after the trap, but we
+ don't want the user or GDB trying to guess whether the
+ saved PC needs adjusting; so always record the adjusted
+ stop_pc. Note that we can't use tpoint->address instead,
+ since it will be wrong for while-stepping actions. This
+ adjustment is a nop for fast tracepoints collected from the
+ in-process lib (but not if GDBserver is collecting one
+ preemptively), since the PC had already been adjusted to
+ contain the tracepoint's address by the jump pad. */
+ trace_debug ("Storing stop pc (0x%s) in regblock",
+ paddress (stop_pc));
+
+ /* This changes the regblock, not the thread's
+ regcache. */
+ regcache_write_pc (&tregcache, stop_pc);
+#endif
+ }
+ break;
+ case 'X':
+ {
+ struct eval_expr_action *eaction;
+ struct eval_agent_expr_context ax_ctx;
+
+ eaction = (struct eval_expr_action *) taction;
+ ax_ctx.regcache = get_context_regcache (ctx);
+ ax_ctx.tframe = tframe;
+ ax_ctx.tpoint = tpoint;
+
+ trace_debug ("Want to evaluate expression");
+
+ err = gdb_eval_agent_expr (&ax_ctx, eaction->expr, NULL);
+
+ if (err != expr_eval_no_error)
+ {
+ record_tracepoint_error (tpoint, "action expression", err);
+ return;
+ }
+ }
+ break;
+ case 'L':
+ {
+#if defined IN_PROCESS_AGENT && defined HAVE_UST
+ trace_debug ("Want to collect static trace data");
+ collect_ust_data_at_tracepoint (ctx, tframe);
+#else
+ trace_debug ("warning: collecting static trace data, "
+ "but static tracepoints are not supported");
+#endif
+ }
+ break;
+ default:
+ trace_debug ("unknown trace action '%c', ignoring", taction->type);
+ break;
+ }
+}
+
+static int
+condition_true_at_tracepoint (struct tracepoint_hit_ctx *ctx,
+ struct tracepoint *tpoint)
+{
+ ULONGEST value = 0;
+ enum eval_result_type err;
+
+ /* Presently, gdbserver doesn't run compiled conditions, only the
+ IPA does. If the program stops at a fast tracepoint's address
+ (e.g., due to a breakpoint, trap tracepoint, or stepping),
+ gdbserver preemptively collect the fast tracepoint. Later, on
+ resume, gdbserver steps over the fast tracepoint like it steps
+ over breakpoints, so that the IPA doesn't see that fast
+ tracepoint. This avoids double collects of fast tracepoints in
+ that stopping scenario. Having gdbserver itself handle the fast
+ tracepoint gives the user a consistent view of when fast or trap
+ tracepoints are collected, compared to an alternative where only
+ trap tracepoints are collected on stop, and fast tracepoints on
+ resume. When a fast tracepoint is being processed by gdbserver,
+ it is always the non-compiled condition expression that is
+ used. */
+#ifdef IN_PROCESS_AGENT
+ if (tpoint->compiled_cond)
+ err = ((condfn) (uintptr_t) (tpoint->compiled_cond)) (ctx, &value);
+ else
+#endif
+ {
+ struct eval_agent_expr_context ax_ctx;
+
+ ax_ctx.regcache = get_context_regcache (ctx);
+ ax_ctx.tframe = NULL;
+ ax_ctx.tpoint = tpoint;
+
+ err = gdb_eval_agent_expr (&ax_ctx, tpoint->cond, &value);
+ }
+ if (err != expr_eval_no_error)
+ {
+ record_tracepoint_error (tpoint, "condition", err);
+ /* The error case must return false. */
+ return 0;
+ }
+
+ trace_debug ("Tracepoint %d at 0x%s condition evals to %s",
+ tpoint->number, paddress (tpoint->address),
+ pulongest (value));
+ return (value ? 1 : 0);
+}
+
+/* Do memory copies for bytecodes. */
+/* Do the recording of memory blocks for actions and bytecodes. */
+
+int
+agent_mem_read (struct eval_agent_expr_context *ctx,
+ unsigned char *to, CORE_ADDR from, ULONGEST len)
+{
+ unsigned char *mspace;
+ ULONGEST remaining = len;
+ unsigned short blocklen;
+
+ /* If a 'to' buffer is specified, use it. */
+ if (to != NULL)
+ {
+ read_inferior_memory (from, to, len);
+ return 0;
+ }
+
+ /* Otherwise, create a new memory block in the trace buffer. */
+ while (remaining > 0)
+ {
+ size_t sp;
+
+ blocklen = (remaining > 65535 ? 65535 : remaining);
+ sp = 1 + sizeof (from) + sizeof (blocklen) + blocklen;
+ mspace = add_traceframe_block (ctx->tframe, ctx->tpoint, sp);
+ if (mspace == NULL)
+ return 1;
+ /* Identify block as a memory block. */
+ *mspace = 'M';
+ ++mspace;
+ /* Record address and size. */
+ memcpy (mspace, &from, sizeof (from));
+ mspace += sizeof (from);
+ memcpy (mspace, &blocklen, sizeof (blocklen));
+ mspace += sizeof (blocklen);
+ /* Record the memory block proper. */
+ read_inferior_memory (from, mspace, blocklen);
+ trace_debug ("%d bytes recorded", blocklen);
+ remaining -= blocklen;
+ from += blocklen;
+ }
+ return 0;
+}
+
+int
+agent_mem_read_string (struct eval_agent_expr_context *ctx,
+ unsigned char *to, CORE_ADDR from, ULONGEST len)
+{
+ unsigned char *buf, *mspace;
+ ULONGEST remaining = len;
+ unsigned short blocklen, i;
+
+ /* To save a bit of space, block lengths are 16-bit, so break large
+ requests into multiple blocks. Bordering on overkill for strings,
+ but it could happen that someone specifies a large max length. */
+ while (remaining > 0)
+ {
+ size_t sp;
+
+ blocklen = (remaining > 65535 ? 65535 : remaining);
+ /* We want working space to accumulate nonzero bytes, since
+ traceframes must have a predecided size (otherwise it gets
+ harder to wrap correctly for the circular case, etc). */
+ buf = (unsigned char *) xmalloc (blocklen + 1);
+ for (i = 0; i < blocklen; ++i)
+ {
+ /* Read the string one byte at a time, in case the string is
+ at the end of a valid memory area - we don't want a
+ correctly-terminated string to engender segvio
+ complaints. */
+ read_inferior_memory (from + i, buf + i, 1);
+
+ if (buf[i] == '\0')
+ {
+ blocklen = i + 1;
+ /* Make sure outer loop stops now too. */
+ remaining = blocklen;
+ break;
+ }
+ }
+ sp = 1 + sizeof (from) + sizeof (blocklen) + blocklen;
+ mspace = add_traceframe_block (ctx->tframe, ctx->tpoint, sp);
+ if (mspace == NULL)
+ {
+ xfree (buf);
+ return 1;
+ }
+ /* Identify block as a memory block. */
+ *mspace = 'M';
+ ++mspace;
+ /* Record address and size. */
+ memcpy ((void *) mspace, (void *) &from, sizeof (from));
+ mspace += sizeof (from);
+ memcpy ((void *) mspace, (void *) &blocklen, sizeof (blocklen));
+ mspace += sizeof (blocklen);
+ /* Copy the string contents. */
+ memcpy ((void *) mspace, (void *) buf, blocklen);
+ remaining -= blocklen;
+ from += blocklen;
+ xfree (buf);
+ }
+ return 0;
+}
+
+/* Record the value of a trace state variable. */
+
+int
+agent_tsv_read (struct eval_agent_expr_context *ctx, int n)
+{
+ unsigned char *vspace;
+ LONGEST val;
+
+ vspace = add_traceframe_block (ctx->tframe, ctx->tpoint,
+ 1 + sizeof (n) + sizeof (LONGEST));
+ if (vspace == NULL)
+ return 1;
+ /* Identify block as a variable. */
+ *vspace = 'V';
+ /* Record variable's number and value. */
+ memcpy (vspace + 1, &n, sizeof (n));
+ val = get_trace_state_variable_value (n);
+ memcpy (vspace + 1 + sizeof (n), &val, sizeof (val));
+ trace_debug ("Variable %d recorded", n);
+ return 0;
+}
+
+#ifndef IN_PROCESS_AGENT
+
+/* Callback for traceframe_walk_blocks, used to find a given block
+ type in a traceframe. */
+
+static int
+match_blocktype (char blocktype, unsigned char *dataptr, void *data)
+{
+ char *wantedp = data;
+
+ if (*wantedp == blocktype)
+ return 1;
+
+ return 0;
+}
+
+/* Walk over all traceframe blocks of the traceframe buffer starting
+ at DATABASE, of DATASIZE bytes long, and call CALLBACK for each
+ block found, passing in DATA unmodified. If CALLBACK returns true,
+ this returns a pointer to where the block is found. Returns NULL
+ if no callback call returned true, indicating that all blocks have
+ been walked. */
+
+static unsigned char *
+traceframe_walk_blocks (unsigned char *database, unsigned int datasize,
+ int tfnum,
+ int (*callback) (char blocktype,
+ unsigned char *dataptr,
+ void *data),
+ void *data)
+{
+ unsigned char *dataptr;
+
+ if (datasize == 0)
+ {
+ trace_debug ("traceframe %d has no data", tfnum);
+ return NULL;
+ }
+
+ /* Iterate through a traceframe's blocks, looking for a block of the
+ requested type. */
+ for (dataptr = database;
+ dataptr < database + datasize;
+ /* nothing */)
+ {
+ char blocktype;
+ unsigned short mlen;
+
+ if (dataptr == trace_buffer_wrap)
+ {
+ /* Adjust to reflect wrapping part of the frame around to
+ the beginning. */
+ datasize = dataptr - database;
+ dataptr = database = trace_buffer_lo;
+ }
+
+ blocktype = *dataptr++;
+
+ if ((*callback) (blocktype, dataptr, data))
+ return dataptr;
+
+ switch (blocktype)
+ {
+ case 'R':
+ /* Skip over the registers block. */
+ dataptr += current_target_desc ()->registers_size;
+ break;
+ case 'M':
+ /* Skip over the memory block. */
+ dataptr += sizeof (CORE_ADDR);
+ memcpy (&mlen, dataptr, sizeof (mlen));
+ dataptr += (sizeof (mlen) + mlen);
+ break;
+ case 'V':
+ /* Skip over the TSV block. */
+ dataptr += (sizeof (int) + sizeof (LONGEST));
+ break;
+ case 'S':
+ /* Skip over the static trace data block. */
+ memcpy (&mlen, dataptr, sizeof (mlen));
+ dataptr += (sizeof (mlen) + mlen);
+ break;
+ default:
+ trace_debug ("traceframe %d has unknown block type 0x%x",
+ tfnum, blocktype);
+ return NULL;
+ }
+ }
+
+ return NULL;
+}
+
+/* Look for the block of type TYPE_WANTED in the trameframe starting
+ at DATABASE of DATASIZE bytes long. TFNUM is the traceframe
+ number. */
+
+static unsigned char *
+traceframe_find_block_type (unsigned char *database, unsigned int datasize,
+ int tfnum, char type_wanted)
+{
+ return traceframe_walk_blocks (database, datasize, tfnum,
+ match_blocktype, &type_wanted);
+}
+
+static unsigned char *
+traceframe_find_regblock (struct traceframe *tframe, int tfnum)
+{
+ unsigned char *regblock;
+
+ regblock = traceframe_find_block_type (tframe->data,
+ tframe->data_size,
+ tfnum, 'R');
+
+ if (regblock == NULL)
+ trace_debug ("traceframe %d has no register data", tfnum);
+
+ return regblock;
+}
+
+/* Get registers from a traceframe. */
+
+int
+fetch_traceframe_registers (int tfnum, struct regcache *regcache, int regnum)
+{
+ unsigned char *dataptr;
+ struct tracepoint *tpoint;
+ struct traceframe *tframe;
+
+ tframe = find_traceframe (tfnum);
+
+ if (tframe == NULL)
+ {
+ trace_debug ("traceframe %d not found", tfnum);
+ return 1;
+ }
+
+ dataptr = traceframe_find_regblock (tframe, tfnum);
+ if (dataptr == NULL)
+ {
+ /* Mark registers unavailable. */
+ supply_regblock (regcache, NULL);
+
+ /* We can generally guess at a PC, although this will be
+ misleading for while-stepping frames and multi-location
+ tracepoints. */
+ tpoint = find_next_tracepoint_by_number (NULL, tframe->tpnum);
+ if (tpoint != NULL)
+ regcache_write_pc (regcache, tpoint->address);
+ }
+ else
+ supply_regblock (regcache, dataptr);
+
+ return 0;
+}
+
+static CORE_ADDR
+traceframe_get_pc (struct traceframe *tframe)
+{
+ struct regcache regcache;
+ unsigned char *dataptr;
+ const struct target_desc *tdesc = current_target_desc ();
+
+ dataptr = traceframe_find_regblock (tframe, -1);
+ if (dataptr == NULL)
+ return 0;
+
+ init_register_cache (®cache, tdesc, dataptr);
+ return regcache_read_pc (®cache);
+}
+
+/* Read a requested block of memory from a trace frame. */
+
+int
+traceframe_read_mem (int tfnum, CORE_ADDR addr,
+ unsigned char *buf, ULONGEST length,
+ ULONGEST *nbytes)
+{
+ struct traceframe *tframe;
+ unsigned char *database, *dataptr;
+ unsigned int datasize;
+ CORE_ADDR maddr;
+ unsigned short mlen;
+
+ trace_debug ("traceframe_read_mem");
+
+ tframe = find_traceframe (tfnum);
+
+ if (!tframe)
+ {
+ trace_debug ("traceframe %d not found", tfnum);
+ return 1;
+ }
+
+ datasize = tframe->data_size;
+ database = dataptr = &tframe->data[0];
+
+ /* Iterate through a traceframe's blocks, looking for memory. */
+ while ((dataptr = traceframe_find_block_type (dataptr,
+ datasize
+ - (dataptr - database),
+ tfnum, 'M')) != NULL)
+ {
+ memcpy (&maddr, dataptr, sizeof (maddr));
+ dataptr += sizeof (maddr);
+ memcpy (&mlen, dataptr, sizeof (mlen));
+ dataptr += sizeof (mlen);
+ trace_debug ("traceframe %d has %d bytes at %s",
+ tfnum, mlen, paddress (maddr));
+
+ /* If the block includes the first part of the desired range,
+ return as much it has; GDB will re-request the remainder,
+ which might be in a different block of this trace frame. */
+ if (maddr <= addr && addr < (maddr + mlen))
+ {
+ ULONGEST amt = (maddr + mlen) - addr;
+ if (amt > length)
+ amt = length;
+
+ memcpy (buf, dataptr + (addr - maddr), amt);
+ *nbytes = amt;
+ return 0;
+ }
+
+ /* Skip over this block. */
+ dataptr += mlen;
+ }
+
+ trace_debug ("traceframe %d has no memory data for the desired region",
+ tfnum);
+
+ *nbytes = 0;
+ return 0;
+}
+
+static int
+traceframe_read_tsv (int tsvnum, LONGEST *val)
+{
+ int tfnum;
+ struct traceframe *tframe;
+ unsigned char *database, *dataptr;
+ unsigned int datasize;
+ int vnum;
+ int found = 0;
+
+ trace_debug ("traceframe_read_tsv");
+
+ tfnum = current_traceframe;
+
+ if (tfnum < 0)
+ {
+ trace_debug ("no current traceframe");
+ return 1;
+ }
+
+ tframe = find_traceframe (tfnum);
+
+ if (tframe == NULL)
+ {
+ trace_debug ("traceframe %d not found", tfnum);
+ return 1;
+ }
+
+ datasize = tframe->data_size;
+ database = dataptr = &tframe->data[0];
+
+ /* Iterate through a traceframe's blocks, looking for the last
+ matched tsv. */
+ while ((dataptr = traceframe_find_block_type (dataptr,
+ datasize
+ - (dataptr - database),
+ tfnum, 'V')) != NULL)
+ {
+ memcpy (&vnum, dataptr, sizeof (vnum));
+ dataptr += sizeof (vnum);
+
+ trace_debug ("traceframe %d has variable %d", tfnum, vnum);
+
+ /* Check that this is the variable we want. */
+ if (tsvnum == vnum)
+ {
+ memcpy (val, dataptr, sizeof (*val));
+ found = 1;
+ }
+
+ /* Skip over this block. */
+ dataptr += sizeof (LONGEST);
+ }