- 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)
- {
- if (record_resume_step)
- {
- /* This is a single step. */
- return record_beneath_to_wait (record_beneath_to_wait_ops,
- ptid, status, options);
- }
- else
- {
- /* 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 == GDB_SIGNAL_TRAP)
- {
- struct regcache *regcache;
- struct address_space *aspace;
-
- /* Yes -- this is likely our single-step finishing,
- but check if there's any reason the core would be
- interested in the event. */
-
- registers_changed ();
- regcache = get_current_regcache ();
- tmp_pc = regcache_read_pc (regcache);
- aspace = get_regcache_aspace (regcache);
-
- if (target_stopped_by_watchpoint ())
- {
- /* Always interested in watchpoints. */
- }
- else if (breakpoint_inserted_here_p (aspace, tmp_pc))
- {
- /* There is a breakpoint here. Let the core
- handle it. */
- if (software_breakpoint_inserted_here_p (aspace, tmp_pc))
- {
- struct gdbarch *gdbarch
- = get_regcache_arch (regcache);
- CORE_ADDR decr_pc_after_break
- = gdbarch_decr_pc_after_break (gdbarch);
- if (decr_pc_after_break)
- regcache_write_pc (regcache,
- tmp_pc + decr_pc_after_break);
- }
- }
- else
- {
- /* 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,
- GDB_SIGNAL_0))
- {
- status->kind = TARGET_WAITKIND_STOPPED;
- 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, step,
- GDB_SIGNAL_0);
- continue;
- }
- }
-
- /* The inferior is broken by a breakpoint or a signal. */
- break;
- }
-
- return ret;
- }
- }
- else
- {
- struct regcache *regcache = get_current_regcache ();
- struct gdbarch *gdbarch = get_regcache_arch (regcache);
- struct address_space *aspace = get_regcache_aspace (regcache);
- int continue_flag = 1;
- int first_record_end = 1;
- struct cleanup *old_cleanups = make_cleanup (record_wait_cleanups, 0);
- CORE_ADDR tmp_pc;
-
- record_hw_watchpoint = 0;
- status->kind = TARGET_WAITKIND_STOPPED;
-
- /* Check breakpoint when forward execute. */
- if (execution_direction == EXEC_FORWARD)
- {
- tmp_pc = regcache_read_pc (regcache);
- if (breakpoint_inserted_here_p (aspace, tmp_pc))
- {
- int decr_pc_after_break = gdbarch_decr_pc_after_break (gdbarch);
-
- if (record_debug)
- fprintf_unfiltered (gdb_stdlog,
- "Process record: break at %s.\n",
- paddress (gdbarch, tmp_pc));
-
- if (decr_pc_after_break
- && !record_resume_step
- && software_breakpoint_inserted_here_p (aspace, tmp_pc))
- regcache_write_pc (regcache,
- tmp_pc + decr_pc_after_break);
- goto replay_out;
- }
- }
-
- /* 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.
- Then set it to terminal_ours to make GDB get the signal. */
- target_terminal_ours ();
-
- /* In EXEC_FORWARD mode, record_list points to the tail of prev
- instruction. */
- if (execution_direction == EXEC_FORWARD && record_list->next)
- record_list = record_list->next;
-
- /* Loop over the record_list, looking for the next place to
- stop. */
- do
- {
- /* Check for beginning and end of log. */
- if (execution_direction == EXEC_REVERSE
- && record_list == &record_first)
- {
- /* Hit beginning of record log in reverse. */
- status->kind = TARGET_WAITKIND_NO_HISTORY;
- break;
- }
- if (execution_direction != EXEC_REVERSE && !record_list->next)
- {
- /* Hit end of record log going forward. */
- status->kind = TARGET_WAITKIND_NO_HISTORY;
- break;
- }
-
- record_exec_insn (regcache, gdbarch, record_list);
-
- if (record_list->type == record_end)
- {
- if (record_debug > 1)
- fprintf_unfiltered (gdb_stdlog,
- "Process record: record_end %s to "
- "inferior.\n",
- host_address_to_string (record_list));
-
- if (first_record_end && execution_direction == EXEC_REVERSE)
- {
- /* When reverse excute, the first record_end is the part of
- current instruction. */
- first_record_end = 0;
- }
- else
- {
- /* In EXEC_REVERSE mode, this is the record_end of prev
- instruction.
- In EXEC_FORWARD mode, this is the record_end of current
- instruction. */
- /* step */
- if (record_resume_step)
- {
- if (record_debug > 1)
- fprintf_unfiltered (gdb_stdlog,
- "Process record: step.\n");
- continue_flag = 0;
- }
-
- /* check breakpoint */
- tmp_pc = regcache_read_pc (regcache);
- if (breakpoint_inserted_here_p (aspace, tmp_pc))
- {
- int decr_pc_after_break
- = gdbarch_decr_pc_after_break (gdbarch);
-
- if (record_debug)
- fprintf_unfiltered (gdb_stdlog,
- "Process record: break "
- "at %s.\n",
- paddress (gdbarch, tmp_pc));
- if (decr_pc_after_break
- && execution_direction == EXEC_FORWARD
- && !record_resume_step
- && software_breakpoint_inserted_here_p (aspace,
- tmp_pc))
- regcache_write_pc (regcache,
- tmp_pc + decr_pc_after_break);
- continue_flag = 0;
- }
-
- if (record_hw_watchpoint)
- {
- if (record_debug)
- fprintf_unfiltered (gdb_stdlog,
- "Process record: hit hw "
- "watchpoint.\n");
- continue_flag = 0;
- }
- /* Check target signal */
- if (record_list->u.end.sigval != GDB_SIGNAL_0)
- /* FIXME: better way to check */
- continue_flag = 0;
- }
- }
-
- if (continue_flag)
- {
- if (execution_direction == EXEC_REVERSE)
- {
- if (record_list->prev)
- record_list = record_list->prev;
- }
- else
- {
- if (record_list->next)
- record_list = record_list->next;
- }
- }
- }
- while (continue_flag);
-
-replay_out:
- if (record_get_sig)
- 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 = 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)
-{
- if (RECORD_IS_REPLAY)
- return record_hw_watchpoint;
- else
- return record_beneath_to_stopped_by_watchpoint ();
-}
-
-static int
-record_stopped_data_address (struct target_ops *ops, CORE_ADDR *addr_p)
-{
- if (RECORD_IS_REPLAY)
- return 0;
- else
- return record_beneath_to_stopped_data_address (ops, addr_p);
-}
-
-/* "to_disconnect" method for process record target. */
-
-static void
-record_disconnect (struct target_ops *target, char *args, int from_tty)
-{
- if (record_debug)
- fprintf_unfiltered (gdb_stdlog, "Process record: record_disconnect\n");
-
- unpush_target (&record_ops);
- target_disconnect (args, from_tty);
-}
-
-/* "to_detach" method for process record target. */
-
-static void
-record_detach (struct target_ops *ops, char *args, int from_tty)
-{
- if (record_debug)
- fprintf_unfiltered (gdb_stdlog, "Process record: record_detach\n");
-
- unpush_target (&record_ops);
- target_detach (args, from_tty);
-}
-
-/* "to_mourn_inferior" method for process record target. */
-
-static void
-record_mourn_inferior (struct target_ops *ops)
-{
- if (record_debug)
- fprintf_unfiltered (gdb_stdlog, "Process record: "
- "record_mourn_inferior\n");
-
- unpush_target (&record_ops);
- target_mourn_inferior ();
-}
-
-/* Close process record target before killing the inferior process. */
-
-static void
-record_kill (struct target_ops *ops)
-{
- if (record_debug)
- fprintf_unfiltered (gdb_stdlog, "Process record: record_kill\n");
-
- unpush_target (&record_ops);
- target_kill ();
-}
-
-/* Record registers change (by user or by GDB) to list as an instruction. */
-
-static void
-record_registers_change (struct regcache *regcache, int regnum)
-{
- /* Check record_insn_num. */
- record_check_insn_num (0);
-
- record_arch_list_head = NULL;
- record_arch_list_tail = NULL;
-
- 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))
- {
- record_list_release (record_arch_list_tail);
- error (_("Process record: failed to record execution log."));
- }
- }
- }
- else
- {
- if (record_arch_list_add_reg (regcache, regnum))
- {
- record_list_release (record_arch_list_tail);
- error (_("Process record: failed to record execution log."));
- }
- }
- if (record_arch_list_add_end ())
- {
- record_list_release (record_arch_list_tail);
- error (_("Process record: failed to record execution log."));
- }
- record_list->next = record_arch_list_head;
- record_arch_list_head->prev = record_list;
- record_list = record_arch_list_tail;
-
- if (record_insn_num == record_insn_max_num && record_insn_max_num)
- record_list_release_first ();
- else
- record_insn_num++;
-}
-
-/* "to_store_registers" method for process record target. */
-
-static void
-record_store_registers (struct target_ops *ops, struct regcache *regcache,
- int regno)
-{
- if (!record_gdb_operation_disable)
- {
- if (RECORD_IS_REPLAY)
- {
- int n;
-
- /* Let user choose if he wants to write register or not. */
- if (regno < 0)
- n =
- query (_("Because GDB is in replay mode, changing the "
- "value of a register will make the execution "
- "log unusable from this point onward. "
- "Change all registers?"));
- else
- n =
- query (_("Because GDB is in replay mode, changing the value "
- "of a register will make the execution log unusable "
- "from this point onward. Change register %s?"),
- gdbarch_register_name (get_regcache_arch (regcache),
- regno));
-
- if (!n)
- {
- /* Invalidate the value of regcache that was set in function
- "regcache_raw_write". */
- if (regno < 0)
- {
- int i;
-
- for (i = 0;
- i < gdbarch_num_regs (get_regcache_arch (regcache));
- i++)
- regcache_invalidate (regcache, i);
- }
- else
- regcache_invalidate (regcache, regno);
-
- error (_("Process record canceled the operation."));
- }
-
- /* Destroy the record from here forward. */
- record_list_release_following (record_list);
- }
-
- record_registers_change (regcache, regno);
- }
- record_beneath_to_store_registers (record_beneath_to_store_registers_ops,
- regcache, regno);
-}
-
-/* "to_xfer_partial" method. Behavior is conditional on RECORD_IS_REPLAY.
- In replay mode, we cannot write memory unles we are willing to
- invalidate the record/replay log from this point forward. */
-
-static LONGEST
-record_xfer_partial (struct target_ops *ops, enum target_object object,
- const char *annex, gdb_byte *readbuf,
- const gdb_byte *writebuf, ULONGEST offset, LONGEST len)
-{
- if (!record_gdb_operation_disable
- && (object == TARGET_OBJECT_MEMORY
- || object == TARGET_OBJECT_RAW_MEMORY) && writebuf)
- {
- if (RECORD_IS_REPLAY)
- {
- /* Let user choose if he wants to write memory or not. */
- 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)))
- error (_("Process record canceled the operation."));
-
- /* Destroy the record from here forward. */
- record_list_release_following (record_list);
- }
-
- /* Check record_insn_num */
- record_check_insn_num (0);
-
- /* Record registers change to list as an instruction. */
- record_arch_list_head = NULL;
- record_arch_list_tail = NULL;
- if (record_arch_list_add_mem (offset, len))
- {
- record_list_release (record_arch_list_tail);
- if (record_debug)
- fprintf_unfiltered (gdb_stdlog,
- "Process record: failed to record "
- "execution log.");
- return -1;
- }
- if (record_arch_list_add_end ())
- {
- record_list_release (record_arch_list_tail);
- if (record_debug)
- fprintf_unfiltered (gdb_stdlog,
- "Process record: failed to record "
- "execution log.");
- return -1;
- }
- record_list->next = record_arch_list_head;
- record_arch_list_head->prev = record_list;
- record_list = record_arch_list_tail;
-
- if (record_insn_num == record_insn_max_num && record_insn_max_num)
- record_list_release_first ();
- else
- record_insn_num++;
- }
-
- return record_beneath_to_xfer_partial (record_beneath_to_xfer_partial_ops,
- object, annex, readbuf, writebuf,
- offset, len);
-}
-
-/* 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)
- {
- /* 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);
-
- 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;
-}
-
-/* "to_remove_breakpoint" method for process record target. */
-
-static int
-record_remove_breakpoint (struct gdbarch *gdbarch,
- struct bp_target_info *bp_tgt)
-{
- struct record_breakpoint *bp;
- int ix;
-
- for (ix = 0;
- VEC_iterate (record_breakpoint_p, record_breakpoints, ix, bp);
- ++ix)
- {
- 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;
-
- 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;
- }
-
- VEC_unordered_remove (record_breakpoint_p, record_breakpoints, ix);
- return 0;
- }
- }
-
- gdb_assert_not_reached ("removing unknown breakpoint");
-}
-
-/* "to_can_execute_reverse" method for process record target. */
-
-static int
-record_can_execute_reverse (void)
-{
- return 1;
-}
-
-/* "to_get_bookmark" method for process record and prec over core. */
-
-static gdb_byte *
-record_get_bookmark (char *args, int from_tty)
-{
- gdb_byte *ret = NULL;
-
- /* Return stringified form of instruction count. */
- if (record_list && record_list->type == record_end)
- ret = xstrdup (pulongest (record_list->u.end.insn_num));
-
- if (record_debug)
- {
- if (ret)
- fprintf_unfiltered (gdb_stdlog,
- "record_get_bookmark returns %s\n", ret);
- else
- fprintf_unfiltered (gdb_stdlog,
- "record_get_bookmark returns NULL\n");
- }
- return ret;
-}
-
-/* The implementation of the command "record goto". */
-static void cmd_record_goto (char *, int);
-
-/* "to_goto_bookmark" method for process record and prec over core. */
-
-static void
-record_goto_bookmark (gdb_byte *bookmark, int from_tty)
-{
- if (record_debug)
- fprintf_unfiltered (gdb_stdlog,
- "record_goto_bookmark receives %s\n", bookmark);
-
- if (bookmark[0] == '\'' || bookmark[0] == '\"')
- {
- if (bookmark[strlen (bookmark) - 1] != bookmark[0])
- error (_("Unbalanced quotes: %s"), bookmark);
-
- /* Strip trailing quote. */
- bookmark[strlen (bookmark) - 1] = '\0';
- /* Strip leading quote. */
- bookmark++;
- /* Pass along to cmd_record_goto. */
- }
-
- cmd_record_goto ((char *) bookmark, 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)
-{
- record_ops.to_shortname = "record";
- record_ops.to_longname = "Process record and replay target";
- record_ops.to_doc =
- "Log program while executing and replay execution from log.";
- record_ops.to_open = record_open;
- record_ops.to_close = record_close;
- record_ops.to_resume = record_resume;
- record_ops.to_wait = record_wait;
- record_ops.to_disconnect = record_disconnect;
- record_ops.to_detach = record_detach;
- record_ops.to_mourn_inferior = record_mourn_inferior;
- record_ops.to_kill = record_kill;
- record_ops.to_create_inferior = find_default_create_inferior;
- record_ops.to_store_registers = record_store_registers;
- record_ops.to_xfer_partial = record_xfer_partial;
- record_ops.to_insert_breakpoint = record_insert_breakpoint;
- record_ops.to_remove_breakpoint = record_remove_breakpoint;
- record_ops.to_stopped_by_watchpoint = record_stopped_by_watchpoint;
- record_ops.to_stopped_data_address = record_stopped_data_address;
- record_ops.to_can_execute_reverse = record_can_execute_reverse;
- record_ops.to_stratum = record_stratum;
- /* 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;
-}
-
-/* "to_resume" method for prec over corefile. */
-
-static void
-record_core_resume (struct target_ops *ops, ptid_t ptid, int step,
- 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. */
-
-static void
-record_core_kill (struct target_ops *ops)
-{
- if (record_debug)
- fprintf_unfiltered (gdb_stdlog, "Process record: record_core_kill\n");
-
- unpush_target (&record_core_ops);
-}
-
-/* "to_fetch_registers" method for prec over corefile. */
-
-static void
-record_core_fetch_registers (struct target_ops *ops,
- struct regcache *regcache,
- int regno)
-{
- if (regno < 0)
- {
- int num = gdbarch_num_regs (get_regcache_arch (regcache));
- int i;
-
- for (i = 0; i < num; i ++)
- regcache_raw_supply (regcache, i,
- record_core_regbuf + MAX_REGISTER_SIZE * i);
- }
- else
- regcache_raw_supply (regcache, regno,
- record_core_regbuf + MAX_REGISTER_SIZE * regno);
-}
-
-/* "to_prepare_to_store" method for prec over corefile. */
-
-static void
-record_core_prepare_to_store (struct regcache *regcache)
-{
-}
-
-/* "to_store_registers" method for prec over corefile. */
-
-static void
-record_core_store_registers (struct target_ops *ops,
- struct regcache *regcache,
- int regno)
-{
- if (record_gdb_operation_disable)
- regcache_raw_collect (regcache, regno,
- record_core_regbuf + MAX_REGISTER_SIZE * regno);
- else
- error (_("You can't do that without a process to debug."));
-}
-
-/* "to_xfer_partial" method for prec over corefile. */
-
-static LONGEST
-record_core_xfer_partial (struct target_ops *ops, enum target_object object,
- const char *annex, gdb_byte *readbuf,
- 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."));
- }
-
- return record_beneath_to_xfer_partial (record_beneath_to_xfer_partial_ops,
- object, annex, readbuf, writebuf,
- offset, len);
-}
-
-/* "to_insert_breakpoint" method for prec over corefile. */
-
-static int
-record_core_insert_breakpoint (struct gdbarch *gdbarch,
- struct bp_target_info *bp_tgt)
-{
- return 0;
-}
-
-/* "to_remove_breakpoint" method for prec over corefile. */
-
-static int
-record_core_remove_breakpoint (struct gdbarch *gdbarch,
- struct bp_target_info *bp_tgt)
-{
- return 0;
-}
-
-/* "to_has_execution" method for prec over corefile. */
-
-static int
-record_core_has_execution (struct target_ops *ops, ptid_t the_ptid)
-{
- return 1;
-}
-
-static void
-init_record_core_ops (void)
-{
- 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.";
- record_core_ops.to_open = record_open;
- record_core_ops.to_close = record_close;
- record_core_ops.to_resume = record_core_resume;
- record_core_ops.to_wait = record_wait;
- record_core_ops.to_kill = record_core_kill;
- record_core_ops.to_fetch_registers = record_core_fetch_registers;
- record_core_ops.to_prepare_to_store = record_core_prepare_to_store;
- record_core_ops.to_store_registers = record_core_store_registers;
- record_core_ops.to_xfer_partial = record_core_xfer_partial;
- record_core_ops.to_insert_breakpoint = record_core_insert_breakpoint;
- record_core_ops.to_remove_breakpoint = record_core_remove_breakpoint;
- record_core_ops.to_stopped_by_watchpoint = record_stopped_by_watchpoint;
- record_core_ops.to_stopped_data_address = record_stopped_data_address;
- record_core_ops.to_can_execute_reverse = record_can_execute_reverse;
- record_core_ops.to_has_execution = record_core_has_execution;
- record_core_ops.to_stratum = record_stratum;
- /* 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;
-}
-
-/* Implement "show record debug" command. */
-
-static void
-show_record_debug (struct ui_file *file, int from_tty,
- struct cmd_list_element *c, const char *value)
-{
- fprintf_filtered (file, _("Debugging of process record target is %s.\n"),
- value);
-}
-
-/* Alias for "target record". */
-
-static void
-cmd_record_start (char *args, int from_tty)
-{
- execute_command ("target record", from_tty);
-}
-
-/* Truncate the record log from the present point
- of replay until the end. */
-
-static void
-cmd_record_delete (char *args, int from_tty)
-{
- if (current_target.to_stratum == record_stratum)
- {
- if (RECORD_IS_REPLAY)
- {
- if (!from_tty || query (_("Delete the log from this point forward "
- "and begin to record the running message "
- "at current PC?")))
- record_list_release_following (record_list);
- }
- else
- printf_unfiltered (_("Already at end of record list.\n"));
-
- }
- else
- printf_unfiltered (_("Process record is not started.\n"));
-}
-
-/* Implement the "stoprecord" or "record stop" command. */
-
-static void
-cmd_record_stop (char *args, int from_tty)
-{
- if (current_target.to_stratum == record_stratum)
- {
- unpush_target (&record_ops);
- printf_unfiltered (_("Process record is stopped and all execution "
- "logs are deleted.\n"));
- }
- else
- printf_unfiltered (_("Process record is not started.\n"));
-}
-
-/* Set upper limit of record log size. */
-
-static void
-set_record_insn_max_num (char *args, int from_tty, struct cmd_list_element *c)
-{
- if (record_insn_num > record_insn_max_num && record_insn_max_num)
- {
- /* Count down record_insn_num while releasing records from list. */
- while (record_insn_num > record_insn_max_num)
- {
- record_list_release_first ();
- record_insn_num--;
- }
- }
-}
-
-static struct cmd_list_element *record_cmdlist, *set_record_cmdlist,
- *show_record_cmdlist, *info_record_cmdlist;
-
-static void
-set_record_command (char *args, int from_tty)
-{
- printf_unfiltered (_("\"set record\" must be followed "
- "by an apporpriate subcommand.\n"));
- help_list (set_record_cmdlist, "set record ", all_commands, gdb_stdout);
-}
-
-static void
-show_record_command (char *args, int from_tty)
-{
- cmd_show_list (show_record_cmdlist, from_tty, "");
-}
-
-/* Display some statistics about the execution log. */
-
-static void
-info_record_command (char *args, int from_tty)
-{
- struct record_entry *p;
-
- if (current_target.to_stratum == record_stratum)
- {
- if (RECORD_IS_REPLAY)
- printf_filtered (_("Replay mode:\n"));
- else
- printf_filtered (_("Record mode:\n"));
-
- /* Find entry for first actual instruction in the log. */
- for (p = record_first.next;
- p != NULL && p->type != record_end;
- p = p->next)
- ;
-
- /* Do we have a log at all? */
- if (p != NULL && p->type == record_end)
- {
- /* Display instruction number for first instruction in the log. */
- printf_filtered (_("Lowest recorded instruction number is %s.\n"),
- pulongest (p->u.end.insn_num));
-
- /* If in replay mode, display where we are in the log. */
- if (RECORD_IS_REPLAY)
- printf_filtered (_("Current instruction number is %s.\n"),
- pulongest (record_list->u.end.insn_num));
-
- /* Display instruction number for last instruction in the log. */
- printf_filtered (_("Highest recorded instruction number is %s.\n"),
- pulongest (record_insn_count));
-
- /* Display log count. */
- printf_filtered (_("Log contains %d instructions.\n"),
- record_insn_num);
- }
- else
- {
- printf_filtered (_("No instructions have been logged.\n"));
- }
- }
- else
- {
- printf_filtered (_("target record is not active.\n"));
- }
-
- /* Display max log size. */
- printf_filtered (_("Max logged instructions is %d.\n"),
- record_insn_max_num);
-}
-
-/* Record log save-file format
- Version 1 (never released)
-
- Header:
- 4 bytes: magic number htonl(0x20090829).
- NOTE: be sure to change whenever this file format changes!
-
- Records:
- record_end:
- 1 byte: record type (record_end, see enum record_type).
- record_reg:
- 1 byte: record type (record_reg, see enum record_type).
- 8 bytes: register id (network byte order).
- MAX_REGISTER_SIZE bytes: register value.
- record_mem:
- 1 byte: record type (record_mem, see enum record_type).
- 8 bytes: memory length (network byte order).
- 8 bytes: memory address (network byte order).
- n bytes: memory value (n == memory length).
-
- Version 2
- 4 bytes: magic number netorder32(0x20091016).
- NOTE: be sure to change whenever this file format changes!
-
- Records:
- record_end:
- 1 byte: record type (record_end, see enum record_type).
- 4 bytes: signal
- 4 bytes: instruction count
- record_reg:
- 1 byte: record type (record_reg, see enum record_type).
- 4 bytes: register id (network byte order).
- n bytes: register value (n == actual register size).
- (eg. 4 bytes for x86 general registers).
- record_mem:
- 1 byte: record type (record_mem, see enum record_type).
- 4 bytes: memory length (network byte order).
- 8 bytes: memory address (network byte order).
- n bytes: memory value (n == memory length).
-
-*/
-
-/* bfdcore_read -- read bytes from a core file section. */
-
-static inline void
-bfdcore_read (bfd *obfd, asection *osec, void *buf, int len, int *offset)
-{
- int ret = bfd_get_section_contents (obfd, osec, buf, *offset, len);
-
- if (ret)
- *offset += len;
- else
- error (_("Failed to read %d bytes from core file %s ('%s')."),
- len, bfd_get_filename (obfd),
- bfd_errmsg (bfd_get_error ()));
-}
-
-static inline uint64_t
-netorder64 (uint64_t input)
-{
- uint64_t ret;
-
- store_unsigned_integer ((gdb_byte *) &ret, sizeof (ret),
- BFD_ENDIAN_BIG, input);
- return ret;
-}
-
-static inline uint32_t
-netorder32 (uint32_t input)
-{
- uint32_t ret;
-
- store_unsigned_integer ((gdb_byte *) &ret, sizeof (ret),
- BFD_ENDIAN_BIG, input);
- return ret;
-}
-
-static inline uint16_t
-netorder16 (uint16_t input)
-{
- uint16_t ret;
-
- store_unsigned_integer ((gdb_byte *) &ret, sizeof (ret),
- BFD_ENDIAN_BIG, input);
- return ret;
-}
-
-/* Restore the execution log from a core_bfd file. */
-static void
-record_restore (void)
-{
- uint32_t magic;
- struct cleanup *old_cleanups;
- struct record_entry *rec;
- asection *osec;
- uint32_t osec_size;
- int bfd_offset = 0;
- struct regcache *regcache;
-
- /* We restore the execution log from the open core bfd,
- if there is one. */
- if (core_bfd == NULL)
- return;
-
- /* "record_restore" can only be called when record list is empty. */
- gdb_assert (record_first.next == NULL);
-
- if (record_debug)
- fprintf_unfiltered (gdb_stdlog, "Restoring recording from core file.\n");
-
- /* Now need to find our special note section. */
- osec = bfd_get_section_by_name (core_bfd, "null0");
- 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));
-
- /* Check the magic code. */
- bfdcore_read (core_bfd, osec, &magic, sizeof (magic), &bfd_offset);
- if (magic != RECORD_FILE_MAGIC)
- 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",
- phex_nz (netorder32 (magic), 4));
-
- /* Restore the entries in recfd into record_arch_list_head and
- record_arch_list_tail. */
- record_arch_list_head = NULL;
- record_arch_list_tail = NULL;
- record_insn_num = 0;
- old_cleanups = make_cleanup (record_arch_list_cleanups, 0);
- regcache = get_current_regcache ();
-
- while (1)
- {
- uint8_t rectype;
- uint32_t regnum, len, signal, count;
- uint64_t addr;
-
- /* We are finished when offset reaches osec_size. */
- if (bfd_offset >= osec_size)
- break;
- bfdcore_read (core_bfd, osec, &rectype, sizeof (rectype), &bfd_offset);
-
- switch (rectype)
- {
- case record_reg: /* reg */
- /* Get register number to regnum. */
- bfdcore_read (core_bfd, osec, ®num,
- sizeof (regnum), &bfd_offset);
- regnum = netorder32 (regnum);
-
- rec = record_reg_alloc (regcache, regnum);
-
- /* Get val. */
- bfdcore_read (core_bfd, osec, record_get_loc (rec),
- rec->u.reg.len, &bfd_offset);
-
- if (record_debug)
- 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);
- break;
-
- case record_mem: /* mem */
- /* Get len. */
- bfdcore_read (core_bfd, osec, &len,
- sizeof (len), &bfd_offset);
- len = netorder32 (len);
-
- /* Get addr. */
- bfdcore_read (core_bfd, osec, &addr,
- sizeof (addr), &bfd_offset);
- addr = netorder64 (addr);
-
- rec = record_mem_alloc (addr, len);
-
- /* Get val. */
- bfdcore_read (core_bfd, osec, record_get_loc (rec),
- 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",
- paddress (get_current_arch (),
- rec->u.mem.addr),
- (unsigned long) sizeof (addr),
- (unsigned long) sizeof (len),
- rec->u.mem.len);
- break;
-
- case record_end: /* end */
- rec = record_end_alloc ();
- record_insn_num ++;
-
- /* Get signal value. */
- bfdcore_read (core_bfd, osec, &signal,
- sizeof (signal), &bfd_offset);
- signal = netorder32 (signal);
- rec->u.end.sigval = signal;
-
- /* Get insn count. */
- bfdcore_read (core_bfd, osec, &count,
- sizeof (count), &bfd_offset);
- count = netorder32 (count);
- 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",
- (unsigned long) sizeof (signal),
- (unsigned long) sizeof (count),
- paddress (get_current_arch (),
- bfd_offset));
- break;
-
- default:
- error (_("Bad entry type in core file %s."),
- bfd_get_filename (core_bfd));
- break;
- }
-
- /* Add rec to record arch list. */
- record_arch_list_add (rec);
- }
-
- discard_cleanups (old_cleanups);
-
- /* Add record_arch_list_head to the end of record list. */
- record_first.next = record_arch_list_head;
- record_arch_list_head->prev = &record_first;
- record_arch_list_tail->next = NULL;
- record_list = &record_first;
-
- /* Update record_insn_max_num. */
- if (record_insn_num > record_insn_max_num)
- {
- record_insn_max_num = record_insn_num;
- warning (_("Auto increase record/replay buffer limit to %d."),
- record_insn_max_num);
- }
-
- /* Succeeded. */
- printf_filtered (_("Restored records from core file %s.\n"),
- bfd_get_filename (core_bfd));
-
- print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
-}
-
-/* bfdcore_write -- write bytes into a core file section. */
-
-static inline void
-bfdcore_write (bfd *obfd, asection *osec, void *buf, int len, int *offset)
-{
- int ret = bfd_set_section_contents (obfd, osec, buf, *offset, len);
-
- if (ret)
- *offset += len;
- else
- error (_("Failed to write %d bytes to core file %s ('%s')."),
- len, bfd_get_filename (obfd),
- bfd_errmsg (bfd_get_error ()));
-}
-
-/* Restore the execution log from a file. We use a modified elf
- corefile format, with an extra section for our data. */
-
-static void
-cmd_record_restore (char *args, int from_tty)
-{
- core_file_command (args, from_tty);
- record_open (args, from_tty);
-}
-
-static void
-record_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. */
-
-static void
-cmd_record_save (char *args, int from_tty)
-{
- char *recfilename, recfilename_buffer[40];
- struct record_entry *cur_record_list;
- 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;
-
- if (strcmp (current_target.to_shortname, "record") != 0)
- error (_("This command can only be used with target 'record'.\n"
- "Use 'target record' first.\n"));
-
- if (args && *args)
- recfilename = args;
- else
- {
- /* Default recfile name is "gdb_record.PID". */
- snprintf (recfilename_buffer, sizeof (recfilename_buffer),
- "gdb_record.%d", PIDGET (inferior_ptid));
- recfilename = recfilename_buffer;
- }
-
- /* Open the save file. */
- if (record_debug)
- fprintf_unfiltered (gdb_stdlog, "Saving execution log to core file '%s'\n",
- recfilename);
-
- /* Open the output file. */
- obfd = create_gcore_bfd (recfilename);
- old_cleanups = make_cleanup (record_save_cleanups, obfd);
-
- /* Save the current record entry to "cur_record_list". */
- cur_record_list = record_list;
-
- /* Get the values of regcache and gdbarch. */
- regcache = get_current_regcache ();
- gdbarch = get_regcache_arch (regcache);
-
- /* Disable the GDB operation record. */
- set_cleanups = record_gdb_operation_disable_set ();
-
- /* Reverse execute to the begin of record list. */
- while (1)
- {
- /* Check for beginning and end of log. */
- if (record_list == &record_first)
- break;
-
- record_exec_insn (regcache, gdbarch, record_list);
-
- if (record_list->prev)
- record_list = record_list->prev;
- }
-
- /* Compute the size needed for the extra bfd section. */
- save_size = 4; /* magic cookie */
- for (record_list = record_first.next; record_list;
- record_list = record_list->next)
- switch (record_list->type)
- {
- case record_end:
- save_size += 1 + 4 + 4;
- break;
- case record_reg:
- save_size += 1 + 4 + record_list->u.reg.len;
- break;
- case record_mem:
- save_size += 1 + 4 + 8 + record_list->u.mem.len;
- break;
- }
-
- /* Make the new bfd section. */
- osec = bfd_make_section_anyway_with_flags (obfd, "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;
-
- /* Save corefile state. */
- write_gcore_file (obfd);
-
- /* Write out the record log. */
- /* 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",
- phex_nz (magic, 4));
- bfdcore_write (obfd, osec, &magic, sizeof (magic), &bfd_offset);
-
- /* Save the entries to recfd and forward execute to the end of
- record list. */
- record_list = &record_first;
- while (1)
- {
- /* Save entry. */
- if (record_list != &record_first)
- {
- uint8_t type;
- uint32_t regnum, len, signal, count;
- uint64_t addr;
-
- type = record_list->type;
- bfdcore_write (obfd, osec, &type, sizeof (type), &bfd_offset);
-
- switch (record_list->type)
- {
- case record_reg: /* reg */
- if (record_debug)
- 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);
-
- /* Write regnum. */
- regnum = netorder32 (record_list->u.reg.num);
- bfdcore_write (obfd, osec, ®num,
- sizeof (regnum), &bfd_offset);
-
- /* Write regval. */
- bfdcore_write (obfd, osec, record_get_loc (record_list),
- record_list->u.reg.len, &bfd_offset);
- break;
-
- case record_mem: /* mem */
- if (record_debug)
- 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),
- (unsigned long) sizeof (len),
- record_list->u.mem.len);
-
- /* Write memlen. */
- len = netorder32 (record_list->u.mem.len);
- bfdcore_write (obfd, osec, &len, sizeof (len), &bfd_offset);
-
- /* Write memaddr. */
- addr = netorder64 (record_list->u.mem.addr);
- bfdcore_write (obfd, osec, &addr,
- sizeof (addr), &bfd_offset);
-
- /* Write memval. */
- bfdcore_write (obfd, osec, record_get_loc (record_list),
- record_list->u.mem.len, &bfd_offset);
- break;
-
- case record_end:
- if (record_debug)
- fprintf_unfiltered (gdb_stdlog,
- " Writing record_end (1 + "
- "%lu + %lu bytes)\n",
- (unsigned long) sizeof (signal),
- (unsigned long) sizeof (count));
- /* Write signal value. */
- signal = netorder32 (record_list->u.end.sigval);
- bfdcore_write (obfd, osec, &signal,
- sizeof (signal), &bfd_offset);
-
- /* Write insn count. */
- count = netorder32 (record_list->u.end.insn_num);
- bfdcore_write (obfd, osec, &count,
- sizeof (count), &bfd_offset);
- break;
- }
- }
-
- /* Execute entry. */
- record_exec_insn (regcache, gdbarch, record_list);
-
- if (record_list->next)
- record_list = record_list->next;
- else
- break;
- }
-
- /* Reverse execute to cur_record_list. */
- while (1)
- {
- /* Check for beginning and end of log. */
- if (record_list == cur_record_list)
- break;
-
- record_exec_insn (regcache, gdbarch, record_list);
-
- if (record_list->prev)
- record_list = record_list->prev;
- }
-
- do_cleanups (set_cleanups);
- gdb_bfd_unref (obfd);
- discard_cleanups (old_cleanups);
-
- /* Succeeded. */
- printf_filtered (_("Saved core file %s with execution log.\n"),
- recfilename);
-}
-
-/* record_goto_insn -- rewind the record log (forward or backward,
- depending on DIR) to the given entry, changing the program state
- correspondingly. */
-
-static void
-record_goto_insn (struct record_entry *entry,
- enum exec_direction_kind dir)
-{
- struct cleanup *set_cleanups = record_gdb_operation_disable_set ();
- struct regcache *regcache = get_current_regcache ();
- struct gdbarch *gdbarch = get_regcache_arch (regcache);
-
- /* Assume everything is valid: we will hit the entry,
- and we will not hit the end of the recording. */
-
- if (dir == EXEC_FORWARD)
- record_list = record_list->next;
-
- do
- {
- record_exec_insn (regcache, gdbarch, record_list);
- if (dir == EXEC_REVERSE)
- record_list = record_list->prev;
- else
- record_list = record_list->next;
- } while (record_list != entry);
- do_cleanups (set_cleanups);
-}
-
-/* "record goto" command. Argument is an instruction number,
- as given by "info record".
-
- Rewinds the recording (forward or backward) to the given instruction. */
-
-static void
-cmd_record_goto (char *arg, int from_tty)
-{
- struct record_entry *p = NULL;
- ULONGEST target_insn = 0;
-
- if (arg == NULL || *arg == '\0')
- error (_("Command requires an argument (insn number to go to)."));
-
- if (strncmp (arg, "start", strlen ("start")) == 0
- || strncmp (arg, "begin", strlen ("begin")) == 0)
- {
- /* Special case. Find first insn. */
- for (p = &record_first; p != NULL; p = p->next)
- if (p->type == record_end)
- break;
- if (p)
- target_insn = p->u.end.insn_num;
- }
- else if (strncmp (arg, "end", strlen ("end")) == 0)
- {
- /* Special case. Find last insn. */
- for (p = record_list; p->next != NULL; p = p->next)
- ;
- for (; p!= NULL; p = p->prev)
- if (p->type == record_end)
- break;
- if (p)
- target_insn = p->u.end.insn_num;
- }
- else
- {
- /* General case. Find designated insn. */
- target_insn = parse_and_eval_long (arg);
-
- for (p = &record_first; p != NULL; p = p->next)
- if (p->type == record_end && p->u.end.insn_num == target_insn)
- break;
- }
-
- if (p == NULL)
- error (_("Target insn '%s' not found."), arg);
- else if (p == record_list)
- error (_("Already at insn '%s'."), arg);
- else if (p->u.end.insn_num > record_list->u.end.insn_num)
- {
- printf_filtered (_("Go forward to insn number %s\n"),
- pulongest (target_insn));
- record_goto_insn (p, EXEC_FORWARD);
- }
- else
- {
- printf_filtered (_("Go backward to insn number %s\n"),
- pulongest (target_insn));
- record_goto_insn (p, EXEC_REVERSE);
- }
- registers_changed ();
- reinit_frame_cache ();
- 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)
-{
- struct cmd_list_element *c;
-
- /* Init record_first. */
- record_first.prev = NULL;
- record_first.next = NULL;
- record_first.type = record_end;
-
- init_record_ops ();
- add_target (&record_ops);
- init_record_core_ops ();
- add_target (&record_core_ops);