#define RECORD_IS_REPLAY \
(record_list->next || execution_direction == EXEC_REVERSE)
-/* These are the core struct of record function.
+/* These are the core structs of the process record functionality.
- An record_entry is a record of the value change of a register
+ A record_entry is a record of the value change of a register
("record_reg") or a part of memory ("record_mem"). And each
- instruction must has a struct record_entry ("record_end") that points out this
- is the last struct record_entry of this instruction.
+ instruction must have a struct record_entry ("record_end") that
+ indicates that this is the last struct record_entry of this
+ instruction.
- Each struct record_entry is linked to "record_list" by "prev" and "next". */
+ Each struct record_entry is linked to "record_list" by "prev" and
+ "next" pointers. */
struct record_reg_entry
{
gdb_byte *val;
};
+struct record_end_entry
+{
+ enum target_signal sigval;
+};
+
enum record_type
{
record_end = 0,
struct record_reg_entry reg;
/* mem */
struct record_mem_entry mem;
+ /* end */
+ struct record_end_entry end;
} u;
};
while (tmp)
{
rec = tmp->next;
- if (tmp->type == record_reg)
+ if (tmp->type == record_end)
record_insn_num--;
else if (tmp->type == record_reg)
xfree (tmp->u.reg.val);
rec->prev = NULL;
rec->next = NULL;
rec->type = record_end;
+ rec->u.end.sigval = TARGET_SIGNAL_0;
record_arch_list_add (rec);
record_list_release (record_arch_list_tail);
}
+struct record_message_args {
+ struct regcache *regcache;
+ enum target_signal signal;
+};
+
static int
record_message (void *args)
{
int ret;
- struct regcache *regcache = args;
+ struct record_message_args *myargs = args;
+ struct gdbarch *gdbarch = get_regcache_arch (myargs->regcache);
struct cleanup *old_cleanups = make_cleanup (record_message_cleanups, 0);
record_arch_list_head = NULL;
/* Check record_insn_num. */
record_check_insn_num (1);
- ret = gdbarch_process_record (get_regcache_arch (regcache),
- regcache,
- regcache_read_pc (regcache));
+ /* If gdb sends a signal value to target_resume,
+ save it in the 'end' field of the previous instruction.
+
+ Maybe process record should record what really happened,
+ rather than what gdb pretends has happened.
+
+ So if Linux delivered the signal to the child process during
+ the record mode, we will record it and deliver it again in
+ the replay mode.
+
+ If user says "ignore this signal" during the record mode, then
+ it will be ignored again during the replay mode (no matter if
+ the user says something different, like "deliver this signal"
+ during the replay mode).
+
+ User should understand that nothing he does during the replay
+ mode will change the behavior of the child. If he tries,
+ then that is a user error.
+
+ But we should still deliver the signal to gdb during the replay,
+ if we delivered it during the recording. Therefore we should
+ record the signal during record_wait, not record_resume. */
+ if (record_list != &record_first) /* FIXME better way to check */
+ {
+ gdb_assert (record_list->type == record_end);
+ record_list->u.end.sigval = myargs->signal;
+ }
+
+ if (myargs->signal == TARGET_SIGNAL_0
+ || !gdbarch_process_record_signal_p (gdbarch))
+ ret = gdbarch_process_record (gdbarch,
+ myargs->regcache,
+ regcache_read_pc (myargs->regcache));
+ else
+ ret = gdbarch_process_record_signal (gdbarch,
+ myargs->regcache,
+ myargs->signal);
+
if (ret > 0)
error (_("Process record: inferior program stopped."));
if (ret < 0)
}
static int
-do_record_message (struct regcache *regcache)
+do_record_message (struct regcache *regcache,
+ enum target_signal signal)
{
- return catch_errors (record_message, regcache, NULL, RETURN_MASK_ALL);
+ struct record_message_args args;
+
+ args.regcache = regcache;
+ args.signal = signal;
+ return catch_errors (record_message, &args, NULL, RETURN_MASK_ALL);
}
/* Set to 1 if record_store_registers and record_xfer_partial
/* Check if record target is already running. */
if (current_target.to_stratum == record_stratum)
- {
- if (!nquery
- (_("Process record target already running, do you want to delete "
- "the old record log?")))
- return;
- }
+ error (_("Process record target already running. Use \"record stop\" to "
+ "stop record target first."));
/*Reset the beneath function pointers. */
record_beneath_to_resume = NULL;
}
static int record_resume_step = 0;
-static enum target_signal record_resume_siggnal;
static int record_resume_error;
static void
record_resume (struct target_ops *ops, ptid_t ptid, int step,
- enum target_signal siggnal)
+ enum target_signal signal)
{
record_resume_step = step;
- record_resume_siggnal = siggnal;
if (!RECORD_IS_REPLAY)
{
- if (do_record_message (get_current_regcache ()))
+ if (do_record_message (get_current_regcache (), signal))
{
record_resume_error = 0;
}
return;
}
record_beneath_to_resume (record_beneath_to_resume_ops, ptid, 1,
- siggnal);
+ signal);
}
}
ret = record_beneath_to_wait (record_beneath_to_wait_ops,
ptid, status, options);
+ /* Is this a SIGTRAP? */
if (status->kind == TARGET_WAITKIND_STOPPED
&& status->value.sig == TARGET_SIGNAL_TRAP)
{
- /* Check if there is a breakpoint. */
+ /* Yes -- check if there is a breakpoint. */
registers_changed ();
tmp_pc = regcache_read_pc (get_current_regcache ());
if (breakpoint_inserted_here_p (tmp_pc))
{
- /* There is a breakpoint. */
+ /* There is a breakpoint. GDB will want to stop. */
CORE_ADDR decr_pc_after_break =
gdbarch_decr_pc_after_break
(get_regcache_arch (get_current_regcache ()));
}
else
{
- /* There is not a breakpoint. */
- if (!do_record_message (get_current_regcache ()))
+ /* There is not a breakpoint, and gdb is not
+ stepping, therefore gdb will not stop.
+ Therefore we will not return to gdb.
+ Record the insn and resume. */
+ if (!do_record_message (get_current_regcache (),
+ TARGET_SIGNAL_0))
{
break;
}
record_beneath_to_resume (record_beneath_to_resume_ops,
ptid, 1,
- record_resume_siggnal);
+ TARGET_SIGNAL_0);
continue;
}
}
gdbarch_decr_pc_after_break (gdbarch));
continue_flag = 0;
}
+ /* Check target signal */
+ if (record_list->u.end.sigval != TARGET_SIGNAL_0)
+ /* FIXME: better way to check */
+ continue_flag = 0;
}
}
replay_out:
if (record_get_sig)
status->value.sig = TARGET_SIGNAL_INT;
+ else if (record_list->u.end.sigval != TARGET_SIGNAL_0)
+ /* FIXME: better way to check */
+ status->value.sig = record_list->u.end.sigval;
else
status->value.sig = TARGET_SIGNAL_TRAP;
/* Let user choose if he wants to write register or not. */
if (regno < 0)
n =
- nquery (_("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?"));
+ 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 =
- nquery (_("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?"),
+ 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 (RECORD_IS_REPLAY)
{
/* Let user choose if he wants to write memory or not. */
- if (!nquery (_("Because GDB is in replay mode, writing to memory "
- "will make the execution log unusable from this "
- "point onward. Write memory at address %s?"),
+ 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."));
{
if (current_target.to_stratum == record_stratum)
{
- if (!record_list || !from_tty || query (_("Delete recorded log and "
- "stop recording?")))
- unpush_target (&record_ops);
+ unpush_target (&record_ops);
+ printf_unfiltered (_("Process record is stoped and all execution "
+ "log is deleted.\n"));
}
else
printf_unfiltered (_("Process record is not started.\n"));
/* Record instructions number limit command. */
add_setshow_boolean_cmd ("stop-at-limit", no_class,
- &record_stop_at_limit, _("\
+ &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\
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."),
- NULL, NULL,
- &set_record_cmdlist, &show_record_cmdlist);
+ NULL, NULL,
+ &set_record_cmdlist, &show_record_cmdlist);
add_setshow_zinteger_cmd ("insn-number-max", no_class,
&record_insn_max_num,
_("Set record/replay buffer limit."),
set_record_insn_max_num,
NULL, &set_record_cmdlist, &show_record_cmdlist);
add_cmd ("insn-number", class_obscure, show_record_insn_number,
- _("Show the current number of instructions in the "
- "record/replay buffer."), &info_record_cmdlist);
+ _("Show the current number of instructions in the "
+ "record/replay buffer."), &info_record_cmdlist);
}