From a474d7c2a38f227e95b863f621856ecf1570cc02 Mon Sep 17 00:00:00 2001 From: Pedro Alves Date: Wed, 9 Jul 2008 22:30:46 +0000 Subject: [PATCH] Per-thread commands. * gdbthread.h: Remove unneeded forward declarations. Include "inferior.h". (struct thread_info): Add continuations, intermediate_continuations, proceed_to_finish, step_over_calls, stop_step, step_multi and stop_signal members. (save_infrun_state): Add continuations, intermediate_continuations, proceed_to_finish, step_over_calls, stop_step, step_multi, stop_signal and stop_bpstat parameters. (load_infrun_state): Add continuations, intermediate_continuations, proceed_to_finish, step_over_calls, stop_step, step_multi, stop_signal and stop_bpstat parameters. * thread.c (load_infrun_state): In non-stop mode, load continuations, intermediate_continuations, proceed_to_finish, step_over_calls, stop_step, step_multi and stop_signal. (save_infrun_state): Store continuations, intermediate_continuations, proceed_to_finish, step_over_calls, stop_step, step_multi, stop_signal and stop_bpstat. (save_infrun_state): Store continuations, intermediate_continuations, proceed_to_finish, step_over_calls, stop_step, step_multi, stop_signal and stop_bpstat. (free_thread): Clear The thread's stop_bpstat. * inferior.h (context_switch_to): Declare. * infrun.c (ecss): New global. (context_switch): Context switch continuations, intermediate_continuations, proceed_to_finish, step_over_calls, stop_step, step_multi, stop_signal and stop_bpstat. (wait_for_inferior): Use global ecss. (async_ecss, async_ecs): Delete. (fetch_inferior_event): Use global ecss. (context_switch_to): New. * top.c (execute_command): In non-stop, only check if the current thread is running, in all-stop, check if there's any thread running. * breakpoint.c (bpstat_remove_breakpoint): New. (bpstat_remove_breakpoint_callback): New. (delete_breakpoint): Clear the stop_bpstats of all threads. * mi/mi-main.c (mi_cmd_execute): In non-stop, only check if the current thread is running, in all-stop, check if there's any thread running. * Makefile.in (gdbthread_h): Depend on $(inferior_h). --- gdb/ChangeLog | 53 ++++++++++++++++++++++++++++++++++++++ gdb/Makefile.in | 2 +- gdb/breakpoint.c | 36 ++++++++++++++++++++------ gdb/gdbthread.h | 43 ++++++++++++++++++++++++------- gdb/inferior.h | 2 ++ gdb/infrun.c | 67 ++++++++++++++++++++++++++++++++---------------- gdb/mi/mi-main.c | 3 ++- gdb/thread.c | 56 ++++++++++++++++++++++++++++++++++++++-- gdb/top.c | 3 ++- 9 files changed, 221 insertions(+), 44 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 1b368739fd5..e4ead4c63ba 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,56 @@ +2008-07-09 Pedro Alves + Vladimir Prus + + Per-thread commands. + + * gdbthread.h: Remove unneeded forward declarations. + Include "inferior.h". + (struct thread_info): Add continuations, + intermediate_continuations, proceed_to_finish, step_over_calls, + stop_step, step_multi and stop_signal members. + (save_infrun_state): Add continuations, + intermediate_continuations, proceed_to_finish, step_over_calls, + stop_step, step_multi, stop_signal and stop_bpstat parameters. + (load_infrun_state): Add continuations, + intermediate_continuations, proceed_to_finish, step_over_calls, + stop_step, step_multi, stop_signal and stop_bpstat parameters. + + * thread.c (load_infrun_state): In non-stop mode, load + continuations, intermediate_continuations, proceed_to_finish, + step_over_calls, stop_step, step_multi and stop_signal. + (save_infrun_state): Store continuations, + intermediate_continuations, proceed_to_finish, step_over_calls, + stop_step, step_multi, stop_signal and stop_bpstat. + (save_infrun_state): Store continuations, + intermediate_continuations, proceed_to_finish, step_over_calls, + stop_step, step_multi, stop_signal and stop_bpstat. + (free_thread): Clear The thread's stop_bpstat. + + * inferior.h (context_switch_to): Declare. + + * infrun.c (ecss): New global. + (context_switch): Context switch continuations, + intermediate_continuations, proceed_to_finish, step_over_calls, + stop_step, step_multi, stop_signal and stop_bpstat. + (wait_for_inferior): Use global ecss. + (async_ecss, async_ecs): Delete. + (fetch_inferior_event): Use global ecss. + (context_switch_to): New. + + * top.c (execute_command): In non-stop, only check if the current + thread is running, in all-stop, check if there's any thread + running. + + * breakpoint.c (bpstat_remove_breakpoint): New. + (bpstat_remove_breakpoint_callback): New. + (delete_breakpoint): Clear the stop_bpstats of all threads. + + * mi/mi-main.c (mi_cmd_execute): In non-stop, only check if the + current thread is running, in all-stop, check if there's any + thread running. + + * Makefile.in (gdbthread_h): Depend on $(inferior_h). + 2008-07-09 Pedro Alves Add non_stop global. diff --git a/gdb/Makefile.in b/gdb/Makefile.in index f12d3858967..d041e30fc73 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -800,7 +800,7 @@ gdb_stabs_h = gdb-stabs.h gdb_stat_h = gdb_stat.h gdb_string_h = gdb_string.h gdb_thread_db_h = gdb_thread_db.h -gdbthread_h = gdbthread.h $(breakpoint_h) $(frame_h) $(ui_out_h) +gdbthread_h = gdbthread.h $(breakpoint_h) $(frame_h) $(ui_out_h) $(inferior_h) gdbtypes_h = gdbtypes.h $(hashtab_h) gdb_vfork_h = gdb_vfork.h gdb_wait_h = gdb_wait.h diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index 3020de21145..ed99ca7778a 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -7168,6 +7168,29 @@ update_global_location_list_nothrow (int inserting) update_global_location_list (inserting); } +/* Clear BPT from a BPS. */ +static void +bpstat_remove_breakpoint (bpstat bps, struct breakpoint *bpt) +{ + bpstat bs; + for (bs = bps; bs; bs = bs->next) + if (bs->breakpoint_at && bs->breakpoint_at->owner == bpt) + { + bs->breakpoint_at = NULL; + bs->old_val = NULL; + /* bs->commands will be freed later. */ + } +} + +/* Callback for iterate_over_threads. */ +static int +bpstat_remove_breakpoint_callback (struct thread_info *th, void *data) +{ + struct breakpoint *bpt = data; + bpstat_remove_breakpoint (th->stop_bpstat, bpt); + return 0; +} + /* Delete a breakpoint and clean up all traces of it in the data structures. */ @@ -7175,7 +7198,6 @@ void delete_breakpoint (struct breakpoint *bpt) { struct breakpoint *b; - bpstat bs; struct bp_location *loc, *next; gdb_assert (bpt != NULL); @@ -7239,13 +7261,11 @@ delete_breakpoint (struct breakpoint *bpt) bpstat_do_actions (&stop_bpstat); in event-top.c won't do anything, and temporary breakpoints with commands won't work. */ - for (bs = stop_bpstat; bs; bs = bs->next) - if (bs->breakpoint_at && bs->breakpoint_at->owner == bpt) - { - bs->breakpoint_at = NULL; - bs->old_val = NULL; - /* bs->commands will be freed later. */ - } + + /* Clear the current context. */ + bpstat_remove_breakpoint (stop_bpstat, bpt); + /* And from all threads. */ + iterate_over_threads (bpstat_remove_breakpoint_callback, bpt); /* Now that breakpoint is removed from breakpoint list, update the global location list. This diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h index 389b2fc24fc..76b8f6cb539 100644 --- a/gdb/gdbthread.h +++ b/gdb/gdbthread.h @@ -22,17 +22,12 @@ #ifndef GDBTHREAD_H #define GDBTHREAD_H -struct breakpoint; -struct frame_id; struct symtab; -/* For bpstat */ #include "breakpoint.h" - -/* For struct frame_id. */ #include "frame.h" - #include "ui-out.h" +#include "inferior.h" struct thread_info { @@ -82,6 +77,20 @@ struct thread_info when we finally do stop stepping. */ bpstat stepping_through_solib_catchpoints; + /* The below are only per-thread in non-stop mode. */ + /* Per-thread command support. */ + struct continuation *continuations; + struct continuation *intermediate_continuations; + int proceed_to_finish; + enum step_over_calls_kind step_over_calls; + int stop_step; + int step_multi; + + enum target_signal stop_signal; + /* Used in continue_command to set the proceed count of the + breakpoint the thread stopped at. */ + bpstat stop_bpstat; + /* Private data used by the target vector implementation. */ struct private_thread_info *private; }; @@ -152,7 +161,15 @@ extern void save_infrun_state (ptid_t ptid, int stepping_through_solib_after_catch, bpstat stepping_through_solib_catchpoints, int current_line, - struct symtab *current_symtab); + struct symtab *current_symtab, + struct continuation *continuations, + struct continuation *intermediate_continuations, + int proceed_to_finish, + enum step_over_calls_kind step_over_calls, + int stop_step, + int step_multi, + enum target_signal stop_signal, + bpstat stop_bpstat); /* infrun context switch: load the debugger state previously saved for the given thread. */ @@ -164,10 +181,18 @@ extern void load_infrun_state (ptid_t ptid, CORE_ADDR *step_range_end, struct frame_id *step_frame_id, int *another_trap, - int *stepping_through_solib_affter_catch, + int *stepping_through_solib_after_catch, bpstat *stepping_through_solib_catchpoints, int *current_line, - struct symtab **current_symtab); + struct symtab **current_symtab, + struct continuation **continuations, + struct continuation **intermediate_continuations, + int *proceed_to_finish, + enum step_over_calls_kind *step_over_calls, + int *stop_step, + int *step_multi, + enum target_signal *stop_signal, + bpstat *stop_bpstat); /* Switch from one thread to another. */ extern void switch_to_thread (ptid_t ptid); diff --git a/gdb/inferior.h b/gdb/inferior.h index 9cbf4b4f188..e89fb74b978 100644 --- a/gdb/inferior.h +++ b/gdb/inferior.h @@ -132,6 +132,8 @@ extern void clear_proceed_status (void); extern void proceed (CORE_ADDR, enum target_signal, int); +extern ptid_t context_switch_to (ptid_t ptid); + /* When set, stop the 'step' command if we enter a function which has no line number information. The normal behavior is that we step over such function. */ diff --git a/gdb/infrun.c b/gdb/infrun.c index d9eca9fe1c7..457c0a0da0c 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -288,6 +288,8 @@ static struct breakpoint *step_resume_breakpoint = NULL; static ptid_t target_last_wait_ptid; static struct target_waitstatus target_last_waitstatus; +struct execution_control_state ecss; + /* This is used to remember when a fork, vfork or exec event was caught by a catchpoint, and thus the event is to be followed at the next resume of the inferior, and not @@ -1429,7 +1431,6 @@ void wait_for_inferior (int treat_exec_as_sigtrap) { struct cleanup *old_cleanups; - struct execution_control_state ecss; struct execution_control_state *ecs; if (debug_infrun) @@ -1440,8 +1441,6 @@ wait_for_inferior (int treat_exec_as_sigtrap) old_cleanups = make_cleanup (delete_step_resume_breakpoint, &step_resume_breakpoint); - /* wfi still stays in a loop, so it's OK just to take the address of - a local to get the ecs pointer. */ ecs = &ecss; /* Fill in with reasonable starting values. */ @@ -1487,25 +1486,20 @@ wait_for_inferior (int treat_exec_as_sigtrap) event loop whenever a change of state is detected on the file descriptor corresponding to the target. It can be called more than once to complete a single execution command. In such cases we need - to keep the state in a global variable ASYNC_ECSS. If it is the - last time that this function is called for a single execution - command, then report to the user that the inferior has stopped, and - do the necessary cleanups. */ - -struct execution_control_state async_ecss; -struct execution_control_state *async_ecs; + to keep the state in a global variable ECSS. If it is the last time + that this function is called for a single execution command, then + report to the user that the inferior has stopped, and do the + necessary cleanups. */ void fetch_inferior_event (void *client_data) { - static struct cleanup *old_cleanups; - - async_ecs = &async_ecss; + struct execution_control_state *ecs = &ecss; - if (!async_ecs->wait_some_more) + if (!ecs->wait_some_more) { /* Fill in with reasonable starting values. */ - init_execution_control_state (async_ecs); + init_execution_control_state (ecs); /* We'll update this if & when we switch to a new thread. */ previous_inferior_ptid = inferior_ptid; @@ -1522,15 +1516,15 @@ fetch_inferior_event (void *client_data) } if (deprecated_target_wait_hook) - async_ecs->ptid = - deprecated_target_wait_hook (async_ecs->waiton_ptid, async_ecs->wp); + ecs->ptid = + deprecated_target_wait_hook (ecs->waiton_ptid, ecs->wp); else - async_ecs->ptid = target_wait (async_ecs->waiton_ptid, async_ecs->wp); + ecs->ptid = target_wait (ecs->waiton_ptid, ecs->wp); /* Now figure out what to do with the result of the result. */ - handle_inferior_event (async_ecs); + handle_inferior_event (ecs); - if (!async_ecs->wait_some_more) + if (!ecs->wait_some_more) { delete_step_resume_breakpoint (&step_resume_breakpoint); @@ -1608,7 +1602,14 @@ context_switch (struct execution_control_state *ecs) ecs->stepping_over_breakpoint, ecs->stepping_through_solib_after_catch, ecs->stepping_through_solib_catchpoints, - ecs->current_line, ecs->current_symtab); + ecs->current_line, ecs->current_symtab, + cmd_continuation, intermediate_continuation, + proceed_to_finish, + step_over_calls, + stop_step, + step_multi, + stop_signal, + stop_bpstat); /* Load infrun state for the new thread. */ load_infrun_state (ecs->ptid, &prev_pc, @@ -1618,12 +1619,34 @@ context_switch (struct execution_control_state *ecs) &ecs->stepping_over_breakpoint, &ecs->stepping_through_solib_after_catch, &ecs->stepping_through_solib_catchpoints, - &ecs->current_line, &ecs->current_symtab); + &ecs->current_line, &ecs->current_symtab, + &cmd_continuation, &intermediate_continuation, + &proceed_to_finish, + &step_over_calls, + &stop_step, + &step_multi, + &stop_signal, + &stop_bpstat); } switch_to_thread (ecs->ptid); } +/* Context switch to thread PTID. */ +ptid_t +context_switch_to (ptid_t ptid) +{ + ptid_t current_ptid = inferior_ptid; + + /* Context switch to the new thread. */ + if (!ptid_equal (ptid, inferior_ptid)) + { + ecss.ptid = ptid; + context_switch (&ecss); + } + return current_ptid; +} + static void adjust_pc_after_break (struct execution_control_state *ecs) { diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c index 5318a4a19b8..06e9d63b0e4 100644 --- a/gdb/mi/mi-main.c +++ b/gdb/mi/mi-main.c @@ -1059,7 +1059,8 @@ mi_cmd_execute (struct mi_parse *parse) if (parse->cmd->argv_func != NULL) { - if (is_running (inferior_ptid)) + if ((!non_stop && any_running ()) + || (non_stop && is_running (inferior_ptid))) { if (strcmp (parse->command, "exec-interrupt")) { diff --git a/gdb/thread.c b/gdb/thread.c index 9bd5f04d23b..e2f8cd7f343 100644 --- a/gdb/thread.c +++ b/gdb/thread.c @@ -93,6 +93,8 @@ free_thread (struct thread_info *tp) if (tp->step_resume_breakpoint) tp->step_resume_breakpoint->disposition = disp_del_at_next_stop; + bpstat_clear (&tp->stop_bpstat); + /* FIXME: do I ever need to call the back-end to give it a chance at this private data before deleting the thread? */ if (tp->private) @@ -358,7 +360,15 @@ load_infrun_state (ptid_t ptid, int *stepping_through_solib_after_catch, bpstat *stepping_through_solib_catchpoints, int *current_line, - struct symtab **current_symtab) + struct symtab **current_symtab, + struct continuation **continuations, + struct continuation **intermediate_continuations, + int *proceed_to_finish, + enum step_over_calls_kind *step_over_calls, + int *stop_step, + int *step_multi, + enum target_signal *stop_signal, + bpstat *stop_bpstat) { struct thread_info *tp; @@ -381,6 +391,26 @@ load_infrun_state (ptid_t ptid, tp->stepping_through_solib_catchpoints; *current_line = tp->current_line; *current_symtab = tp->current_symtab; + + /* In all-stop mode, these are global state, while in non-stop mode, + they are per thread. */ + if (non_stop) + { + *continuations = tp->continuations; + tp->continuations = NULL; + *intermediate_continuations = tp->intermediate_continuations; + tp->intermediate_continuations = NULL; + *proceed_to_finish = tp->proceed_to_finish; + *step_over_calls = tp->step_over_calls; + *stop_step = tp->stop_step; + *step_multi = tp->step_multi; + *stop_signal = tp->stop_signal; + + /* Swap instead of copy, so we only have to update one of + them. */ + *stop_bpstat = tp->stop_bpstat; + tp->stop_bpstat = 0; + } } /* Save infrun state for the thread PID. */ @@ -397,7 +427,15 @@ save_infrun_state (ptid_t ptid, int stepping_through_solib_after_catch, bpstat stepping_through_solib_catchpoints, int current_line, - struct symtab *current_symtab) + struct symtab *current_symtab, + struct continuation *continuations, + struct continuation *intermediate_continuations, + int proceed_to_finish, + enum step_over_calls_kind step_over_calls, + int stop_step, + int step_multi, + enum target_signal stop_signal, + bpstat stop_bpstat) { struct thread_info *tp; @@ -418,6 +456,20 @@ save_infrun_state (ptid_t ptid, tp->stepping_through_solib_catchpoints = stepping_through_solib_catchpoints; tp->current_line = current_line; tp->current_symtab = current_symtab; + + /* In all-stop mode, these are global state, while in non-stop mode, + they are per thread. */ + if (non_stop) + { + tp->continuations = continuations; + tp->intermediate_continuations = intermediate_continuations; + tp->proceed_to_finish = proceed_to_finish; + tp->step_over_calls = step_over_calls; + tp->stop_step = stop_step; + tp->step_multi = step_multi; + tp->stop_signal = stop_signal; + tp->stop_bpstat = stop_bpstat; + } } /* Return true if TP is an active thread. */ diff --git a/gdb/top.c b/gdb/top.c index 84894346697..fe4b1d0aebd 100644 --- a/gdb/top.c +++ b/gdb/top.c @@ -418,7 +418,8 @@ execute_command (char *p, int from_tty) /* If the target is running, we allow only a limited set of commands. */ if (target_can_async_p () - && any_running () + && ((!non_stop && any_running ()) + || (non_stop && is_running (inferior_ptid))) && !get_cmd_async_ok (c)) error (_("Cannot execute this command while the target is running.")); -- 2.30.2