{
char *name = exec_file_find (execd_pathname, NULL);
- execd_pathname = alloca (strlen (name) + 1);
+ execd_pathname = (char *) alloca (strlen (name) + 1);
strcpy (execd_pathname, name);
xfree (name);
}
static void
displaced_step_clear_cleanup (void *arg)
{
- struct displaced_step_inferior_state *state = arg;
+ struct displaced_step_inferior_state *state
+ = (struct displaced_step_inferior_state *) arg;
displaced_step_clear (state);
}
len = gdbarch_max_insn_length (gdbarch);
/* Save the original contents of the copy area. */
- displaced->step_saved_copy = xmalloc (len);
+ displaced->step_saved_copy = (gdb_byte *) xmalloc (len);
ignore_cleanups = make_cleanup (free_current_contents,
&displaced->step_saved_copy);
status = target_read_memory (copy, displaced->step_saved_copy, len);
static const char schedlock_off[] = "off";
static const char schedlock_on[] = "on";
static const char schedlock_step[] = "step";
+static const char schedlock_replay[] = "replay";
static const char *const scheduler_enums[] = {
schedlock_off,
schedlock_on,
schedlock_step,
+ schedlock_replay,
NULL
};
-static const char *scheduler_mode = schedlock_off;
+static const char *scheduler_mode = schedlock_replay;
static void
show_scheduler_mode (struct ui_file *file, int from_tty,
struct cmd_list_element *c, const char *value)
resume. */
resume_ptid = inferior_ptid;
}
+ else if ((scheduler_mode == schedlock_replay)
+ && target_record_will_replay (minus_one_ptid, execution_direction))
+ {
+ /* User-settable 'scheduler' mode requires solo thread resume in replay
+ mode. */
+ resume_ptid = inferior_ptid;
+ }
else if (!sched_multi && target_supports_multi_process ())
{
/* Resume all threads of the current process (and none of other
\f
/* Proceeding. */
+/* See infrun.h. */
+
+/* Counter that tracks number of user visible stops. This can be used
+ to tell whether a command has proceeded the inferior past the
+ current location. This allows e.g., inferior function calls in
+ breakpoint commands to not interrupt the command list. When the
+ call finishes successfully, the inferior is standing at the same
+ breakpoint as if nothing happened (and so we don't call
+ normal_stop). */
+static ULONGEST current_stop_id;
+
+/* See infrun.h. */
+
+ULONGEST
+get_stop_id (void)
+{
+ return current_stop_id;
+}
+
+/* Called when we report a user visible stop. */
+
+static void
+new_stop_id (void)
+{
+ current_stop_id++;
+}
+
/* Clear out all variables saying what to do when inferior is continued.
First do this, then set the ones you want, then call `proceed'. */
void
clear_proceed_status (int step)
{
+ /* With scheduler-locking replay, stop replaying other threads if we're
+ not replaying the user-visible resume ptid.
+
+ This is a convenience feature to not require the user to explicitly
+ stop replaying the other threads. We're assuming that the user's
+ intent is to resume tracing the recorded process. */
+ if (!non_stop && scheduler_mode == schedlock_replay
+ && target_record_is_replaying (minus_one_ptid)
+ && !target_record_will_replay (user_visible_resume_ptid (step),
+ execution_direction))
+ target_record_stop_replaying ();
+
if (!non_stop)
{
struct thread_info *tp;
{
return (scheduler_mode == schedlock_on
|| (scheduler_mode == schedlock_step
- && tp->control.stepping_command));
+ && tp->control.stepping_command)
+ || (scheduler_mode == schedlock_replay
+ && target_record_will_replay (minus_one_ptid,
+ execution_direction)));
}
/* Basic routine for continuing the program in various fashions.
if (should_notify_stop)
{
+ int proceeded = 0;
+
/* We may not find an inferior if this was a process exit. */
if (inf == NULL || inf->control.stop_soon == NO_STOP_QUIETLY)
- normal_stop ();
+ proceeded = normal_stop ();
- inferior_event_handler (INF_EXEC_COMPLETE, NULL);
- cmd_done = 1;
+ if (!proceeded)
+ {
+ inferior_event_handler (INF_EXEC_COMPLETE, NULL);
+ cmd_done = 1;
+ }
}
}
}
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_NO_HISTORY\n");
/* Reverse execution: target ran out of history info. */
+ /* Switch to the stopped thread. */
+ if (!ptid_equal (ecs->ptid, inferior_ptid))
+ context_switch (ecs->ptid);
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog, "infrun: stopped\n");
+
delete_just_stopped_threads_single_step_breakpoints ();
- stop_pc = regcache_read_pc (get_thread_regcache (ecs->ptid));
+ stop_pc = regcache_read_pc (get_thread_regcache (inferior_ptid));
observer_notify_no_history ();
stop_waiting (ecs);
return;
static void
restore_current_uiout_cleanup (void *arg)
{
- struct ui_out *saved_uiout = arg;
+ struct ui_out *saved_uiout = (struct ui_out *) arg;
current_uiout = saved_uiout;
}
}
}
-/* Here to return control to GDB when the inferior stops for real.
- Print appropriate messages, remove breakpoints, give terminal our modes.
+/* The execution context that just caused a normal stop. */
- STOP_PRINT_FRAME nonzero means print the executing frame
- (pc, function, args, file, line number and line text).
- BREAKPOINTS_FAILED nonzero means stop was due to error
- attempting to insert breakpoints. */
+struct stop_context
+{
+ /* The stop ID. */
+ ULONGEST stop_id;
-void
+ /* The event PTID. */
+
+ ptid_t ptid;
+
+ /* If stopp for a thread event, this is the thread that caused the
+ stop. */
+ struct thread_info *thread;
+
+ /* The inferior that caused the stop. */
+ int inf_num;
+};
+
+/* Returns a new stop context. If stopped for a thread event, this
+ takes a strong reference to the thread. */
+
+static struct stop_context *
+save_stop_context (void)
+{
+ struct stop_context *sc = XNEW (struct stop_context);
+
+ sc->stop_id = get_stop_id ();
+ sc->ptid = inferior_ptid;
+ sc->inf_num = current_inferior ()->num;
+
+ if (!ptid_equal (inferior_ptid, null_ptid))
+ {
+ /* Take a strong reference so that the thread can't be deleted
+ yet. */
+ sc->thread = inferior_thread ();
+ sc->thread->refcount++;
+ }
+ else
+ sc->thread = NULL;
+
+ return sc;
+}
+
+/* Release a stop context previously created with save_stop_context.
+ Releases the strong reference to the thread as well. */
+
+static void
+release_stop_context_cleanup (void *arg)
+{
+ struct stop_context *sc = (struct stop_context *) arg;
+
+ if (sc->thread != NULL)
+ sc->thread->refcount--;
+ xfree (sc);
+}
+
+/* Return true if the current context no longer matches the saved stop
+ context. */
+
+static int
+stop_context_changed (struct stop_context *prev)
+{
+ if (!ptid_equal (prev->ptid, inferior_ptid))
+ return 1;
+ if (prev->inf_num != current_inferior ()->num)
+ return 1;
+ if (prev->thread != NULL && prev->thread->state != THREAD_STOPPED)
+ return 1;
+ if (get_stop_id () != prev->stop_id)
+ return 1;
+ return 0;
+}
+
+/* See infrun.h. */
+
+int
normal_stop (void)
{
struct target_waitstatus last;
get_last_target_status (&last_ptid, &last);
+ new_stop_id ();
+
/* If an exception is thrown from this point on, make sure to
propagate GDB's knowledge of the executing state to the
frontend/user running state. A QUIT is an easy exception to see
/* Look up the hook_stop and run it (CLI internally handles problem
of stop_command's pre-hook not existing). */
- if (stop_command)
- catch_errors (hook_stop_stub, stop_command,
- "Error while running hook_stop:\n", RETURN_MASK_ALL);
+ if (stop_command != NULL)
+ {
+ struct stop_context *saved_context = save_stop_context ();
+ struct cleanup *old_chain
+ = make_cleanup (release_stop_context_cleanup, saved_context);
+
+ catch_errors (hook_stop_stub, stop_command,
+ "Error while running hook_stop:\n", RETURN_MASK_ALL);
+
+ /* If the stop hook resumes the target, then there's no point in
+ trying to notify about the previous stop; its context is
+ gone. Likewise if the command switches thread or inferior --
+ the observers would print a stop for the wrong
+ thread/inferior. */
+ if (stop_context_changed (saved_context))
+ {
+ do_cleanups (old_chain);
+ return 1;
+ }
+ do_cleanups (old_chain);
+ }
/* Notify observers about the stop. This is where the interpreters
print the stop event. */
longer needed. Keeping those around slows down things linearly.
Note that this never removes the current inferior. */
prune_inferiors ();
+
+ return 0;
}
static int
size_t len = TYPE_LENGTH (type);
struct cleanup *back_to;
- siginfo_data = xmalloc (len);
+ siginfo_data = (gdb_byte *) xmalloc (len);
back_to = make_cleanup (xfree, siginfo_data);
if (target_read (¤t_target, TARGET_OBJECT_SIGNAL_INFO, NULL,
static void
do_restore_infcall_suspend_state_cleanup (void *state)
{
- restore_infcall_suspend_state (state);
+ restore_infcall_suspend_state ((struct infcall_suspend_state *) state);
}
struct cleanup *
static void
do_restore_infcall_control_state_cleanup (void *sts)
{
- restore_infcall_control_state (sts);
+ restore_infcall_control_state ((struct infcall_control_state *) sts);
}
struct cleanup *
static void
restore_inferior_ptid (void *arg)
{
- ptid_t *saved_ptid_ptr = arg;
+ ptid_t *saved_ptid_ptr = (ptid_t *) arg;
inferior_ptid = *saved_ptid_ptr;
xfree (arg);
scheduler_enums, &scheduler_mode, _("\
Set mode for locking scheduler during execution."), _("\
Show mode for locking scheduler during execution."), _("\
-off == no locking (threads may preempt at any time)\n\
-on == full locking (no thread except the current thread may run)\n\
-step == scheduler locked during stepping commands (step, next, stepi, nexti).\n\
- In this mode, other threads may run during other commands."),
+off == no locking (threads may preempt at any time)\n\
+on == full locking (no thread except the current thread may run)\n\
+ This applies to both normal execution and replay mode.\n\
+step == scheduler locked during stepping commands (step, next, stepi, nexti).\n\
+ In this mode, other threads may run during other commands.\n\
+ This applies to both normal execution and replay mode.\n\
+replay == scheduler locked in replay mode and unlocked during normal execution."),
set_schedlock_func, /* traps on target vector */
show_scheduler_mode,
&setlist, &showlist);