X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gdb%2Frecord.c;h=08ffb687e2e0502900e3cba2c12036d198e60f6f;hb=bcef180ccbeb8a6a9301065ede1fe038c3040fa5;hp=9d24f0130da395bd0361e044bdd4be3e00775361;hpb=ec6dbf37039f379951bcd79d7ddc9e9bd6708524;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/record.c b/gdb/record.c index 9d24f0130d..08ffb687e2 100644 --- a/gdb/record.c +++ b/gdb/record.c @@ -1,6 +1,6 @@ /* Process record and replay target for GDB, the GNU debugger. - Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc. + Copyright (C) 2008-2012 Free Software Foundation, Inc. This file is part of GDB. @@ -30,6 +30,10 @@ #include "record.h" #include "elf-bfd.h" #include "gcore.h" +#include "event-loop.h" +#include "inf-loop.h" +#include "gdb_bfd.h" +#include "observer.h" #include @@ -98,7 +102,7 @@ struct record_reg_entry struct record_end_entry { - enum target_signal sigval; + enum gdb_signal sigval; ULONGEST insn_num; }; @@ -150,7 +154,11 @@ struct record_entry }; /* This is the debug switch for process record. */ -int record_debug = 0; +unsigned int record_debug = 0; + +/* If true, query if PREC cannot record memory + change of next instruction. */ +int record_memory_query = 0; struct record_core_buf_entry { @@ -203,7 +211,7 @@ static struct target_ops record_core_ops; /* The beneath function pointers. */ static struct target_ops *record_beneath_to_resume_ops; static void (*record_beneath_to_resume) (struct target_ops *, ptid_t, int, - enum target_signal); + enum gdb_signal); static struct target_ops *record_beneath_to_wait_ops; static ptid_t (*record_beneath_to_wait) (struct target_ops *, ptid_t, struct target_waitstatus *, @@ -227,6 +235,7 @@ static int (*record_beneath_to_remove_breakpoint) (struct gdbarch *, static int (*record_beneath_to_stopped_by_watchpoint) (void); static int (*record_beneath_to_stopped_data_address) (struct target_ops *, CORE_ADDR *); +static void (*record_beneath_to_async) (void (*) (enum inferior_event_type, void *), void *); /* Alloc and free functions for record_reg, record_mem, and record_end entries. */ @@ -450,7 +459,7 @@ record_get_loc (struct record_entry *rec) return rec->u.reg.u.buf; case record_end: default: - gdb_assert (0); + gdb_assert_not_reached ("unexpected record_entry type"); return NULL; } } @@ -477,6 +486,20 @@ record_arch_list_add_reg (struct regcache *regcache, int regnum) return 0; } +int +record_read_memory (struct gdbarch *gdbarch, + CORE_ADDR memaddr, gdb_byte *myaddr, + ssize_t len) +{ + int ret = target_read_memory (memaddr, myaddr, len); + + if (ret && record_debug) + printf_unfiltered (_("Process record: error reading memory " + "at addr %s len = %ld.\n"), + paddress (gdbarch, memaddr), (long) len); + return ret; +} + /* Record the value of a region of memory whose address is ADDR and length is LEN to record_arch_list. */ @@ -489,20 +512,15 @@ record_arch_list_add_mem (CORE_ADDR addr, int len) fprintf_unfiltered (gdb_stdlog, "Process record: add mem addr = %s len = %d to " "record list.\n", - paddress (target_gdbarch, addr), len); + paddress (target_gdbarch (), addr), len); - if (!addr) /* FIXME: Why? Some arch must permit it... */ + if (!addr) /* FIXME: Why? Some arch must permit it... */ return 0; rec = record_mem_alloc (addr, len); - if (target_read_memory (addr, record_get_loc (rec), len)) + if (record_read_memory (target_gdbarch (), addr, record_get_loc (rec), len)) { - if (record_debug) - fprintf_unfiltered (gdb_stdlog, - "Process record: error reading memory at " - "addr = %s len = %d.\n", - paddress (target_gdbarch, addr), len); record_mem_release (rec); return -1; } @@ -524,7 +542,7 @@ record_arch_list_add_end (void) "Process record: add end to arch list.\n"); rec = record_end_alloc (); - rec->u.end.sigval = TARGET_SIGNAL_0; + rec->u.end.sigval = GDB_SIGNAL_0; rec->u.end.insn_num = ++record_insn_count; record_arch_list_add (rec); @@ -544,6 +562,7 @@ record_check_insn_num (int set_terminal) if (record_stop_at_limit) { int q; + if (set_terminal) target_terminal_ours (); q = yquery (_("Do you want to auto delete previous execution " @@ -573,7 +592,7 @@ record_arch_list_cleanups (void *ignore) record_arch_list, and add it to record_list. */ static int -record_message (struct regcache *regcache, enum target_signal signal) +record_message (struct regcache *regcache, enum gdb_signal signal) { int ret; struct gdbarch *gdbarch = get_regcache_arch (regcache); @@ -613,7 +632,7 @@ record_message (struct regcache *regcache, enum target_signal signal) record_list->u.end.sigval = signal; } - if (signal == TARGET_SIGNAL_0 + if (signal == GDB_SIGNAL_0 || !gdbarch_process_record_signal_p (gdbarch)) ret = gdbarch_process_record (gdbarch, regcache, @@ -644,7 +663,7 @@ record_message (struct regcache *regcache, enum target_signal signal) struct record_message_args { struct regcache *regcache; - enum target_signal signal; + enum gdb_signal signal; }; static int @@ -657,7 +676,7 @@ record_message_wrapper (void *args) static int record_message_wrapper_safe (struct regcache *regcache, - enum target_signal signal) + enum gdb_signal signal) { struct record_message_args args; @@ -730,15 +749,9 @@ record_exec_insn (struct regcache *regcache, struct gdbarch *gdbarch, paddress (gdbarch, entry->u.mem.addr), entry->u.mem.len); - if (target_read_memory (entry->u.mem.addr, mem, entry->u.mem.len)) - { - entry->u.mem.mem_entry_not_accessible = 1; - if (record_debug) - warning ("Process record: error reading memory at " - "addr = %s len = %d.", - paddress (gdbarch, entry->u.mem.addr), - entry->u.mem.len); - } + if (record_read_memory (gdbarch, + entry->u.mem.addr, mem, entry->u.mem.len)) + entry->u.mem.mem_entry_not_accessible = 1; else { if (target_write_memory (entry->u.mem.addr, @@ -747,8 +760,8 @@ record_exec_insn (struct regcache *regcache, struct gdbarch *gdbarch, { entry->u.mem.mem_entry_not_accessible = 1; if (record_debug) - warning ("Process record: error writing memory at " - "addr = %s len = %d.", + warning (_("Process record: error writing memory at " + "addr = %s len = %d."), paddress (gdbarch, entry->u.mem.addr), entry->u.mem.len); } @@ -778,7 +791,7 @@ record_exec_insn (struct regcache *regcache, struct gdbarch *gdbarch, static struct target_ops *tmp_to_resume_ops; static void (*tmp_to_resume) (struct target_ops *, ptid_t, int, - enum target_signal); + enum gdb_signal); static struct target_ops *tmp_to_wait_ops; static ptid_t (*tmp_to_wait) (struct target_ops *, ptid_t, struct target_waitstatus *, @@ -801,9 +814,22 @@ static int (*tmp_to_remove_breakpoint) (struct gdbarch *, struct bp_target_info *); static int (*tmp_to_stopped_by_watchpoint) (void); static int (*tmp_to_stopped_data_address) (struct target_ops *, CORE_ADDR *); +static int (*tmp_to_stopped_data_address) (struct target_ops *, CORE_ADDR *); +static void (*tmp_to_async) (void (*) (enum inferior_event_type, void *), void *); static void record_restore (void); +/* Asynchronous signal handle registered as event loop source for when + we have pending events ready to be passed to the core. */ + +static struct async_event_handler *record_async_inferior_event_token; + +static void +record_async_inferior_event_handler (gdb_client_data data) +{ + inferior_event_handler (INF_REG_EVENT, NULL); +} + /* Open the process record target. */ static void @@ -838,8 +864,6 @@ record_core_open_1 (char *name, int from_tty) static void record_open_1 (char *name, int from_tty) { - struct target_ops *t; - if (record_debug) fprintf_unfiltered (gdb_stdlog, "Process record: record_open\n"); @@ -849,11 +873,8 @@ record_open_1 (char *name, int from_tty) if (non_stop) error (_("Process record target can't debug inferior in non-stop mode " "(non-stop).")); - if (target_async_permitted) - error (_("Process record target can't debug inferior in asynchronous " - "mode (target-async).")); - if (!gdbarch_process_record_p (target_gdbarch)) + if (!gdbarch_process_record_p (target_gdbarch ())) error (_("Process record: the current architecture doesn't support " "record function.")); @@ -862,15 +883,26 @@ record_open_1 (char *name, int from_tty) if (!tmp_to_wait) error (_("Could not find 'to_wait' method on the target stack.")); if (!tmp_to_store_registers) - error (_("Could not find 'to_store_registers' method on the target stack.")); + error (_("Could not find 'to_store_registers' " + "method on the target stack.")); if (!tmp_to_insert_breakpoint) - error (_("Could not find 'to_insert_breakpoint' method on the target stack.")); + error (_("Could not find 'to_insert_breakpoint' " + "method on the target stack.")); if (!tmp_to_remove_breakpoint) - error (_("Could not find 'to_remove_breakpoint' method on the target stack.")); + error (_("Could not find 'to_remove_breakpoint' " + "method on the target stack.")); + if (!tmp_to_stopped_by_watchpoint) + error (_("Could not find 'to_stopped_by_watchpoint' " + "method on the target stack.")); + if (!tmp_to_stopped_data_address) + error (_("Could not find 'to_stopped_data_address' " + "method on the target stack.")); push_target (&record_ops); } +static void record_init_record_breakpoints (void); + /* "to_open" target method. Open the process record target. */ static void @@ -897,6 +929,9 @@ record_open (char *name, int from_tty) tmp_to_xfer_partial = NULL; tmp_to_insert_breakpoint = NULL; tmp_to_remove_breakpoint = NULL; + tmp_to_stopped_by_watchpoint = NULL; + tmp_to_stopped_data_address = NULL; + tmp_to_async = NULL; /* Set the beneath function pointers. */ for (t = current_target.beneath; t != NULL; t = t->beneath) @@ -929,6 +964,8 @@ record_open (char *name, int from_tty) tmp_to_stopped_by_watchpoint = t->to_stopped_by_watchpoint; if (!tmp_to_stopped_data_address) tmp_to_stopped_data_address = t->to_stopped_data_address; + if (!tmp_to_async) + tmp_to_async = t->to_async; } if (!tmp_to_xfer_partial) error (_("Could not find 'to_xfer_partial' method on the target stack.")); @@ -952,11 +989,21 @@ record_open (char *name, int from_tty) record_beneath_to_remove_breakpoint = tmp_to_remove_breakpoint; record_beneath_to_stopped_by_watchpoint = tmp_to_stopped_by_watchpoint; record_beneath_to_stopped_data_address = tmp_to_stopped_data_address; + record_beneath_to_async = tmp_to_async; - if (current_target.to_stratum == core_stratum) + if (core_bfd) record_core_open_1 (name, from_tty); else record_open_1 (name, from_tty); + + /* Register extra event sources in the event loop. */ + record_async_inferior_event_token + = create_async_event_handler (record_async_inferior_event_handler, + NULL); + + record_init_record_breakpoints (); + + observer_notify_record_changed (current_inferior (), 1); } /* "to_close" target method. Close the process record target. */ @@ -988,23 +1035,96 @@ record_close (int quitting) } record_core_buf_list = NULL; } + + if (record_async_inferior_event_token) + delete_async_event_handler (&record_async_inferior_event_token); } static int record_resume_step = 0; +/* True if we've been resumed, and so each record_wait call should + advance execution. If this is false, record_wait will return a + TARGET_WAITKIND_IGNORE. */ +static int record_resumed = 0; + +/* The execution direction of the last resume we got. This is + necessary for async mode. Vis (order is not strictly accurate): + + 1. user has the global execution direction set to forward + 2. user does a reverse-step command + 3. record_resume is called with global execution direction + temporarily switched to reverse + 4. GDB's execution direction is reverted back to forward + 5. target record notifies event loop there's an event to handle + 6. infrun asks the target which direction was it going, and switches + the global execution direction accordingly (to reverse) + 7. infrun polls an event out of the record target, and handles it + 8. GDB goes back to the event loop, and goto #4. +*/ +static enum exec_direction_kind record_execution_dir = EXEC_FORWARD; + /* "to_resume" target method. Resume the process record target. */ static void record_resume (struct target_ops *ops, ptid_t ptid, int step, - enum target_signal signal) + enum gdb_signal signal) { record_resume_step = step; + record_resumed = 1; + record_execution_dir = execution_direction; if (!RECORD_IS_REPLAY) { + struct gdbarch *gdbarch = target_thread_architecture (ptid); + record_message (get_current_regcache (), signal); - record_beneath_to_resume (record_beneath_to_resume_ops, ptid, 1, - signal); + + if (!step) + { + /* This is not hard single step. */ + if (!gdbarch_software_single_step_p (gdbarch)) + { + /* This is a normal continue. */ + step = 1; + } + else + { + /* This arch support soft sigle step. */ + if (single_step_breakpoints_inserted ()) + { + /* This is a soft single step. */ + record_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; + } + } + } + } + + /* Make sure the target beneath reports all signals. */ + target_pass_signals (0, NULL); + + record_beneath_to_resume (record_beneath_to_resume_ops, + ptid, step, signal); + } + + /* 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); + /* Notify the event loop there's an event to wait for. We do + most of the work in record_wait. */ + mark_async_event_handler (record_async_inferior_event_token); } } @@ -1052,17 +1172,30 @@ record_wait_cleanups (void *ignore) where to stop. */ static ptid_t -record_wait (struct target_ops *ops, - ptid_t ptid, struct target_waitstatus *status, - int options) +record_wait_1 (struct target_ops *ops, + ptid_t ptid, struct target_waitstatus *status, + int options) { struct cleanup *set_cleanups = record_gdb_operation_disable_set (); if (record_debug) fprintf_unfiltered (gdb_stdlog, "Process record: record_wait " - "record_resume_step = %d\n", - record_resume_step); + "record_resume_step = %d, record_resumed = %d, direction=%s\n", + record_resume_step, record_resumed, + record_execution_dir == EXEC_FORWARD ? "forward" : "reverse"); + + if (!record_resumed) + { + gdb_assert ((options & TARGET_WNOHANG) != 0); + + /* No interesting event. */ + status->kind = TARGET_WAITKIND_IGNORE; + return minus_one_ptid; + } + + record_get_sig = 0; + signal (SIGINT, record_sig_handler); if (!RECORD_IS_REPLAY && ops != &record_core_ops) { @@ -1077,15 +1210,30 @@ record_wait (struct target_ops *ops, /* This is not a single step. */ ptid_t ret; CORE_ADDR tmp_pc; + struct gdbarch *gdbarch = target_thread_architecture (inferior_ptid); while (1) { ret = record_beneath_to_wait (record_beneath_to_wait_ops, ptid, status, options); + if (status->kind == TARGET_WAITKIND_IGNORE) + { + if (record_debug) + fprintf_unfiltered (gdb_stdlog, + "Process record: record_wait " + "target beneath not done yet\n"); + return ret; + } + + if (single_step_breakpoints_inserted ()) + remove_single_step_breakpoints (); + + if (record_resume_step) + return ret; /* Is this a SIGTRAP? */ if (status->kind == TARGET_WAITKIND_STOPPED - && status->value.sig == TARGET_SIGNAL_TRAP) + && status->value.sig == GDB_SIGNAL_TRAP) { struct regcache *regcache; struct address_space *aspace; @@ -1109,7 +1257,8 @@ record_wait (struct target_ops *ops, handle it. */ if (software_breakpoint_inserted_here_p (aspace, tmp_pc)) { - struct gdbarch *gdbarch = get_regcache_arch (regcache); + struct gdbarch *gdbarch + = get_regcache_arch (regcache); CORE_ADDR decr_pc_after_break = gdbarch_decr_pc_after_break (gdbarch); if (decr_pc_after_break) @@ -1119,19 +1268,39 @@ record_wait (struct target_ops *ops, } else { - /* This must be a single-step trap. Record the - insn and issue another step. */ + /* This is a single-step trap. Record the + insn and issue another step. + FIXME: this part can be a random SIGTRAP too. + But GDB cannot handle it. */ + int step = 1; + if (!record_message_wrapper_safe (regcache, - TARGET_SIGNAL_0)) + GDB_SIGNAL_0)) { status->kind = TARGET_WAITKIND_STOPPED; - status->value.sig = TARGET_SIGNAL_0; + status->value.sig = GDB_SIGNAL_0; break; } + if (gdbarch_software_single_step_p (gdbarch)) + { + /* Try to insert the software single step breakpoint. + 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; + set_executing (inferior_ptid, 1); + } + + if (record_debug) + fprintf_unfiltered (gdb_stdlog, + "Process record: record_wait " + "issuing one more step in the target beneath\n"); record_beneath_to_resume (record_beneath_to_resume_ops, - ptid, 1, - TARGET_SIGNAL_0); + ptid, step, + GDB_SIGNAL_0); continue; } } @@ -1178,8 +1347,6 @@ record_wait (struct target_ops *ops, } } - record_get_sig = 0; - signal (SIGINT, record_sig_handler); /* If GDB is in terminal_inferior mode, it will not get the signal. And in GDB replay mode, GDB doesn't need to be in terminal_inferior mode, because inferior will not executed. @@ -1266,12 +1433,13 @@ record_wait (struct target_ops *ops, if (record_hw_watchpoint) { if (record_debug) - fprintf_unfiltered (gdb_stdlog, "\ -Process record: hit hw watchpoint.\n"); + fprintf_unfiltered (gdb_stdlog, + "Process record: hit hw " + "watchpoint.\n"); continue_flag = 0; } /* Check target signal */ - if (record_list->u.end.sigval != TARGET_SIGNAL_0) + if (record_list->u.end.sigval != GDB_SIGNAL_0) /* FIXME: better way to check */ continue_flag = 0; } @@ -1293,24 +1461,42 @@ Process record: hit hw watchpoint.\n"); } while (continue_flag); - signal (SIGINT, handle_sigint); - replay_out: if (record_get_sig) - status->value.sig = TARGET_SIGNAL_INT; - else if (record_list->u.end.sigval != TARGET_SIGNAL_0) + status->value.sig = GDB_SIGNAL_INT; + else if (record_list->u.end.sigval != GDB_SIGNAL_0) /* FIXME: better way to check */ status->value.sig = record_list->u.end.sigval; else - status->value.sig = TARGET_SIGNAL_TRAP; + status->value.sig = GDB_SIGNAL_TRAP; discard_cleanups (old_cleanups); } + signal (SIGINT, handle_sigint); + do_cleanups (set_cleanups); return inferior_ptid; } +static ptid_t +record_wait (struct target_ops *ops, + ptid_t ptid, struct target_waitstatus *status, + int options) +{ + ptid_t return_ptid; + + return_ptid = record_wait_1 (ops, ptid, status, options); + if (status->kind != TARGET_WAITKIND_IGNORE) + { + /* We're reporting a stop. Make sure any spurious + target_wait(WNOHANG) doesn't advance the target until the + core wants us resumed again. */ + record_resumed = 0; + } + return return_ptid; +} + static int record_stopped_by_watchpoint (void) { @@ -1392,6 +1578,7 @@ record_registers_change (struct regcache *regcache, int regnum) if (regnum < 0) { int i; + for (i = 0; i < gdbarch_num_regs (get_regcache_arch (regcache)); i++) { if (record_arch_list_add_reg (regcache, i)) @@ -1458,6 +1645,7 @@ record_store_registers (struct target_ops *ops, struct regcache *regcache, if (regno < 0) { int i; + for (i = 0; i < gdbarch_num_regs (get_regcache_arch (regcache)); i++) @@ -1498,7 +1686,7 @@ record_xfer_partial (struct target_ops *ops, enum target_object object, if (!query (_("Because GDB is in replay mode, writing to memory " "will make the execution log unusable from this " "point onward. Write memory at address %s?"), - paddress (target_gdbarch, offset))) + paddress (target_gdbarch (), offset))) error (_("Process record canceled the operation.")); /* Destroy the record from here forward. */ @@ -1544,24 +1732,97 @@ record_xfer_partial (struct target_ops *ops, enum target_object object, offset, len); } -/* Behavior is conditional on RECORD_IS_REPLAY. - We will not actually insert or remove breakpoints when replaying, - nor when recording. */ +/* This structure represents a breakpoint inserted while the record + target is active. We use this to know when to install/remove + breakpoints in/from the target beneath. For example, a breakpoint + may be inserted while recording, but removed when not replaying nor + recording. In that case, the breakpoint had not been inserted on + the target beneath, so we should not try to remove it there. */ + +struct record_breakpoint +{ + /* The address and address space the breakpoint was set at. */ + struct address_space *address_space; + CORE_ADDR addr; + + /* True when the breakpoint has been also installed in the target + beneath. This will be false for breakpoints set during replay or + when recording. */ + int in_target_beneath; +}; + +typedef struct record_breakpoint *record_breakpoint_p; +DEF_VEC_P(record_breakpoint_p); + +/* The list of breakpoints inserted while the record target is + active. */ +VEC(record_breakpoint_p) *record_breakpoints = NULL; + +static void +record_sync_record_breakpoints (struct bp_location *loc, void *data) +{ + if (loc->loc_type != bp_loc_software_breakpoint) + return; + + if (loc->inserted) + { + struct record_breakpoint *bp = XNEW (struct record_breakpoint); + + bp->addr = loc->target_info.placed_address; + bp->address_space = loc->target_info.placed_address_space; + + bp->in_target_beneath = 1; + + VEC_safe_push (record_breakpoint_p, record_breakpoints, bp); + } +} + +/* Sync existing breakpoints to record_breakpoints. */ + +static void +record_init_record_breakpoints (void) +{ + VEC_free (record_breakpoint_p, record_breakpoints); + + iterate_over_bp_locations (record_sync_record_breakpoints); +} + +/* Behavior is conditional on RECORD_IS_REPLAY. We will not actually + insert or remove breakpoints in the real target when replaying, nor + when recording. */ static int record_insert_breakpoint (struct gdbarch *gdbarch, struct bp_target_info *bp_tgt) { + struct record_breakpoint *bp; + int in_target_beneath = 0; + if (!RECORD_IS_REPLAY) { - struct cleanup *old_cleanups = record_gdb_operation_disable_set (); - int ret = record_beneath_to_insert_breakpoint (gdbarch, bp_tgt); + /* When recording, we currently always single-step, so we don't + 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. */ + struct cleanup *old_cleanups; + int ret; + old_cleanups = record_gdb_operation_disable_set (); + ret = record_beneath_to_insert_breakpoint (gdbarch, bp_tgt); do_cleanups (old_cleanups); - return ret; + if (ret != 0) + return ret; + + in_target_beneath = 1; } + bp = XNEW (struct record_breakpoint); + bp->addr = bp_tgt->placed_address; + bp->address_space = bp_tgt->placed_address_space; + bp->in_target_beneath = in_target_beneath; + VEC_safe_push (record_breakpoint_p, record_breakpoints, bp); return 0; } @@ -1571,17 +1832,35 @@ static int record_remove_breakpoint (struct gdbarch *gdbarch, struct bp_target_info *bp_tgt) { - if (!RECORD_IS_REPLAY) + struct record_breakpoint *bp; + int ix; + + for (ix = 0; + VEC_iterate (record_breakpoint_p, record_breakpoints, ix, bp); + ++ix) { - struct cleanup *old_cleanups = record_gdb_operation_disable_set (); - int ret = record_beneath_to_remove_breakpoint (gdbarch, bp_tgt); + if (bp->addr == bp_tgt->placed_address + && bp->address_space == bp_tgt->placed_address_space) + { + if (bp->in_target_beneath) + { + struct cleanup *old_cleanups; + int ret; - do_cleanups (old_cleanups); + old_cleanups = record_gdb_operation_disable_set (); + ret = record_beneath_to_remove_breakpoint (gdbarch, bp_tgt); + do_cleanups (old_cleanups); + + if (ret != 0) + return ret; + } - return ret; + VEC_unordered_remove (record_breakpoint_p, record_breakpoints, ix); + return 0; + } } - return 0; + gdb_assert_not_reached ("removing unknown breakpoint"); } /* "to_can_execute_reverse" method for process record target. */ @@ -1643,6 +1922,37 @@ record_goto_bookmark (gdb_byte *bookmark, int from_tty) return; } +static void +record_async (void (*callback) (enum inferior_event_type event_type, + void *context), void *context) +{ + /* If we're on top of a line target (e.g., linux-nat, remote), then + set it to async mode as well. Will be NULL if we're sitting on + top of the core target, for "record restore". */ + if (record_beneath_to_async != NULL) + record_beneath_to_async (callback, context); +} + +static int +record_can_async_p (void) +{ + /* We only enable async when the user specifically asks for it. */ + return target_async_permitted; +} + +static int +record_is_async_p (void) +{ + /* We only enable async when the user specifically asks for it. */ + return target_async_permitted; +} + +static enum exec_direction_kind +record_execution_direction (void) +{ + return record_execution_dir; +} + static void init_record_ops (void) { @@ -1670,6 +1980,10 @@ init_record_ops (void) /* Add bookmark target methods. */ record_ops.to_get_bookmark = record_get_bookmark; record_ops.to_goto_bookmark = record_goto_bookmark; + record_ops.to_async = record_async; + record_ops.to_can_async_p = record_can_async_p; + record_ops.to_is_async_p = record_is_async_p; + record_ops.to_execution_direction = record_execution_direction; record_ops.to_magic = OPS_MAGIC; } @@ -1677,9 +1991,21 @@ init_record_ops (void) static void record_core_resume (struct target_ops *ops, ptid_t ptid, int step, - enum target_signal signal) + enum gdb_signal signal) { record_resume_step = step; + record_resumed = 1; + record_execution_dir = execution_direction; + + /* 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); + + /* Notify the event loop there's an event to wait for. */ + mark_async_event_handler (record_async_inferior_event_token); + } } /* "to_kill" method for prec over corefile. */ @@ -1743,85 +2069,84 @@ record_core_xfer_partial (struct target_ops *ops, enum target_object object, const gdb_byte *writebuf, ULONGEST offset, LONGEST len) { - if (object == TARGET_OBJECT_MEMORY) - { - if (record_gdb_operation_disable || !writebuf) - { - struct target_section *p; - for (p = record_core_start; p < record_core_end; p++) - { - if (offset >= p->addr) - { - struct record_core_buf_entry *entry; - ULONGEST sec_offset; - - if (offset >= p->endaddr) - continue; - - if (offset + len > p->endaddr) - len = p->endaddr - offset; - - sec_offset = offset - p->addr; - - /* Read readbuf or write writebuf p, offset, len. */ - /* Check flags. */ - if (p->the_bfd_section->flags & SEC_CONSTRUCTOR - || (p->the_bfd_section->flags & SEC_HAS_CONTENTS) == 0) - { - if (readbuf) - memset (readbuf, 0, len); - return len; - } - /* Get record_core_buf_entry. */ - for (entry = record_core_buf_list; entry; - entry = entry->prev) - if (entry->p == p) - break; - if (writebuf) - { - if (!entry) - { - /* Add a new entry. */ - entry - = (struct record_core_buf_entry *) - xmalloc - (sizeof (struct record_core_buf_entry)); - entry->p = p; - if (!bfd_malloc_and_get_section (p->bfd, - p->the_bfd_section, - &entry->buf)) - { - xfree (entry); - return 0; - } - entry->prev = record_core_buf_list; - record_core_buf_list = entry; - } - - memcpy (entry->buf + sec_offset, writebuf, - (size_t) len); - } - else - { - if (!entry) - return record_beneath_to_xfer_partial - (record_beneath_to_xfer_partial_ops, - object, annex, readbuf, writebuf, - offset, len); - - memcpy (readbuf, entry->buf + sec_offset, - (size_t) len); - } - - return len; - } - } - - return -1; - } - else - error (_("You can't do that without a process to debug.")); - } + if (object == TARGET_OBJECT_MEMORY) + { + if (record_gdb_operation_disable || !writebuf) + { + struct target_section *p; + + for (p = record_core_start; p < record_core_end; p++) + { + if (offset >= p->addr) + { + struct record_core_buf_entry *entry; + ULONGEST sec_offset; + + if (offset >= p->endaddr) + continue; + + if (offset + len > p->endaddr) + len = p->endaddr - offset; + + sec_offset = offset - p->addr; + + /* Read readbuf or write writebuf p, offset, len. */ + /* Check flags. */ + if (p->the_bfd_section->flags & SEC_CONSTRUCTOR + || (p->the_bfd_section->flags & SEC_HAS_CONTENTS) == 0) + { + if (readbuf) + memset (readbuf, 0, len); + return len; + } + /* Get record_core_buf_entry. */ + for (entry = record_core_buf_list; entry; + entry = entry->prev) + if (entry->p == p) + break; + if (writebuf) + { + if (!entry) + { + /* Add a new entry. */ + entry = (struct record_core_buf_entry *) + xmalloc (sizeof (struct record_core_buf_entry)); + entry->p = p; + if (!bfd_malloc_and_get_section (p->bfd, + p->the_bfd_section, + &entry->buf)) + { + xfree (entry); + return 0; + } + entry->prev = record_core_buf_list; + record_core_buf_list = entry; + } + + memcpy (entry->buf + sec_offset, writebuf, + (size_t) len); + } + else + { + if (!entry) + return record_beneath_to_xfer_partial + (record_beneath_to_xfer_partial_ops, + object, annex, readbuf, writebuf, + offset, len); + + memcpy (readbuf, entry->buf + sec_offset, + (size_t) len); + } + + return len; + } + } + + return -1; + } + else + error (_("You can't do that without a process to debug.")); + } return record_beneath_to_xfer_partial (record_beneath_to_xfer_partial_ops, object, annex, readbuf, writebuf, @@ -1848,8 +2173,8 @@ record_core_remove_breakpoint (struct gdbarch *gdbarch, /* "to_has_execution" method for prec over corefile. */ -int -record_core_has_execution (struct target_ops *ops) +static int +record_core_has_execution (struct target_ops *ops, ptid_t the_ptid) { return 1; } @@ -1857,7 +2182,7 @@ record_core_has_execution (struct target_ops *ops) static void init_record_core_ops (void) { - record_core_ops.to_shortname = "record_core"; + record_core_ops.to_shortname = "record-core"; record_core_ops.to_longname = "Process record and replay target"; record_core_ops.to_doc = "Log program while executing and replay execution from log."; @@ -1880,6 +2205,10 @@ init_record_core_ops (void) /* Add bookmark target methods. */ record_core_ops.to_get_bookmark = record_get_bookmark; record_core_ops.to_goto_bookmark = record_goto_bookmark; + record_core_ops.to_async = record_async; + record_core_ops.to_can_async_p = record_can_async_p; + record_core_ops.to_is_async_p = record_is_async_p; + record_core_ops.to_execution_direction = record_execution_direction; record_core_ops.to_magic = OPS_MAGIC; } @@ -1934,6 +2263,8 @@ cmd_record_stop (char *args, int from_tty) unpush_target (&record_ops); printf_unfiltered (_("Process record is stopped and all execution " "logs are deleted.\n")); + + observer_notify_record_changed (current_inferior (), 0); } else printf_unfiltered (_("Process record is not started.\n")); @@ -1961,8 +2292,8 @@ static struct cmd_list_element *record_cmdlist, *set_record_cmdlist, static void set_record_command (char *args, int from_tty) { - printf_unfiltered (_("\ -\"set record\" must be followed by an apporpriate subcommand.\n")); + printf_unfiltered (_("\"set record\" must be followed " + "by an apporpriate subcommand.\n")); help_list (set_record_cmdlist, "set record ", all_commands, gdb_stdout); } @@ -2079,7 +2410,7 @@ bfdcore_read (bfd *obfd, asection *osec, void *buf, int len, int *offset) if (ret) *offset += len; else - error (_("Failed to read %d bytes from core file %s ('%s').\n"), + error (_("Failed to read %d bytes from core file %s ('%s')."), len, bfd_get_filename (obfd), bfd_errmsg (bfd_get_error ())); } @@ -2139,12 +2470,12 @@ record_restore (void) /* Now need to find our special note section. */ osec = bfd_get_section_by_name (core_bfd, "null0"); - osec_size = bfd_section_size (core_bfd, osec); if (record_debug) fprintf_unfiltered (gdb_stdlog, "Find precord section %s.\n", osec ? "succeeded" : "failed"); if (osec == NULL) return; + osec_size = bfd_section_size (core_bfd, osec); if (record_debug) fprintf_unfiltered (gdb_stdlog, "%s", bfd_section_name (core_bfd, osec)); @@ -2154,8 +2485,9 @@ record_restore (void) error (_("Version mis-match or file format error in core file %s."), bfd_get_filename (core_bfd)); if (record_debug) - fprintf_unfiltered (gdb_stdlog, "\ - Reading 4-byte magic cookie RECORD_FILE_MAGIC (0x%s)\n", + fprintf_unfiltered (gdb_stdlog, + " Reading 4-byte magic cookie " + "RECORD_FILE_MAGIC (0x%s)\n", phex_nz (netorder32 (magic), 4)); /* Restore the entries in recfd into record_arch_list_head and @@ -2168,7 +2500,6 @@ record_restore (void) while (1) { - int ret; uint8_t rectype; uint32_t regnum, len, signal, count; uint64_t addr; @@ -2193,8 +2524,9 @@ record_restore (void) rec->u.reg.len, &bfd_offset); if (record_debug) - fprintf_unfiltered (gdb_stdlog, "\ - Reading register %d (1 plus %lu plus %d bytes)\n", + fprintf_unfiltered (gdb_stdlog, + " Reading register %d (1 " + "plus %lu plus %d bytes)\n", rec->u.reg.num, (unsigned long) sizeof (regnum), rec->u.reg.len); @@ -2218,8 +2550,9 @@ record_restore (void) rec->u.mem.len, &bfd_offset); if (record_debug) - fprintf_unfiltered (gdb_stdlog, "\ - Reading memory %s (1 plus %lu plus %lu plus %d bytes)\n", + fprintf_unfiltered (gdb_stdlog, + " Reading memory %s (1 plus " + "%lu plus %lu plus %d bytes)\n", paddress (get_current_arch (), rec->u.mem.addr), (unsigned long) sizeof (addr), @@ -2244,8 +2577,9 @@ record_restore (void) rec->u.end.insn_num = count; record_insn_count = count + 1; if (record_debug) - fprintf_unfiltered (gdb_stdlog, "\ - Reading record_end (1 + %lu + %lu bytes), offset == %s\n", + fprintf_unfiltered (gdb_stdlog, + " Reading record_end (1 + " + "%lu + %lu bytes), offset == %s\n", (unsigned long) sizeof (signal), (unsigned long) sizeof (count), paddress (get_current_arch (), @@ -2295,7 +2629,7 @@ bfdcore_write (bfd *obfd, asection *osec, void *buf, int len, int *offset) if (ret) *offset += len; else - error (_("Failed to write %d bytes to core file %s ('%s').\n"), + error (_("Failed to write %d bytes to core file %s ('%s')."), len, bfd_get_filename (obfd), bfd_errmsg (bfd_get_error ())); } @@ -2315,7 +2649,8 @@ record_save_cleanups (void *data) { bfd *obfd = data; char *pathname = xstrdup (bfd_get_filename (obfd)); - bfd_close (obfd); + + gdb_bfd_unref (obfd); unlink (pathname); xfree (pathname); } @@ -2327,7 +2662,6 @@ static void cmd_record_save (char *args, int from_tty) { char *recfilename, recfilename_buffer[40]; - int recfd; struct record_entry *cur_record_list; uint32_t magic; struct regcache *regcache; @@ -2422,8 +2756,9 @@ cmd_record_save (char *args, int from_tty) /* Write the magic code. */ magic = RECORD_FILE_MAGIC; if (record_debug) - fprintf_unfiltered (gdb_stdlog, "\ - Writing 4-byte magic cookie RECORD_FILE_MAGIC (0x%s)\n", + fprintf_unfiltered (gdb_stdlog, + " Writing 4-byte magic cookie " + "RECORD_FILE_MAGIC (0x%s)\n", phex_nz (magic, 4)); bfdcore_write (obfd, osec, &magic, sizeof (magic), &bfd_offset); @@ -2446,8 +2781,9 @@ cmd_record_save (char *args, int from_tty) { case record_reg: /* reg */ if (record_debug) - fprintf_unfiltered (gdb_stdlog, "\ - Writing register %d (1 plus %lu plus %d bytes)\n", + fprintf_unfiltered (gdb_stdlog, + " Writing register %d (1 " + "plus %lu plus %d bytes)\n", record_list->u.reg.num, (unsigned long) sizeof (regnum), record_list->u.reg.len); @@ -2464,8 +2800,9 @@ cmd_record_save (char *args, int from_tty) case record_mem: /* mem */ if (record_debug) - fprintf_unfiltered (gdb_stdlog, "\ - Writing memory %s (1 plus %lu plus %lu plus %d bytes)\n", + fprintf_unfiltered (gdb_stdlog, + " Writing memory %s (1 plus " + "%lu plus %lu plus %d bytes)\n", paddress (gdbarch, record_list->u.mem.addr), (unsigned long) sizeof (addr), @@ -2488,8 +2825,9 @@ cmd_record_save (char *args, int from_tty) case record_end: if (record_debug) - fprintf_unfiltered (gdb_stdlog, "\ - Writing record_end (1 + %lu + %lu bytes)\n", + fprintf_unfiltered (gdb_stdlog, + " Writing record_end (1 + " + "%lu + %lu bytes)\n", (unsigned long) sizeof (signal), (unsigned long) sizeof (count)); /* Write signal value. */ @@ -2528,7 +2866,7 @@ cmd_record_save (char *args, int from_tty) } do_cleanups (set_cleanups); - bfd_close (obfd); + gdb_bfd_unref (obfd); discard_cleanups (old_cleanups); /* Succeeded. */ @@ -2631,6 +2969,9 @@ cmd_record_goto (char *arg, int from_tty) print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC); } +/* Provide a prototype to silence -Wmissing-prototypes. */ +extern initialize_file_ftype _initialize_record; + void _initialize_record (void) { @@ -2646,13 +2987,13 @@ _initialize_record (void) init_record_core_ops (); add_target (&record_core_ops); - add_setshow_zinteger_cmd ("record", no_class, &record_debug, - _("Set debugging of record/replay feature."), - _("Show debugging of record/replay feature."), - _("When enabled, debugging output for " - "record/replay feature is displayed."), - NULL, show_record_debug, &setdebuglist, - &showdebuglist); + add_setshow_zuinteger_cmd ("record", no_class, &record_debug, + _("Set debugging of record/replay feature."), + _("Show debugging of record/replay feature."), + _("When enabled, debugging output for " + "record/replay feature is displayed."), + NULL, show_record_debug, &setdebuglist, + &showdebuglist); c = add_prefix_cmd ("record", class_obscure, cmd_record_start, _("Abbreviated form of \"target record\" command."), @@ -2701,8 +3042,8 @@ Argument is filename. File must be created with 'record save'."), add_setshow_boolean_cmd ("stop-at-limit", no_class, &record_stop_at_limit, _("\ Set whether record/replay stops when record/replay buffer becomes full."), _("\ -Show whether record/replay stops when record/replay buffer becomes full."), _("\ -Default is ON.\n\ +Show whether record/replay stops when record/replay buffer becomes full."), + _("Default is ON.\n\ When ON, if the record/replay buffer becomes full, ask user what to do.\n\ When OFF, if the record/replay buffer becomes full,\n\ delete the oldest recorded instruction to make room for each new one."), @@ -2721,4 +3062,16 @@ record/replay buffer. Zero means unlimited. Default is 200000."), Restore the program to its state at instruction number N.\n\ Argument is instruction number, as shown by 'info record'."), &record_cmdlist); + + add_setshow_boolean_cmd ("memory-query", no_class, + &record_memory_query, _("\ +Set whether query if PREC cannot record memory change of next instruction."), + _("\ +Show whether query if PREC cannot record memory change of next instruction."), + _("\ +Default is OFF.\n\ +When ON, query if PREC cannot record memory change of next instruction."), + NULL, NULL, + &set_record_cmdlist, &show_record_cmdlist); + }