/* Process record and replay target for GDB, the GNU debugger.
- Copyright (C) 2013-2015 Free Software Foundation, Inc.
+ Copyright (C) 2013-2017 Free Software Foundation, Inc.
This file is part of GDB.
#include "gdb_bfd.h"
#include "observer.h"
#include "infrun.h"
+#include "common/gdb_unlinker.h"
#include <signal.h>
struct record_full_entry *rec;
struct gdbarch *gdbarch = get_regcache_arch (regcache);
- rec = xcalloc (1, sizeof (struct record_full_entry));
+ rec = XCNEW (struct record_full_entry);
rec->type = record_full_reg;
rec->u.reg.num = regnum;
rec->u.reg.len = register_size (gdbarch, regnum);
{
struct record_full_entry *rec;
- rec = xcalloc (1, sizeof (struct record_full_entry));
+ rec = XCNEW (struct record_full_entry);
rec->type = record_full_mem;
rec->u.mem.addr = addr;
rec->u.mem.len = len;
{
struct record_full_entry *rec;
- rec = xcalloc (1, sizeof (struct record_full_entry));
+ rec = XCNEW (struct record_full_entry);
rec->type = record_full_end;
return rec;
}
static void
-record_full_check_insn_num (int set_terminal)
+record_full_check_insn_num (void)
{
if (record_full_insn_num == record_full_insn_max_num)
{
/* Ask user what to do. */
if (record_full_stop_at_limit)
{
- int q;
-
- if (set_terminal)
- target_terminal_ours ();
- q = yquery (_("Do you want to auto delete previous execution "
+ if (!yquery (_("Do you want to auto delete previous execution "
"log entries when record/replay buffer becomes "
- "full (record full stop-at-limit)?"));
- if (set_terminal)
- target_terminal_inferior ();
- if (q)
- record_full_stop_at_limit = 0;
- else
+ "full (record full stop-at-limit)?")))
error (_("Process record: stopped by user."));
+ record_full_stop_at_limit = 0;
}
}
}
record_full_arch_list_tail = NULL;
/* Check record_full_insn_num. */
- record_full_check_insn_num (1);
+ record_full_check_insn_num ();
/* If gdb sends a signal value to target_resume,
save it in the 'end' field of the previous instruction.
static int
record_full_message_wrapper (void *args)
{
- struct record_full_message_args *record_full_args = args;
+ struct record_full_message_args *record_full_args
+ = (struct record_full_message_args *) args;
return record_full_message (record_full_args->regcache,
record_full_args->signal);
args.regcache = regcache;
args.signal = signal;
- return catch_errors (record_full_message_wrapper, &args, NULL,
+ return catch_errors (record_full_message_wrapper, &args, "",
RETURN_MASK_ALL);
}
/* Nothing to do if the entry is flagged not_accessible. */
if (!entry->u.mem.mem_entry_not_accessible)
{
- gdb_byte *mem = alloca (entry->u.mem.len);
+ gdb_byte *mem = (gdb_byte *) xmalloc (entry->u.mem.len);
+ struct cleanup *cleanup = make_cleanup (xfree, mem);
if (record_debug > 1)
fprintf_unfiltered (gdb_stdlog,
record_full_stop_reason = TARGET_STOPPED_BY_WATCHPOINT;
}
}
+
+ do_cleanups (cleanup);
}
}
break;
/* Get record_full_core_regbuf. */
target_fetch_registers (regcache, -1);
- record_full_core_regbuf = xmalloc (MAX_REGISTER_SIZE * regnum);
+ record_full_core_regbuf = (gdb_byte *) xmalloc (MAX_REGISTER_SIZE * regnum);
for (i = 0; i < regnum; i ++)
regcache_raw_collect (regcache, i,
record_full_core_regbuf + MAX_REGISTER_SIZE * i);
record_full_open_1 (const char *name, int from_tty)
{
if (record_debug)
- fprintf_unfiltered (gdb_stdlog, "Process record: record_full_open\n");
+ fprintf_unfiltered (gdb_stdlog, "Process record: record_full_open_1\n");
/* check exec */
if (!target_has_execution)
record_full_init_record_breakpoints ();
- observer_notify_record_changed (current_inferior (), 1);
+ observer_notify_record_changed (current_inferior (), 1, "full", NULL);
}
/* "to_close" target method. Close the process record target. */
/* "to_async" target method. */
static void
-record_full_async (struct target_ops *ops,
- void (*callback) (enum inferior_event_type event_type,
- void *context),
- void *context)
+record_full_async (struct target_ops *ops, int enable)
{
- if (callback != NULL)
+ if (enable)
mark_async_event_handler (record_full_async_inferior_event_token);
else
clear_async_event_handler (record_full_async_inferior_event_token);
- ops->beneath->to_async (ops->beneath, callback, context);
+ ops->beneath->to_async (ops->beneath, enable);
}
static int record_full_resume_step = 0;
}
else
{
- /* This arch support soft sigle step. */
+ /* This arch supports soft single step. */
if (thread_has_single_step_breakpoints_set (inferior_thread ()))
{
/* This is a soft single step. */
record_full_resume_step = 1;
}
else
- {
- /* This is a continue.
- Try to insert a soft single step breakpoint. */
- if (!gdbarch_software_single_step (gdbarch,
- get_current_frame ()))
- {
- /* This system don't want use soft single step.
- Use hard sigle step. */
- step = 1;
- }
- }
+ step = !insert_single_step_breakpoints (gdbarch);
}
}
/* We are about to start executing the inferior (or simulate it),
let's register it with the event loop. */
if (target_can_async_p ())
- target_async (inferior_event_handler, 0);
+ target_async (1);
+}
+
+/* "to_commit_resume" method for process record target. */
+
+static void
+record_full_commit_resume (struct target_ops *ops)
+{
+ if (!RECORD_FULL_IS_REPLAY)
+ ops->beneath->to_commit_resume (ops->beneath);
}
static int record_full_get_sig = 0;
If insert success, set step to 0. */
set_executing (inferior_ptid, 0);
reinit_frame_cache ();
- if (gdbarch_software_single_step (gdbarch,
- get_current_frame ()))
- step = 0;
+
+ step = !insert_single_step_breakpoints (gdbarch);
+
set_executing (inferior_ptid, 1);
}
"target beneath\n");
ops->beneath->to_resume (ops->beneath, ptid, step,
GDB_SIGNAL_0);
+ ops->beneath->to_commit_resume (ops->beneath);
continue;
}
}
record_full_registers_change (struct regcache *regcache, int regnum)
{
/* Check record_full_insn_num. */
- record_full_check_insn_num (0);
+ record_full_check_insn_num ();
record_full_arch_list_head = NULL;
record_full_arch_list_tail = NULL;
}
/* Check record_full_insn_num */
- record_full_check_insn_num (0);
+ record_full_check_insn_num ();
/* Record registers change to list as an instruction. */
record_full_arch_list_head = NULL;
{
struct record_full_breakpoint *bp;
int in_target_beneath = 0;
+ int ix;
if (!RECORD_FULL_IS_REPLAY)
{
really need to install regular breakpoints in the inferior.
However, we do have to insert software single-step
breakpoints, in case the target can't hardware step. To keep
- things single, we always insert. */
+ things simple, we always insert. */
struct cleanup *old_cleanups;
int ret;
in_target_beneath = 1;
}
+ /* Use the existing entries if found in order to avoid duplication
+ in record_full_breakpoints. */
+
+ for (ix = 0;
+ VEC_iterate (record_full_breakpoint_p,
+ record_full_breakpoints, ix, bp);
+ ++ix)
+ {
+ if (bp->addr == bp_tgt->placed_address
+ && bp->address_space == bp_tgt->placed_address_space)
+ {
+ gdb_assert (bp->in_target_beneath == in_target_beneath);
+ return 0;
+ }
+ }
+
bp = XNEW (struct record_full_breakpoint);
bp->addr = bp_tgt->placed_address;
bp->address_space = bp_tgt->placed_address_space;
static int
record_full_remove_breakpoint (struct target_ops *ops,
struct gdbarch *gdbarch,
- struct bp_target_info *bp_tgt)
+ struct bp_target_info *bp_tgt,
+ enum remove_bp_reason reason)
{
struct record_full_breakpoint *bp;
int ix;
old_cleanups = record_full_gdb_operation_disable_set ();
ret = ops->beneath->to_remove_breakpoint (ops->beneath, gdbarch,
- bp_tgt);
+ bp_tgt, reason);
do_cleanups (old_cleanups);
if (ret != 0)
return ret;
}
- VEC_unordered_remove (record_full_breakpoint_p,
- record_full_breakpoints, ix);
+ if (reason == REMOVE_BREAKPOINT)
+ {
+ VEC_unordered_remove (record_full_breakpoint_p,
+ record_full_breakpoints, ix);
+ }
return 0;
}
}
return record_full_execution_dir;
}
+/* The to_record_method method of target record-full. */
+
+enum record_method
+record_full_record_method (struct target_ops *self, ptid_t ptid)
+{
+ return RECORD_METHOD_FULL;
+}
+
static void
record_full_info (struct target_ops *self)
{
/* The "to_record_is_replaying" target method. */
static int
-record_full_is_replaying (struct target_ops *self)
+record_full_is_replaying (struct target_ops *self, ptid_t ptid)
{
return RECORD_FULL_IS_REPLAY;
}
+/* The "to_record_will_replay" target method. */
+
+static int
+record_full_will_replay (struct target_ops *self, ptid_t ptid, int dir)
+{
+ /* We can currently only record when executing forwards. Should we be able
+ to record when executing backwards on targets that support reverse
+ execution, this needs to be changed. */
+
+ return RECORD_FULL_IS_REPLAY || dir == EXEC_REVERSE;
+}
+
/* Go to a specific entry. */
static void
registers_changed ();
reinit_frame_cache ();
+ stop_pc = regcache_read_pc (get_current_regcache ());
print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC, 1);
}
record_full_goto_entry (p);
}
+/* The "to_record_stop_replaying" target method. */
+
+static void
+record_full_stop_replaying (struct target_ops *self)
+{
+ record_full_goto_end (self);
+}
+
static void
init_record_full_ops (void)
{
record_full_ops.to_close = record_full_close;
record_full_ops.to_async = record_full_async;
record_full_ops.to_resume = record_full_resume;
+ record_full_ops.to_commit_resume = record_full_commit_resume;
record_full_ops.to_wait = record_full_wait;
record_full_ops.to_disconnect = record_disconnect;
record_full_ops.to_detach = record_detach;
record_full_ops.to_get_bookmark = record_full_get_bookmark;
record_full_ops.to_goto_bookmark = record_full_goto_bookmark;
record_full_ops.to_execution_direction = record_full_execution_direction;
+ record_full_ops.to_record_method = record_full_record_method;
record_full_ops.to_info_record = record_full_info;
record_full_ops.to_save_record = record_full_save;
record_full_ops.to_delete_record = record_full_delete;
record_full_ops.to_record_is_replaying = record_full_is_replaying;
+ record_full_ops.to_record_will_replay = record_full_will_replay;
+ record_full_ops.to_record_stop_replaying = record_full_stop_replaying;
record_full_ops.to_goto_record_begin = record_full_goto_begin;
record_full_ops.to_goto_record_end = record_full_goto_end;
record_full_ops.to_goto_record = record_full_goto;
/* We are about to start executing the inferior (or simulate it),
let's register it with the event loop. */
if (target_can_async_p ())
- target_async (inferior_event_handler, 0);
+ target_async (1);
}
/* "to_kill" method for prec over corefile. */
if (!entry)
{
/* Add a new entry. */
- entry = (struct record_full_core_buf_entry *)
- xmalloc
- (sizeof (struct record_full_core_buf_entry));
+ entry = XNEW (struct record_full_core_buf_entry);
entry->p = p;
if (!bfd_malloc_and_get_section
(p->the_bfd_section->owner,
static int
record_full_core_remove_breakpoint (struct target_ops *ops,
struct gdbarch *gdbarch,
- struct bp_target_info *bp_tgt)
+ struct bp_target_info *bp_tgt,
+ enum remove_bp_reason reason)
{
return 0;
}
record_full_core_ops.to_goto_bookmark = record_full_goto_bookmark;
record_full_core_ops.to_execution_direction
= record_full_execution_direction;
+ record_full_core_ops.to_record_method = record_full_record_method;
record_full_core_ops.to_info_record = record_full_info;
record_full_core_ops.to_delete_record = record_full_delete;
record_full_core_ops.to_record_is_replaying = record_full_is_replaying;
+ record_full_core_ops.to_record_will_replay = record_full_will_replay;
record_full_core_ops.to_goto_record_begin = record_full_goto_begin;
record_full_core_ops.to_goto_record_end = record_full_goto_end;
record_full_core_ops.to_goto_record = record_full_goto;
bfdcore_read (core_bfd, osec, &signal,
sizeof (signal), &bfd_offset);
signal = netorder32 (signal);
- rec->u.end.sigval = signal;
+ rec->u.end.sigval = (enum gdb_signal) signal;
/* Get insn count. */
bfdcore_read (core_bfd, osec, &count,
record_full_open (args, from_tty);
}
-static void
-record_full_save_cleanups (void *data)
-{
- bfd *obfd = data;
- char *pathname = xstrdup (bfd_get_filename (obfd));
-
- gdb_bfd_unref (obfd);
- unlink (pathname);
- xfree (pathname);
-}
-
/* Save the execution log to a file. We use a modified elf corefile
format, with an extra section for our data. */
uint32_t magic;
struct regcache *regcache;
struct gdbarch *gdbarch;
- struct cleanup *old_cleanups;
struct cleanup *set_cleanups;
- bfd *obfd;
int save_size = 0;
asection *osec = NULL;
int bfd_offset = 0;
recfilename);
/* Open the output file. */
- obfd = create_gcore_bfd (recfilename);
- old_cleanups = make_cleanup (record_full_save_cleanups, obfd);
+ gdb_bfd_ref_ptr obfd (create_gcore_bfd (recfilename));
+
+ /* Arrange to remove the output file on failure. */
+ gdb::unlinker unlink_file (recfilename);
/* Save the current record entry to "cur_record_full_list". */
cur_record_full_list = record_full_list;
}
/* Make the new bfd section. */
- osec = bfd_make_section_anyway_with_flags (obfd, "precord",
+ osec = bfd_make_section_anyway_with_flags (obfd.get (), "precord",
SEC_HAS_CONTENTS
| SEC_READONLY);
if (osec == NULL)
error (_("Failed to create 'precord' section for corefile %s: %s"),
recfilename,
bfd_errmsg (bfd_get_error ()));
- bfd_set_section_size (obfd, osec, save_size);
- bfd_set_section_vma (obfd, osec, 0);
- bfd_set_section_alignment (obfd, osec, 0);
- bfd_section_lma (obfd, osec) = 0;
+ bfd_set_section_size (obfd.get (), osec, save_size);
+ bfd_set_section_vma (obfd.get (), osec, 0);
+ bfd_set_section_alignment (obfd.get (), osec, 0);
+ bfd_section_lma (obfd.get (), osec) = 0;
/* Save corefile state. */
- write_gcore_file (obfd);
+ write_gcore_file (obfd.get ());
/* Write out the record log. */
/* Write the magic code. */
" Writing 4-byte magic cookie "
"RECORD_FULL_FILE_MAGIC (0x%s)\n",
phex_nz (magic, 4));
- bfdcore_write (obfd, osec, &magic, sizeof (magic), &bfd_offset);
+ bfdcore_write (obfd.get (), osec, &magic, sizeof (magic), &bfd_offset);
/* Save the entries to recfd and forward execute to the end of
record list. */
uint64_t addr;
type = record_full_list->type;
- bfdcore_write (obfd, osec, &type, sizeof (type), &bfd_offset);
+ bfdcore_write (obfd.get (), osec, &type, sizeof (type), &bfd_offset);
switch (record_full_list->type)
{
/* Write regnum. */
regnum = netorder32 (record_full_list->u.reg.num);
- bfdcore_write (obfd, osec, ®num,
+ bfdcore_write (obfd.get (), osec, ®num,
sizeof (regnum), &bfd_offset);
/* Write regval. */
- bfdcore_write (obfd, osec,
+ bfdcore_write (obfd.get (), osec,
record_full_get_loc (record_full_list),
record_full_list->u.reg.len, &bfd_offset);
break;
/* Write memlen. */
len = netorder32 (record_full_list->u.mem.len);
- bfdcore_write (obfd, osec, &len, sizeof (len), &bfd_offset);
+ bfdcore_write (obfd.get (), osec, &len, sizeof (len),
+ &bfd_offset);
/* Write memaddr. */
addr = netorder64 (record_full_list->u.mem.addr);
- bfdcore_write (obfd, osec, &addr,
+ bfdcore_write (obfd.get (), osec, &addr,
sizeof (addr), &bfd_offset);
/* Write memval. */
- bfdcore_write (obfd, osec,
+ bfdcore_write (obfd.get (), osec,
record_full_get_loc (record_full_list),
record_full_list->u.mem.len, &bfd_offset);
break;
(unsigned long) sizeof (count));
/* Write signal value. */
signal = netorder32 (record_full_list->u.end.sigval);
- bfdcore_write (obfd, osec, &signal,
+ bfdcore_write (obfd.get (), osec, &signal,
sizeof (signal), &bfd_offset);
/* Write insn count. */
count = netorder32 (record_full_list->u.end.insn_num);
- bfdcore_write (obfd, osec, &count,
+ bfdcore_write (obfd.get (), osec, &count,
sizeof (count), &bfd_offset);
break;
}
}
do_cleanups (set_cleanups);
- gdb_bfd_unref (obfd);
- discard_cleanups (old_cleanups);
+ unlink_file.keep ();
/* Succeeded. */
printf_filtered (_("Saved core file %s with execution log.\n"),