/* Branch trace support for GDB, the GNU debugger.
- Copyright (C) 2013-2019 Free Software Foundation, Inc.
+ Copyright (C) 2013-2021 Free Software Foundation, Inc.
Contributed by Intel Corp. <markus.t.metzger@intel.com>
#include "frame-unwind.h"
#include "hashtab.h"
#include "infrun.h"
-#include "event-loop.h"
+#include "gdbsupport/event-loop.h"
#include "inf-loop.h"
-#include "gdbsupport/vec.h"
#include "inferior.h"
#include <algorithm>
+#include "gdbarch.h"
+#include "cli/cli-style.h"
+#include "async-event.h"
+#include <forward_list>
static const target_info record_btrace_target_info = {
"record-btrace",
const struct frame_unwind *get_tailcall_unwinder () override;
- void commit_resume () override;
void resume (ptid_t, int, enum gdb_signal) override;
- ptid_t wait (ptid_t, struct target_waitstatus *, int) override;
+ ptid_t wait (ptid_t, struct target_waitstatus *, target_wait_flags) override;
void stop (ptid_t) override;
void update_thread_list () override;
do \
{ \
if (record_debug != 0) \
- fprintf_unfiltered (gdb_stdlog, \
+ fprintf_unfiltered (gdb_stdlog, \
"[record-btrace] " msg "\n", ##args); \
} \
while (0)
static void
record_btrace_enable_warn (struct thread_info *tp)
{
+ /* Ignore this thread if its inferior is not recorded by us. */
+ target_ops *rec = tp->inf->target_at (record_stratum);
+ if (rec != &record_btrace_ops)
+ return;
+
try
{
btrace_enable (tp, &record_btrace_conf);
DEBUG ("attach thread observer");
gdb::observers::new_thread.attach (record_btrace_enable_warn,
- record_btrace_thread_observer_token);
+ record_btrace_thread_observer_token,
+ "record-btrace");
}
/* Disable automatic tracing of new threads. */
static void
record_btrace_handle_async_inferior_event (gdb_client_data data)
{
- inferior_event_handler (INF_REG_EVENT, NULL);
+ inferior_event_handler (INF_REG_EVENT);
}
/* See record-btrace.h. */
record_btrace_auto_enable ();
- push_target (&record_btrace_ops);
+ current_inferior ()->push_target (&record_btrace_ops);
record_btrace_async_inferior_event_handler
= create_async_event_handler (record_btrace_handle_async_inferior_event,
- NULL);
+ NULL, "record-btrace");
record_btrace_generating_corefile = 0;
format = btrace_format_short_string (record_btrace_conf.format);
record_preopen ();
- if (!target_has_execution)
+ if (!target_has_execution ())
error (_("The program is not being run."));
- for (thread_info *tp : all_non_exited_threads ())
+ for (thread_info *tp : current_inferior ()->non_exited_threads ())
if (args == NULL || *args == 0 || number_is_in_list (args, tp->global_num))
{
btrace_enable (tp, &record_btrace_conf);
record_btrace_auto_disable ();
- for (thread_info *tp : all_non_exited_threads ())
+ for (thread_info *tp : current_inferior ()->non_exited_threads ())
if (tp->btrace.target != NULL)
btrace_disable (tp);
}
struct target_ops *beneath = this->beneath ();
/* Do not stop recording, just clean up GDB side. */
- unpush_target (this);
+ current_inferior ()->unpush_target (this);
/* Forward disconnect. */
beneath->disconnect (args, from_tty);
/* We should have already stopped recording.
Tear down btrace in case we have not. */
- for (thread_info *tp : all_non_exited_threads ())
+ for (thread_info *tp : current_inferior ()->non_exited_threads ())
btrace_teardown (tp);
}
return;
}
- internal_error (__FILE__, __LINE__, _("Unkown branch trace format."));
+ internal_error (__FILE__, __LINE__, _("Unknown branch trace format."));
}
/* The info_record method of target record-btrace. */
DEBUG ("info");
- tp = find_thread_ptid (inferior_ptid);
- if (tp == NULL)
+ if (inferior_ptid == null_ptid)
error (_("No thread."));
+ tp = inferior_thread ();
+
validate_registers_access ();
btinfo = &tp->btrace;
if (!(format == BTRACE_FORMAT_PT && errcode > 0))
{
uiout->text (_("decode error ("));
- uiout->field_int ("errcode", errcode);
+ uiout->field_signed ("errcode", errcode);
uiout->text (_("): "));
}
uiout->text (errstr);
uiout->text (_("]\n"));
}
-/* Print an unsigned int. */
-
-static void
-ui_out_field_uint (struct ui_out *uiout, const char *fld, unsigned int val)
-{
- uiout->field_fmt (fld, "%u", val);
-}
-
/* A range of source lines. */
struct btrace_line_range
range = btrace_mk_line_range (symtab, 0, 0);
for (i = 0; i < nlines - 1; i++)
{
- if ((lines[i].pc == pc) && (lines[i].line != 0))
+ /* The test of is_stmt here was added when the is_stmt field was
+ introduced to the 'struct linetable_entry' structure. This
+ ensured that this loop maintained the same behaviour as before we
+ introduced is_stmt. That said, it might be that we would be
+ better off not checking is_stmt here, this would lead to us
+ possibly adding more line numbers to the range. At the time this
+ change was made I was unsure how to test this so chose to go with
+ maintaining the existing experience. */
+ if ((lines[i].pc == pc) && (lines[i].line != 0)
+ && (lines[i].is_stmt == 1))
range = btrace_line_range_add (range, lines[i].line);
}
gdb::optional<ui_out_emit_tuple> src_and_asm_tuple;
gdb::optional<ui_out_emit_list> asm_list;
- gdb_pretty_print_disassembler disasm (gdbarch);
+ gdb_pretty_print_disassembler disasm (gdbarch, uiout);
for (btrace_insn_iterator it = *begin; btrace_insn_cmp (&it, end) != 0;
- btrace_insn_next (&it, 1))
+ btrace_insn_next (&it, 1))
{
const struct btrace_insn *insn;
if ((insn->flags & BTRACE_INSN_FLAG_SPECULATIVE) != 0)
dinsn.is_speculative = 1;
- disasm.pretty_print_insn (uiout, &dinsn, flags);
+ disasm.pretty_print_insn (&dinsn, flags);
}
}
}
begin = bfun->insn_offset;
end = begin + size - 1;
- ui_out_field_uint (uiout, "insn begin", begin);
+ uiout->field_unsigned ("insn begin", begin);
uiout->text (",");
- ui_out_field_uint (uiout, "insn end", end);
+ uiout->field_unsigned ("insn end", end);
}
/* Compute the lowest and highest source line for the instructions in BFUN
uiout->field_string ("file",
symtab_to_filename_for_display (symbol_symtab (sym)),
- ui_out_style_kind::FILE);
+ file_name_style.style ());
btrace_compute_src_line_range (bfun, &begin, &end);
if (end < begin)
return;
uiout->text (":");
- uiout->field_int ("min line", begin);
+ uiout->field_signed ("min line", begin);
if (end == begin)
return;
uiout->text (",");
- uiout->field_int ("max line", end);
+ uiout->field_signed ("max line", end);
}
/* Get the name of a branch trace function. */
sym = bfun->sym;
if (sym != NULL)
- return SYMBOL_PRINT_NAME (sym);
+ return sym->print_name ();
else if (msym != NULL)
- return MSYMBOL_PRINT_NAME (msym);
+ return msym->print_name ();
else
return "??";
}
msym = bfun->msym;
/* Print the function index. */
- ui_out_field_uint (uiout, "index", bfun->number);
+ uiout->field_unsigned ("index", bfun->number);
uiout->text ("\t");
/* Indicate gaps in the trace. */
}
if (sym != NULL)
- uiout->field_string ("function", SYMBOL_PRINT_NAME (sym),
- ui_out_style_kind::FUNCTION);
+ uiout->field_string ("function", sym->print_name (),
+ function_name_style.style ());
else if (msym != NULL)
- uiout->field_string ("function", MSYMBOL_PRINT_NAME (msym),
- ui_out_style_kind::FUNCTION);
+ uiout->field_string ("function", msym->print_name (),
+ function_name_style.style ());
else if (!uiout->is_mi_like_p ())
uiout->field_string ("function", "??",
- ui_out_style_kind::FUNCTION);
+ function_name_style.style ());
if ((flags & RECORD_PRINT_INSN_RANGE) != 0)
{
enum record_method
record_btrace_target::record_method (ptid_t ptid)
{
- struct thread_info * const tp = find_thread_ptid (ptid);
+ process_stratum_target *proc_target = current_inferior ()->process_target ();
+ thread_info *const tp = find_thread_ptid (proc_target, ptid);
if (tp == NULL)
error (_("No thread."));
bool
record_btrace_target::record_is_replaying (ptid_t ptid)
{
- for (thread_info *tp : all_non_exited_threads (ptid))
+ process_stratum_target *proc_target = current_inferior ()->process_target ();
+ for (thread_info *tp : all_non_exited_threads (proc_target, ptid))
if (btrace_is_replaying (tp))
return true;
{
case TARGET_OBJECT_MEMORY:
{
- struct target_section *section;
+ const struct target_section *section;
/* We do not allow writing memory in general. */
if (writebuf != NULL)
if (section != NULL)
{
/* Check if the section we found is readonly. */
- if ((bfd_get_section_flags (section->the_bfd_section->owner,
- section->the_bfd_section)
+ if ((bfd_section_flags (section->the_bfd_section)
& SEC_READONLY) != 0)
{
/* Truncate the request to fit into this section. */
void
record_btrace_target::fetch_registers (struct regcache *regcache, int regno)
{
- struct btrace_insn_iterator *replay;
- struct thread_info *tp;
+ btrace_insn_iterator *replay = nullptr;
- tp = find_thread_ptid (regcache->ptid ());
- gdb_assert (tp != NULL);
+ /* Thread-db may ask for a thread's registers before GDB knows about the
+ thread. We forward the request to the target beneath in this
+ case. */
+ thread_info *tp = find_thread_ptid (regcache->target (), regcache->ptid ());
+ if (tp != nullptr)
+ replay = tp->btrace.replay;
- replay = tp->btrace.replay;
- if (replay != NULL && !record_btrace_generating_corefile)
+ if (replay != nullptr && !record_btrace_generating_corefile)
{
const struct btrace_insn *insn;
struct gdbarch *gdbarch;
&& record_is_replaying (regcache->ptid ()))
error (_("Cannot write registers while replaying."));
- gdb_assert (may_write_registers != 0);
+ gdb_assert (may_write_registers);
this->beneath ()->store_registers (regcache, regno);
}
}
/* btrace recording does not store previous memory content, neither the stack
- frames content. Any unwinding would return errorneous results as the stack
+ frames content. Any unwinding would return erroneous results as the stack
contents no longer matches the changed PC value restored from history.
Therefore this unwinder reports any possibly unwound registers as
<unavailable>. */
/* Return a human-readable string for FLAG. */
static const char *
-btrace_thread_flag_to_str (enum btrace_thread_flag flag)
+btrace_thread_flag_to_str (btrace_thread_flags flag)
{
switch (flag)
{
get_thread_current_frame_id (struct thread_info *tp)
{
struct frame_id id;
- int executing;
+ bool executing;
/* Set current thread, which is implicitly used by
get_current_frame. */
switch_to_thread (tp);
+ process_stratum_target *proc_target = tp->inf->process_target ();
+
/* Clear the executing flag to allow changes to the current frame.
We are not actually running, yet. We just started a reverse execution
command or a record goto command.
For the latter, EXECUTING is false and this has no effect.
For the former, EXECUTING is true and we're in wait, about to
move the thread. Since we need to recompute the stack, we temporarily
- set EXECUTING to flase. */
+ set EXECUTING to false. */
executing = tp->executing;
- set_executing (inferior_ptid, false);
+ set_executing (proc_target, inferior_ptid, false);
id = null_frame_id;
try
catch (const gdb_exception &except)
{
/* Restore the previous execution state. */
- set_executing (inferior_ptid, executing);
+ set_executing (proc_target, inferior_ptid, executing);
throw;
}
/* Restore the previous execution state. */
- set_executing (inferior_ptid, executing);
+ set_executing (proc_target, inferior_ptid, executing);
return id;
}
record_btrace_wait below.
For all-stop targets, we only step INFERIOR_PTID and continue others. */
+
+ process_stratum_target *proc_target = current_inferior ()->process_target ();
+
if (!target_is_non_stop_p ())
{
gdb_assert (inferior_ptid.matches (ptid));
- for (thread_info *tp : all_non_exited_threads (ptid))
+ for (thread_info *tp : all_non_exited_threads (proc_target, ptid))
{
if (tp->ptid.matches (inferior_ptid))
record_btrace_resume_thread (tp, flag);
}
else
{
- for (thread_info *tp : all_non_exited_threads (ptid))
+ for (thread_info *tp : all_non_exited_threads (proc_target, ptid))
record_btrace_resume_thread (tp, flag);
}
}
}
-/* The commit_resume method of target record-btrace. */
-
-void
-record_btrace_target::commit_resume ()
-{
- if ((::execution_direction != EXEC_REVERSE)
- && !record_is_replaying (minus_one_ptid))
- beneath ()->commit_resume ();
-}
-
/* Cancel resuming TP. */
static void
record_btrace_cancel_resume (struct thread_info *tp)
{
- enum btrace_thread_flag flags;
+ btrace_thread_flags flags;
flags = tp->btrace.flags & (BTHR_MOVE | BTHR_STOP);
if (flags == 0)
DEBUG ("cancel resume thread %s (%s): %x (%s)",
print_thread_id (tp),
- target_pid_to_str (tp->ptid).c_str (), flags,
+ target_pid_to_str (tp->ptid).c_str (), flags.raw (),
btrace_thread_flag_to_str (flags));
tp->btrace.flags &= ~(BTHR_MOVE | BTHR_STOP);
{
struct btrace_thread_info *btinfo;
struct target_waitstatus status;
- enum btrace_thread_flag flags;
+ btrace_thread_flags flags;
btinfo = &tp->btrace;
btinfo->flags &= ~(BTHR_MOVE | BTHR_STOP);
DEBUG ("stepping thread %s (%s): %x (%s)", print_thread_id (tp),
- target_pid_to_str (tp->ptid).c_str (), flags,
+ target_pid_to_str (tp->ptid).c_str (), flags.raw (),
btrace_thread_flag_to_str (flags));
/* We can't step without an execution history. */
ptid_t
record_btrace_target::wait (ptid_t ptid, struct target_waitstatus *status,
- int options)
+ target_wait_flags options)
{
std::vector<thread_info *> moving;
std::vector<thread_info *> no_history;
- DEBUG ("wait %s (0x%x)", target_pid_to_str (ptid).c_str (), options);
+ /* Clear this, if needed we'll re-mark it below. */
+ clear_async_event_handler (record_btrace_async_inferior_event_handler);
+
+ DEBUG ("wait %s (0x%x)", target_pid_to_str (ptid).c_str (),
+ (unsigned) options);
/* As long as we're not replaying, just forward the request. */
if ((::execution_direction != EXEC_REVERSE)
}
/* Keep a work list of moving threads. */
- for (thread_info *tp : all_non_exited_threads (ptid))
+ process_stratum_target *proc_target = current_inferior ()->process_target ();
+ for (thread_info *tp : all_non_exited_threads (proc_target, ptid))
if ((tp->btrace.flags & (BTHR_MOVE | BTHR_STOP)) != 0)
moving.push_back (tp);
/* Stop all other threads. */
if (!target_is_non_stop_p ())
{
- for (thread_info *tp : all_non_exited_threads ())
+ for (thread_info *tp : current_inferior ()->non_exited_threads ())
record_btrace_cancel_resume (tp);
}
}
else
{
- for (thread_info *tp : all_non_exited_threads (ptid))
+ process_stratum_target *proc_target
+ = current_inferior ()->process_target ();
+
+ for (thread_info *tp : all_non_exited_threads (proc_target, ptid))
{
tp->btrace.flags &= ~BTHR_MOVE;
tp->btrace.flags |= BTHR_STOP;
void
record_btrace_target::record_stop_replaying ()
{
- for (thread_info *tp : all_non_exited_threads ())
+ for (thread_info *tp : current_inferior ()->non_exited_threads ())
record_btrace_stop_replaying (tp);
}
}
}
-/* The "set record btrace" command. */
-
-static void
-cmd_set_record_btrace (const char *args, int from_tty)
-{
- printf_unfiltered (_("\"set record btrace\" must be followed "
- "by an appropriate subcommand.\n"));
- help_list (set_record_btrace_cmdlist, "set record btrace ",
- all_commands, gdb_stdout);
-}
-
-/* The "show record btrace" command. */
-
-static void
-cmd_show_record_btrace (const char *args, int from_tty)
-{
- cmd_show_list (show_record_btrace_cmdlist, from_tty, "");
-}
-
/* The "show record btrace replay-memory-access" command. */
static void
error (_("Internal error: bad cpu state."));
}
-/* The "s record btrace bts" command. */
-
-static void
-cmd_set_record_btrace_bts (const char *args, int from_tty)
-{
- printf_unfiltered (_("\"set record btrace bts\" must be followed "
- "by an appropriate subcommand.\n"));
- help_list (set_record_btrace_bts_cmdlist, "set record btrace bts ",
- all_commands, gdb_stdout);
-}
-
-/* The "show record btrace bts" command. */
-
-static void
-cmd_show_record_btrace_bts (const char *args, int from_tty)
-{
- cmd_show_list (show_record_btrace_bts_cmdlist, from_tty, "");
-}
-
-/* The "set record btrace pt" command. */
-
-static void
-cmd_set_record_btrace_pt (const char *args, int from_tty)
-{
- printf_unfiltered (_("\"set record btrace pt\" must be followed "
- "by an appropriate subcommand.\n"));
- help_list (set_record_btrace_pt_cmdlist, "set record btrace pt ",
- all_commands, gdb_stdout);
-}
-
-/* The "show record btrace pt" command. */
-
-static void
-cmd_show_record_btrace_pt (const char *args, int from_tty)
-{
- cmd_show_list (show_record_btrace_pt_cmdlist, from_tty, "");
-}
-
/* The "record bts buffer-size" show value function. */
static void
/* Initialize btrace commands. */
+void _initialize_record_btrace ();
void
-_initialize_record_btrace (void)
+_initialize_record_btrace ()
{
add_prefix_cmd ("btrace", class_obscure, cmd_record_btrace_start,
_("Start branch trace recording."), &record_btrace_cmdlist,
- "record btrace ", 0, &record_cmdlist);
+ 0, &record_cmdlist);
add_alias_cmd ("b", "btrace", class_obscure, 1, &record_cmdlist);
add_cmd ("bts", class_obscure, cmd_record_btrace_bts_start,
&record_btrace_cmdlist);
add_alias_cmd ("pt", "btrace pt", class_obscure, 1, &record_cmdlist);
- add_prefix_cmd ("btrace", class_support, cmd_set_record_btrace,
- _("Set record options"), &set_record_btrace_cmdlist,
- "set record btrace ", 0, &set_record_cmdlist);
+ add_basic_prefix_cmd ("btrace", class_support,
+ _("Set record options."), &set_record_btrace_cmdlist,
+ 0, &set_record_cmdlist);
- add_prefix_cmd ("btrace", class_support, cmd_show_record_btrace,
- _("Show record options"), &show_record_btrace_cmdlist,
- "show record btrace ", 0, &show_record_cmdlist);
+ add_show_prefix_cmd ("btrace", class_support,
+ _("Show record options."), &show_record_btrace_cmdlist,
+ 0, &show_record_cmdlist);
add_setshow_enum_cmd ("replay-memory-access", no_class,
replay_memory_access_types, &replay_memory_access, _("\
workarounds for a similar cpu that GDB supports.\n\n\
When set to \"none\", errata workarounds are disabled."),
&set_record_btrace_cpu_cmdlist,
- _("set record btrace cpu "), 1,
+ 1,
&set_record_btrace_cmdlist);
add_cmd ("auto", class_support, cmd_set_record_btrace_cpu_auto, _("\
Show the cpu to be used for trace decode."),
&show_record_btrace_cmdlist);
- add_prefix_cmd ("bts", class_support, cmd_set_record_btrace_bts,
- _("Set record btrace bts options"),
- &set_record_btrace_bts_cmdlist,
- "set record btrace bts ", 0, &set_record_btrace_cmdlist);
+ add_basic_prefix_cmd ("bts", class_support,
+ _("Set record btrace bts options."),
+ &set_record_btrace_bts_cmdlist,
+ 0,
+ &set_record_btrace_cmdlist);
- add_prefix_cmd ("bts", class_support, cmd_show_record_btrace_bts,
- _("Show record btrace bts options"),
- &show_record_btrace_bts_cmdlist,
- "show record btrace bts ", 0, &show_record_btrace_cmdlist);
+ add_show_prefix_cmd ("bts", class_support,
+ _("Show record btrace bts options."),
+ &show_record_btrace_bts_cmdlist,
+ 0,
+ &show_record_btrace_cmdlist);
add_setshow_uinteger_cmd ("buffer-size", no_class,
&record_btrace_conf.bts.size,
&set_record_btrace_bts_cmdlist,
&show_record_btrace_bts_cmdlist);
- add_prefix_cmd ("pt", class_support, cmd_set_record_btrace_pt,
- _("Set record btrace pt options"),
- &set_record_btrace_pt_cmdlist,
- "set record btrace pt ", 0, &set_record_btrace_cmdlist);
-
- add_prefix_cmd ("pt", class_support, cmd_show_record_btrace_pt,
- _("Show record btrace pt options"),
- &show_record_btrace_pt_cmdlist,
- "show record btrace pt ", 0, &show_record_btrace_cmdlist);
+ add_basic_prefix_cmd ("pt", class_support,
+ _("Set record btrace pt options."),
+ &set_record_btrace_pt_cmdlist,
+ 0,
+ &set_record_btrace_cmdlist);
+
+ add_show_prefix_cmd ("pt", class_support,
+ _("Show record btrace pt options."),
+ &show_record_btrace_pt_cmdlist,
+ 0,
+ &show_record_btrace_cmdlist);
add_setshow_uinteger_cmd ("buffer-size", no_class,
&record_btrace_conf.pt.size,