From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 20532 invoked by alias); 28 Nov 2011 15:46:08 -0000 Received: (qmail 20390 invoked by uid 22791); 28 Nov 2011 15:45:51 -0000 X-SWARE-Spam-Status: Yes, hits=6.5 required=5.0 tests=AWL,BAYES_50,BOTNET,FROM_12LTRDOM,KAM_STOCKGEN,RDNS_DYNAMIC,TW_FC,TW_GJ X-Spam-Check-By: sourceware.org Received: from bl22-166-20.dsl.telepac.pt (HELO localhost6.localdomain6) (2.83.166.20) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Mon, 28 Nov 2011 15:45:31 +0000 Received: from localhost6.localdomain6 (localhost.localdomain [127.0.0.1]) by localhost6.localdomain6 (8.14.4/8.14.4/Debian-2ubuntu1) with ESMTP id pASFdrll018072 for ; Mon, 28 Nov 2011 15:39:53 GMT Subject: [RFC/WIP PATCH 11/14] Add I/T set support to most execution commands To: gdb-patches@sourceware.org From: Pedro Alves Date: Mon, 28 Nov 2011 15:46:00 -0000 Message-ID: <20111128153953.17761.62637.stgit@localhost6.localdomain6> In-Reply-To: <20111128153742.17761.21459.stgit@localhost6.localdomain6> References: <20111128153742.17761.21459.stgit@localhost6.localdomain6> User-Agent: StGit/0.15 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2011-11/txt/msg00774.txt.bz2 This adds I/T set support to most execution commands. For execution commands, there are actually two sets in effect: - The "apply set": This is the set of threads the execution command is applied to. For example, for "step", the apply set is the set of threads that will be stepped; for "finish", the apply set is the set of threads that will the finished. - The "run free set": This is the set of threads that is allowed to run free while the execution command is applied to the all threads in the "apply set". The run free set is derived from the current set (as set by itfocus, or by the [SET] prefix), and to remain backwards compatible, from "set scheduler-locking on/step", "set schedule-multiple" and "set non-stop on" too. Example, if the current set is [all] (all threads), by default the run free set is the [all] set too. But if either scheduler-locking is on/step or "set non-stop" is on, the run free set is set to the empty set, meaning no thread is let run free. With these two sets, a user that is not aware of GDBs new capabilities, does not notice any user interface change. For example, by default, "(gdb) step" will still step the current thread, and let all other threads in the current inferior run free. The pointer them comes from being able to override these new sets to do more complicated commands. For example, making use of the %ITSET% prompt substitution patch, $ gdb -ex "set prompt %ITSET%> " ~/gdb/tests/threads [all]> start Temporary breakpoint 1 at 0x40068c: file threads.c, line 35. Starting program: /home/pedro/gdb/tests/threads [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Temporary breakpoint 1, main () at threads.c:35 35 long i = 0; [all]> info threads Id Target Id Frame 3 Thread 0x7ffff7028700 (LWP 22403) "threads" 0x00007ffff7bcc78e in __lll_lock_wait_private () from /lib/x86_64-linux-gnu/libpthread.so.0 * 2 Thread 0x7ffff7829700 (LWP 22402) "threads" thread_function0 (arg=0x0) at threads.c:63 1 Thread 0x7ffff7fcb720 (LWP 22323) "threads" 0x00007ffff7910011 in clone () from /lib/x86_64-linux-gnu/libc.so.6 [all]> itfocus [1.*] warning: 3 threads for inferior 1 in the current i/t set, switching to first New focus: Current inferior is 1. [1.*]> info threads Id Target Id Frame * 3 Thread 0x7ffff7028700 (LWP 22403) "threads" 0x00007ffff7bcc78e in __lll_lock_wait_private () from /lib/x86_64-linux-gnu/libpthread.so.0 2 Thread 0x7ffff7829700 (LWP 22402) "threads" thread_function0 (arg=0x0) at threads.c:63 1 Thread 0x7ffff7fcb720 (LWP 22323) "threads" 0x00007ffff7910011 in clone () from /lib/x86_64-linux-gnu/libc.so.6 [1.*]> step This last "step" commands steps thread 3 (the apply set, derived from the selected thread), and, lets all other threads of inferior 1 run free (derived from the current focus). You can step more than one thread in parallel. For that, you pass an explicit apply set as argument to the execution command. For example: [1.*]> step [.1, .2] That steps threads 1 and 2, and lets all other threads in the current focus run free. Currently each of thread in the apply set step independently and unaware of other threads in the same apply set. That is, say, thread 1 finishes stepping first, and we'll give back the prompt to the user immediately, while thread 2 continues stepping in the background (as if "thread 1; step&; thread 2; step&" had been issued). Instead, I think it'd make sense to wait for all threads in the apply set to finish what they were told to do before considering the whole command done with, and giving the prompt back to the user. If you want to step all threads in the current focus in parallel, although you could, you don't need to spell out the current focus set as the apply set explicitly as in [1.*]> step [1.*] Instead, you can pass the -a (from "all") flag to the execution command, as in: [1.*]> step -a That is, this makes the apply set be equal to the current set, thus stepping all threads in parallel. The "run free set" is still the current set, but, naturally, there are no threads left to run free. Note you can make use of the [] prefix command to temporarily switch the current set, so you can for example have defined a set named [workers], and then step them all with: [1.*]> [workers] step -a Often you'll want to step only a given thread or set of threads, and not let any other thread run free while stepping. That is, run a step as if "set scheduler-locking" was step or on. You can do that already with what I've presented above, with e.g., [1.*]> [1.2] step [1.2] That is, the [1.2] prefix sets the current set temporarily to thread 2, and the apply set is also set to thread 2. Or, you may more simply do: [1.*]> [] step [1.2] That is, the [] prefix sets the current set temporarily to the empty set, and the apply set is thread 2. Or, you may also do: [1.*]> [] step where the current set is set to the empty set, and the apply set is derived from the current thread. I've added yet a third option (-l, from lock) to get the same explicit locking. E.g., if the current thread is thread 2, then these are equivalent: [1.2]> step [1.*]> [1.2] step [1.*]> [] step [1.*]> step -l they all step thread 2, with scheduler locking on. Lastly, it'd be useful to have a way to override the global "set scheduler-locking on/step" or "set non-stop on" options. That is done with the "-c" (from current set) option. Maybe that that instead be "-u" for unlocked? So this: [all]> set scheduler-locking on [all]> step steps the currently selected thread, and doesn't let any other thread run free. And this: [all]> set scheduler-locking on [all]> step -c steps the currently selected thread, and let's all threads in [all] run free. That is, it's the equivalent of: [all]> set scheduler-locking off [all]> step [all]> set scheduler-locking on Lastly, all these switches are currently implemented in the following commands: step, next, stepi, nexti, continue, until, jump, signal, finish and interrupt. You'll note that several of the commands' implementations have split in two passes - the first pass loops over all threads in the apply set, validating if everything is okay to apply the command, thus avoiding most cases of resuming thread 1..3, and then later throwing an error resuming thread N. A lot is missing: - This whole series is not regression free yet. - "step [set]" should wait for all thread in the set to finish the step. - make sure that we're not regressing against a target that doesn't support non-stop/async. I haven't tried that yet. - MI support. - We're always resuming all threads individually. This is not really a problem for native targets, but if you're debugging 1000 threads against the remote target, and you do a "s", we'll send 1 vCont;sTID packet, plus 999 vCont;cTID packets, instead of a single vCont;sTID;c". We could make remote_resume not really resume, but collect resume requests in a vector, and add a new target_apply_resume method that actually applies all pending resume requests, so we could compress the requests. Native targets would still resume from within target_resume, and would implement target_apply_resume as doing nothing. The trouble is in knowing whether the 999 continue requests do indeed map to "all threads", and so can be compressed. vCont;c is not really the same as resuming all threads individually. The former means that we'll get the pending events for threads we hadn't heard yet too. This needs a bit more thought and experimentation. I'm not sure I'll be able to get this far in this first round though, so I may end up leaving all this native only at first somehow. - Probably many other things I'm not remembering right now. --- gdb/breakpoint.c | 166 ++++++-- gdb/gdbthread.h | 5 gdb/infcall.c | 23 + gdb/infcmd.c | 831 +++++++++++++++++++++++++++++++-------- gdb/inferior.h | 7 gdb/infrun.c | 100 +++-- gdb/itset.c | 8 gdb/itset.h | 2 gdb/testsuite/gdb.base/jump.exp | 2 gdb/thread.c | 2 10 files changed, 899 insertions(+), 247 deletions(-) diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index 75badf9..d642651 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -9799,64 +9799,149 @@ until_break_command_continuation (void *arg, int err) delete_longjmp_breakpoint (a->thread_num); } +struct until_break_cmd_data +{ + struct symtab_and_line sal; + struct frame_id breakpoint_frame_id; + struct frame_id selected_frame_id; +}; + +struct until_break_aec_callback_data +{ + int from_tty; +}; + +static void until_break_aec_callback (struct thread_info *thread, void *data); + +static void +itset_free_p (void *arg) +{ + struct itset **itset_p = arg; + + if (*itset_p) + itset_free (*itset_p); +} + +char *parse_execution_args (char *args, int step, + struct itset **apply_itset, + struct itset **run_free_itset); + +typedef void (*aec_callback_func) (struct thread_info *thr, void *data); +void apply_execution_command (struct itset *apply_itset, + struct itset *run_free_itset, + aec_callback_func callback, void *callback_data); + +void ensure_runnable (struct thread_info *thr); + void until_break_command (char *arg, int from_tty, int anywhere) { - struct symtabs_and_lines sals; - struct symtab_and_line sal; - struct frame_info *frame = get_selected_frame (NULL); - struct breakpoint *breakpoint; - struct breakpoint *breakpoint2 = NULL; + struct itset *apply_itset = NULL; + struct itset *run_free_itset = NULL; + struct thread_info *thr; struct cleanup *old_chain; - int thread; - struct thread_info *tp; + int thr_count = 0; + struct until_break_aec_callback_data cb_data; - clear_proceed_status (); + old_chain = make_cleanup (itset_free_p, &apply_itset); + make_cleanup (itset_free_p, &run_free_itset); - /* Set a breakpoint where the user wants it and at return from - this function. */ + arg = parse_execution_args (arg, 0, &apply_itset, &run_free_itset); - if (last_displayed_sal_is_valid ()) - sals = decode_line_1 (&arg, 1, - get_last_displayed_symtab (), - get_last_displayed_line (), - NULL); - else - sals = decode_line_1 (&arg, 1, (struct symtab *) NULL, 0, NULL); + ALL_THREADS (thr) + if (itset_contains_thread (apply_itset, thr)) + { + struct symtabs_and_lines sals; + struct symtab_and_line sal; + struct frame_info *frame; + struct frame_id breakpoint_frame_id; + struct until_break_cmd_data *cmd_data; - if (sals.nelts != 1) - error (_("Couldn't get information on specified line.")); + ++thr_count; - sal = sals.sals[0]; - xfree (sals.sals); /* malloc'd, so freed. */ + ensure_runnable (thr); - if (*arg) - error (_("Junk at end of arguments.")); + if (!ptid_equal (inferior_ptid, thr->ptid)) + switch_to_thread (thr->ptid); - resolve_sal_pc (&sal); + frame = get_selected_frame (NULL); - if (anywhere) - /* If the user told us to continue until a specified location, - we don't specify a frame at which we need to stop. */ - breakpoint = set_momentary_breakpoint (get_frame_arch (frame), sal, - null_frame_id, bp_until); - else - /* Otherwise, specify the selected frame, because we want to stop - only at the very same frame. */ - breakpoint = set_momentary_breakpoint (get_frame_arch (frame), sal, - get_stack_frame_id (frame), - bp_until); + /* Set a breakpoint where the user wants it and at return from + this function. */ + + if (last_displayed_sal_is_valid ()) + sals = decode_line_1 (&arg, 1, + get_last_displayed_symtab (), + get_last_displayed_line (), + NULL); + else + sals = decode_line_1 (&arg, 1, (struct symtab *) NULL, 0, NULL); + + if (sals.nelts != 1) + error (_("Couldn't get information on specified line.")); + + sal = sals.sals[0]; + xfree (sals.sals); /* malloc'd, so freed. */ + + if (*arg) + error (_("Junk at end of arguments.")); + + resolve_sal_pc (&sal); + + if (anywhere) + /* If the user told us to continue until a specified + location, we don't specify a frame at which we need to + stop. */ + breakpoint_frame_id = null_frame_id; + else + /* Otherwise, specify the selected frame, because we want to + stop only at the very same frame. */ + breakpoint_frame_id = get_stack_frame_id (frame); + + cmd_data = XNEW (struct until_break_cmd_data); + cmd_data->sal = sal; + cmd_data->breakpoint_frame_id = breakpoint_frame_id; + cmd_data->selected_frame_id = get_frame_id (frame); + thr->cmd_data = cmd_data; + } + + cb_data.from_tty = from_tty; + apply_execution_command (apply_itset, run_free_itset, + until_break_aec_callback, NULL); + + do_cleanups (old_chain); +} + +static void +until_break_aec_callback (struct thread_info *thread, void *data) +{ + struct breakpoint *breakpoint; + struct breakpoint *breakpoint2 = NULL; + int thread_num; + struct until_break_aec_callback_data *d = data; + struct frame_info *frame; + struct until_break_cmd_data *cmd_data = thread->cmd_data; + int ix; + struct cleanup *old_chain; + frame = frame_find_by_id (cmd_data->selected_frame_id); + select_frame (frame); + + breakpoint = set_momentary_breakpoint (get_frame_arch (frame), + cmd_data->sal, + cmd_data->breakpoint_frame_id, + bp_until); old_chain = make_cleanup_delete_breakpoint (breakpoint); - tp = inferior_thread (); - thread = tp->num; + thread_num = thread->num; /* Keep within the current frame, or in frames called by the current one. */ if (frame_id_p (frame_unwind_caller_id (frame))) { + struct symtab_and_line sal; + sal = find_pc_line (frame_unwind_caller_pc (frame), 0); sal.pc = frame_unwind_caller_pc (frame); breakpoint2 = set_momentary_breakpoint (frame_unwind_caller_arch (frame), @@ -9865,10 +9950,11 @@ until_break_command (char *arg, int from_tty, int anywhere) bp_until); make_cleanup_delete_breakpoint (breakpoint2); - set_longjmp_breakpoint (tp, frame_unwind_caller_id (frame)); - make_cleanup (delete_longjmp_breakpoint_cleanup, &thread); + set_longjmp_breakpoint (thread, frame_unwind_caller_id (frame)); + make_cleanup (delete_longjmp_breakpoint_cleanup, &thread_num); } + clear_proceed_status (); proceed (-1, TARGET_SIGNAL_DEFAULT, 0); /* If we are running asynchronously, and proceed call above has @@ -9883,7 +9969,7 @@ until_break_command (char *arg, int from_tty, int anywhere) args->breakpoint = breakpoint; args->breakpoint2 = breakpoint2; - args->thread_num = thread; + args->thread_num = thread_num; discard_cleanups (old_chain); add_continuation (inferior_thread (), diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h index 0135219..7598f6a 100644 --- a/gdb/gdbthread.h +++ b/gdb/gdbthread.h @@ -188,6 +188,8 @@ struct thread_info call. See `struct thread_suspend_state'. */ struct thread_suspend_state suspend; + int reported_event; + int current_line; struct symtab *current_symtab; @@ -241,6 +243,9 @@ struct thread_info which exceptions to intercept. */ struct frame_id initiating_frame; + /* Data used by the execution command in effect. */ + void *cmd_data; + /* Private data used by the target vector implementation. */ struct private_thread_info *private; diff --git a/gdb/infcall.c b/gdb/infcall.c index 50f1289..7149db4 100644 --- a/gdb/infcall.c +++ b/gdb/infcall.c @@ -38,6 +38,14 @@ #include "ada-lang.h" #include "gdbthread.h" #include "exceptions.h" +#include "itset.h" + +typedef void (*aec_callback_func) (struct thread_info *thr, void *data); +void apply_execution_command (struct itset *apply_itset, + struct itset *run_free_itset, + aec_callback_func callback, void *callback_data); + +struct itset *default_run_free_itset (struct itset *apply_itset, int step); /* If we can't find a function's name from its address, we print this instead. */ @@ -413,6 +421,21 @@ run_inferior_call (struct thread_info *call_thread, CORE_ADDR real_pc) { proceed (real_pc, TARGET_SIGNAL_0, 0); + if (target_is_non_stop_p ()) + { + struct itset *apply_itset = itset_create_empty (); + struct itset *run_free_itset + = default_run_free_itset (apply_itset, 0); + + apply_execution_command (apply_itset, current_itset, + NULL, NULL); + + itset_free (apply_itset); + itset_free (run_free_itset); + + switch_to_thread (call_thread->ptid); + } + /* Inferior function calls are always synchronous, even if the target supports asynchronous execution. Do here what `proceed' itself does in sync mode. */ diff --git a/gdb/infcmd.c b/gdb/infcmd.c index 7b935fe..e22c87f 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -57,6 +57,102 @@ #include "tracepoint.h" #include "inf-loop.h" #include "continuations.h" +#include "itset.h" +#include "cli/cli-utils.h" + + +struct itset * +current_thread_set (void) +{ + struct itset *set; + struct inferior *inf; + struct thread_info *tp; + char *b; + + inf = current_inferior (); + tp = inferior_thread (); + + b = alloca (256); + sprintf (b, "[%d.%d]", inf->num, tp->num); + return itset_create (&b); +} + +void do_target_resume (ptid_t ptid, int step, enum target_signal signo); + +typedef void (*aec_callback_func) (struct thread_info *thr, void *data); + +int follow_fork (int should_resume); + +void +apply_execution_command (struct itset *apply_itset, + struct itset *run_free_itset, + aec_callback_func callback, void *callback_data) +{ + if (target_is_non_stop_p ()) + { + struct thread_info *t; + int followed_fork = 0; + + /* See if there are threads we'd run free that are stopped at + forks. If so, follow the fork, and refuse to apply the + execution command further. */ + ALL_THREADS (t) + { + if (t->state == THREAD_STOPPED + && itset_contains_thread (run_free_itset, t) + && !itset_contains_thread (apply_itset, t)) + { + if (t->suspend.waitstatus.kind == TARGET_WAITKIND_FORKED + || t->suspend.waitstatus.kind == TARGET_WAITKIND_VFORKED) + { + switch_to_thread (t->ptid); + follow_fork (0); + followed_fork = 1; + } + } + } + + if (followed_fork) + { + normal_stop (); + if (target_can_async_p ()) + inferior_event_handler (INF_EXEC_COMPLETE, NULL); + return; + } + + ALL_THREADS (t) + { + if (t->state == THREAD_STOPPED + && itset_contains_thread (apply_itset, t)) + { + switch_to_thread (t->ptid); + (*callback) (t, callback_data); + } + else if (t->state == THREAD_STOPPED + && itset_contains_thread (run_free_itset, t)) + { + /* If T has reported an event before (rather than having + been forced-suspended by GDB, then have it step over + any breakpoint its stopped at. Otherwise, resume it + as is, and let it hit any breakpoint it may be + stopped at (or report it's already pending event), so + the event is reported. */ + if (t->reported_event) + { + switch_to_thread (t->ptid); + clear_proceed_status_thread (t); + proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 0); + } + else + do_target_resume (t->ptid, 0, TARGET_SIGNAL_0); + } + } + } + else + { + (*callback) (inferior_thread (), callback_data); + } +} /* Functions exported for general use, in inferior.h: */ @@ -79,7 +175,7 @@ static void nofp_registers_info (char *, int); static void print_return_value (struct type *func_type, struct type *value_type); -static void until_next_command (int); +static void until_next_command (char *, int); static void until_command (char *, int); @@ -596,7 +692,7 @@ run_command_1 (char *args, int from_tty, int tbreak_at_main) events --- the frontend shouldn't see them as stopped. In all-stop, always finish the state of all threads, as we may be resuming more than just the new process. */ - if (non_stop) + if (target_is_non_stop_p ()) ptid = pid_to_ptid (ptid_get_pid (inferior_ptid)); else ptid = minus_one_ptid; @@ -656,11 +752,11 @@ proceed_thread_callback (struct thread_info *thread, void *arg) much. If/when GDB gains a way to tell the target `hold this thread stopped until I say otherwise', then we can optimize this. */ - if (!is_stopped (thread->ptid)) + if (thread->state != THREAD_STOPPED) return 0; switch_to_thread (thread->ptid); - clear_proceed_status (); + clear_proceed_status_thread (thread); proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 0); return 0; } @@ -713,23 +809,182 @@ continue_1 (int all_threads) } } +static void +itset_free_p (void *arg) +{ + struct itset **itset_p = arg; + + if (*itset_p) + itset_free (*itset_p); +} + +struct itset * +default_run_free_itset (struct itset *apply_itset, int step) +{ + if (non_stop) + { + /* In non-stop mode, threads are always handled + individually. */ + return itset_create_empty (); + } + else if (scheduler_mode == schedlock_on + || (scheduler_mode == schedlock_step && step)) + { + /* User-settable 'scheduler' mode requires solo thread + resume. */ + return itset_create_empty (); + } + else if (!sched_multi) + { + struct inferior *inf; + char *set_spec; + char *p; + int first = 1; + struct itset *set; + + /* Resume only threads of the current inferior process. */ + set_spec = xstrdup ("["); + ALL_INFERIORS (inf) + if (itset_contains_inferior (apply_itset, inf)) + { + char buf[128]; + + if (first) + { + first = 0; + sprintf (buf, "%d.*", inf->num); + } + else + sprintf (buf, ",%d.*", inf->num); + + set_spec = reconcat (set_spec, set_spec, buf, (char *) NULL); + } + set_spec = reconcat (set_spec, set_spec, "]", (char *) NULL); + + p = set_spec; + set = itset_create (&p); + xfree (set_spec); + return set; + } + else + { + /* By default, resume all threads in the current set. */ + return itset_reference (current_itset); + } +} + +char * +parse_execution_args (char *args, int step, + struct itset **apply_itset, + struct itset **run_free_itset) +{ + if (args != NULL) + { + while (*args) + { + char *p; + + args = skip_spaces (args); + p = skip_to_space (args); + + if (strncmp (args, "-a", p - args) == 0) + { + if (*apply_itset) + itset_free (*apply_itset); + *apply_itset = itset_reference (current_itset); + args = p; + } + else if (strncmp (args, "-c", p - args) == 0) + { + if (*run_free_itset) + itset_free (*run_free_itset); + *run_free_itset = itset_reference (current_itset); + args = p; + } + else if (strncmp (args, "-l", p - args) == 0) + { + if (*run_free_itset) + itset_free (*run_free_itset); + *run_free_itset = itset_create_empty (); + args = p; + } + else if (strcmp (args, "--") == 0) + { + args += 2; + break; + } + else + break; + } + + args = skip_spaces (args); + + if (*args == '[') + { + if (*apply_itset) + itset_free (*apply_itset); + *apply_itset = itset_create (&args); + args = skip_spaces (args); + } + } + + if (*apply_itset == NULL) + *apply_itset = current_thread_set (); + + if (*run_free_itset == NULL) + *run_free_itset = default_run_free_itset (*apply_itset, step); + + if (args && *args == '\0') + return NULL; + else + return args; +} + +static void +continue_aec_callback (struct thread_info *thread, void *data) +{ + proceed_thread_callback (thread, NULL); +} + /* continue [-a] [proceed-count] [&] */ void continue_command (char *args, int from_tty) { int async_exec = 0; int all_threads = 0; + int ignore_count = 0; + int ignore_count_p = 0; + struct itset *apply_itset = NULL; + struct itset *run_free_itset = NULL; + struct cleanup *old_chain; ERROR_NO_INFERIOR; /* Find out whether we must run in the background. */ if (args != NULL) async_exec = strip_bg_char (&args); + old_chain = make_cleanup (itset_free_p, &apply_itset); + make_cleanup (itset_free_p, &run_free_itset); + + args = parse_execution_args (args, 0, &apply_itset, &run_free_itset); + if (args) + { + args = skip_spaces (args); + if (*args != '\0') + { + ignore_count = parse_and_eval_long (args); + ignore_count_p = 1; + } + } + /* If we must run in the background, but the target can't do it, error out. */ if (async_exec && !target_can_async_p ()) error (_("Asynchronous execution not supported on this target.")); + if (itset_is_empty (apply_itset)) + error (_("Set of threads to continue is empty.")); + /* If we are not asked to run in the bg, then prepare to run in the foreground, synchronously. */ if (!async_exec && target_can_async_p ()) @@ -738,17 +993,6 @@ continue_command (char *args, int from_tty) async_disable_stdin (); } - if (args != NULL) - { - if (strncmp (args, "-a", sizeof ("-a") - 1) == 0) - { - all_threads = 1; - args += sizeof ("-a") - 1; - if (*args == '\0') - args = NULL; - } - } - if (!non_stop && all_threads) error (_("`-a' is meaningless in all-stop mode.")); @@ -756,9 +1000,9 @@ continue_command (char *args, int from_tty) error (_("Can't resume all threads and specify " "proceed count simultaneously.")); - /* If we have an argument left, set proceed count of breakpoint we - stopped at. */ - if (args != NULL) + /* Set proceed count of breakpoint we stopped at, if the user + requested it. */ + if (ignore_count_p) { bpstat bs = NULL; int num, stat; @@ -781,9 +1025,7 @@ continue_command (char *args, int from_tty) while ((stat = bpstat_num (&bs, &num)) != 0) if (stat > 0) { - set_ignore_count (num, - parse_and_eval_long (args) - 1, - from_tty); + set_ignore_count (num, ignore_count - 1, from_tty); /* set_ignore_count prints a message ending with a period. So print two spaces before "Continuing.". */ if (from_tty) @@ -801,7 +1043,10 @@ continue_command (char *args, int from_tty) if (from_tty) printf_filtered (_("Continuing.\n")); - continue_1 (all_threads); + apply_execution_command (apply_itset, run_free_itset, + continue_aec_callback, NULL); + + do_cleanups (old_chain); } /* Record the starting point of a "step" or "next" command. */ @@ -852,36 +1097,106 @@ delete_longjmp_breakpoint_cleanup (void *arg) delete_longjmp_breakpoint (thread); } +struct step_1_args +{ + int count; + int skip_subroutines; + int single_inst; + int thread; +}; + +static void step_1_1 (int skip_subroutines, int single_inst, int count); + static void -step_1 (int skip_subroutines, int single_inst, char *count_string) +step_1_aec_callback (struct thread_info *thread, void *data) +{ + struct step_1_args *args = data; + + switch_to_thread (thread->ptid); + step_1_1 (args->skip_subroutines, args->single_inst, args->count); +} + +void +ensure_runnable (struct thread_info *thr) +{ + if (thr->state == THREAD_EXITED) + error (_("Thread %d (%s) has exited."), + thr->num, target_pid_to_str (thr->ptid)); + else if (thr->state == THREAD_EXITED) + error (_("Thread %d (%s) is running."), + thr->num, target_pid_to_str (thr->ptid)); +} + +static void +step_1 (int skip_subroutines, int single_inst, char *args) { int count = 1; - struct cleanup *cleanups = make_cleanup (null_cleanup, NULL); int async_exec = 0; - int thread = -1; + struct cleanup *old_chain; + struct itset *apply_itset = NULL; + struct itset *run_free_itset = NULL; + struct step_1_args step_args; + struct thread_info *thr; + int thr_count = 0; ERROR_NO_INFERIOR; ensure_not_tfind_mode (); - ensure_valid_thread (); - ensure_not_running (); - if (count_string) - async_exec = strip_bg_char (&count_string); + if (args) + async_exec = strip_bg_char (&args); + + old_chain = make_cleanup (itset_free_p, &apply_itset); + make_cleanup (itset_free_p, &run_free_itset); + + args = parse_execution_args (args, 1, &apply_itset, &run_free_itset); + if (args) + { + args = skip_spaces (args); + if (*args != '\0') + count = parse_and_eval_long (args); + } /* If we get a request for running in the bg but the target doesn't support it, error out. */ if (async_exec && !target_can_async_p ()) error (_("Asynchronous execution not supported on this target.")); + ALL_THREADS (thr) + if (itset_contains_thread (apply_itset, thr)) + { + ++thr_count; + + ensure_runnable (thr); + } + + if (thr_count == 0) + error (_("Set of threads to step is empty.")); + /* If we don't get a request of running in the bg, then we need to simulate synchronous (fg) execution. */ + /* FIXME: should only do this is actually about to resume. */ if (!async_exec && target_can_async_p ()) { /* Simulate synchronous execution. */ async_disable_stdin (); } - count = count_string ? parse_and_eval_long (count_string) : 1; + step_args.skip_subroutines = skip_subroutines; + step_args.single_inst = single_inst; + step_args.count = count; + step_args.thread = -1; + + apply_execution_command (apply_itset, run_free_itset, + step_1_aec_callback, &step_args); + + do_cleanups (old_chain); +} + +static void +step_1_1 (int skip_subroutines, int single_inst, int count) +{ + int thread = -1; + struct cleanup *cleanups = make_cleanup (null_cleanup, NULL); if (!single_inst || skip_subroutines) /* Leave si command alone. */ { @@ -935,14 +1250,6 @@ step_1 (int skip_subroutines, int single_inst, char *count_string) } } -struct step_1_continuation_args -{ - int count; - int skip_subroutines; - int single_inst; - int thread; -}; - /* Called after we are done with one step operation, to check whether we need to step again, before we print the prompt and return control to the user. If count is > 1, we will need to do one more call to @@ -951,7 +1258,7 @@ struct step_1_continuation_args static void step_1_continuation (void *args, int err) { - struct step_1_continuation_args *a = args; + struct step_1_args *a = args; if (target_has_execution) { @@ -1072,7 +1379,7 @@ step_once (int skip_subroutines, int single_inst, int count, int thread) further stepping. */ if (target_can_async_p ()) { - struct step_1_continuation_args *args; + struct step_1_args *args; args = xmalloc (sizeof (*args)); args->skip_subroutines = skip_subroutines; @@ -1086,23 +1393,53 @@ step_once (int skip_subroutines, int single_inst, int count, int thread) } + +struct jump_cmd_data +{ + CORE_ADDR addr; +}; + +struct jump_aec_callback_data +{ + int from_tty; +}; + +static void +jump_aec_callback (struct thread_info *thread, void *data) +{ + struct jump_aec_callback_data *arg = data; + struct jump_map_entry *jme; + int ix; + struct gdbarch *gdbarch = get_current_arch (); + int from_tty = arg->from_tty; + struct jump_cmd_data *cmd_data = thread->cmd_data; + CORE_ADDR addr = cmd_data->addr; + + if (from_tty) + printf_filtered (_("Continuing %d (%s) at %s.\n"), + thread->num, target_pid_to_str (thread->ptid), + paddress (gdbarch, addr)); + + clear_proceed_status (); + proceed (addr, TARGET_SIGNAL_0, 0); + return; +} + /* Continue program at specified address. */ static void jump_command (char *arg, int from_tty) { - struct gdbarch *gdbarch = get_current_arch (); - CORE_ADDR addr; - struct symtabs_and_lines sals; - struct symtab_and_line sal; - struct symbol *fn; - struct symbol *sfn; int async_exec = 0; + struct itset *apply_itset = NULL; + struct itset *run_free_itset = NULL; + struct cleanup *old_chain; + struct thread_info *thr; + int thr_count = 0; + struct jump_aec_callback_data cb_data; ERROR_NO_INFERIOR; ensure_not_tfind_mode (); - ensure_valid_thread (); - ensure_not_running (); /* Find out whether we must run in the background. */ if (arg != NULL) @@ -1113,59 +1450,71 @@ jump_command (char *arg, int from_tty) if (async_exec && !target_can_async_p ()) error (_("Asynchronous execution not supported on this target.")); + old_chain = make_cleanup (itset_free_p, &apply_itset); + make_cleanup (itset_free_p, &run_free_itset); + + arg = parse_execution_args (arg, 0, &apply_itset, &run_free_itset); if (!arg) error_no_arg (_("starting address")); - sals = decode_line_spec_1 (arg, 1); - if (sals.nelts != 1) - { - error (_("Unreasonable jump request")); - } + ALL_THREADS (thr) + if (itset_contains_thread (apply_itset, thr)) + { + struct symtabs_and_lines sals; + struct symtab_and_line sal; + struct symbol *fn; + struct symbol *sfn; + struct jump_cmd_data *cmd_data; - sal = sals.sals[0]; - xfree (sals.sals); + ++thr_count; - if (sal.symtab == 0 && sal.pc == 0) - error (_("No source file has been specified.")); + ensure_runnable (thr); - resolve_sal_pc (&sal); /* May error out. */ + if (!ptid_equal (inferior_ptid, thr->ptid)) + switch_to_thread (thr->ptid); - /* See if we are trying to jump to another function. */ - fn = get_frame_function (get_current_frame ()); - sfn = find_pc_function (sal.pc); - if (fn != NULL && sfn != fn) - { - if (!query (_("Line %d is not in `%s'. Jump anyway? "), sal.line, - SYMBOL_PRINT_NAME (fn))) - { - error (_("Not confirmed.")); - /* NOTREACHED */ - } - } + sals = decode_line_spec_1 (arg, 1); + if (sals.nelts != 1) + error (_("Unreasonable jump request for thread %s"), + target_pid_to_str (thr->ptid)); - if (sfn != NULL) - { - fixup_symbol_section (sfn, 0); - if (section_is_overlay (SYMBOL_OBJ_SECTION (sfn)) && - !section_is_mapped (SYMBOL_OBJ_SECTION (sfn))) - { - if (!query (_("WARNING!!! Destination is in " - "unmapped overlay! Jump anyway? "))) - { + sal = sals.sals[0]; + xfree (sals.sals); + + if (sal.symtab == 0 && sal.pc == 0) + error (_("No source file has been specified.")); + + resolve_sal_pc (&sal); /* May error out. */ + + /* See if we are trying to jump to another function. */ + fn = get_frame_function (get_current_frame ()); + sfn = find_pc_function (sal.pc); + if (fn != NULL && sfn != fn) + { + if (!query (_("Line %d is not in `%s'. Jump anyway? "), sal.line, + SYMBOL_PRINT_NAME (fn))) error (_("Not confirmed.")); - /* NOTREACHED */ - } - } - } + } - addr = sal.pc; + if (sfn != NULL) + { + fixup_symbol_section (sfn, 0); + if (section_is_overlay (SYMBOL_OBJ_SECTION (sfn)) && + !section_is_mapped (SYMBOL_OBJ_SECTION (sfn))) + { + if (!query (_("WARNING!!! Destination is in " + "unmapped overlay! Jump anyway? "))) + error (_("Not confirmed.")); + } + } - if (from_tty) - { - printf_filtered (_("Continuing at ")); - fputs_filtered (paddress (gdbarch, addr), gdb_stdout); - printf_filtered (".\n"); - } + cmd_data = XNEW (struct jump_cmd_data); + cmd_data->addr = sal.pc; + thr->cmd_data = cmd_data; + } + + if (thr_count == 0) + error (_("Set of threads to jump is empty.")); /* If we are not asked to run in the bg, then prepare to run in the foreground, synchronously. */ @@ -1175,8 +1524,11 @@ jump_command (char *arg, int from_tty) async_disable_stdin (); } - clear_proceed_status (); - proceed (addr, TARGET_SIGNAL_0, 0); + cb_data.from_tty = from_tty; + apply_execution_command (apply_itset, run_free_itset, + jump_aec_callback, &cb_data); + + do_cleanups (old_chain); } @@ -1196,27 +1548,43 @@ go_command (char *line_no, int from_tty) /* Continue program giving it specified signal. */ +struct signal_aec_callback_data +{ + enum target_signal oursig; +}; + +static void signal_aec_callback (struct thread_info *thread, void *data); + static void -signal_command (char *signum_exp, int from_tty) +signal_command (char *arg, int from_tty) { enum target_signal oursig; int async_exec = 0; + struct itset *apply_itset = NULL; + struct itset *run_free_itset = NULL; + struct cleanup *old_chain; + struct signal_aec_callback_data cb_data; + struct thread_info *thr; + int thr_count = 0; dont_repeat (); /* Too dangerous. */ ERROR_NO_INFERIOR; ensure_not_tfind_mode (); - ensure_valid_thread (); - ensure_not_running (); /* Find out whether we must run in the background. */ - if (signum_exp != NULL) - async_exec = strip_bg_char (&signum_exp); + if (arg != NULL) + async_exec = strip_bg_char (&arg); /* If we must run in the background, but the target can't do it, error out. */ if (async_exec && !target_can_async_p ()) error (_("Asynchronous execution not supported on this target.")); + old_chain = make_cleanup (itset_free_p, &apply_itset); + make_cleanup (itset_free_p, &run_free_itset); + + arg = parse_execution_args (arg, 0, &apply_itset, &run_free_itset); + /* If we are not asked to run in the bg, then prepare to run in the foreground, synchronously. */ if (!async_exec && target_can_async_p ()) @@ -1225,18 +1593,29 @@ signal_command (char *signum_exp, int from_tty) async_disable_stdin (); } - if (!signum_exp) + if (!arg) error_no_arg (_("signal number")); + ALL_THREADS (thr) + if (itset_contains_thread (apply_itset, thr)) + { + ++thr_count; + + ensure_runnable (thr); + } + + if (thr_count == 0) + error (_("Set of threads to signal is empty.")); + /* It would be even slicker to make signal names be valid expressions, (the type could be "enum $signal" or some such), then the user could assign them to convenience variables. */ - oursig = target_signal_from_name (signum_exp); + oursig = target_signal_from_name (arg); if (oursig == TARGET_SIGNAL_UNKNOWN) { /* No, try numeric. */ - int num = parse_and_eval_long (signum_exp); + int num = parse_and_eval_long (arg); if (num == 0) oursig = TARGET_SIGNAL_0; @@ -1253,8 +1632,20 @@ signal_command (char *signum_exp, int from_tty) target_signal_to_name (oursig)); } - clear_proceed_status (); - proceed ((CORE_ADDR) -1, oursig, 0); + cb_data.oursig = oursig; + apply_execution_command (apply_itset, run_free_itset, + signal_aec_callback, &cb_data); + + do_cleanups (old_chain); +} + +static void +signal_aec_callback (struct thread_info *thread, void *data) +{ + struct signal_aec_callback_data *d = data; + + clear_proceed_status_thread (thread); + proceed ((CORE_ADDR) -1, d->oursig, 0); } /* Continuation args to be passed to the "until" command @@ -1283,51 +1674,91 @@ until_next_continuation (void *arg, int err) we set. This may involve changes to wait_for_inferior and the proceed status code. */ +static void until_next_aec_callback (struct thread_info *thread, void *data); + static void -until_next_command (int from_tty) +until_next_command (char *arg, int from_tty) { - struct frame_info *frame; - CORE_ADDR pc; - struct symbol *func; - struct symtab_and_line sal; - struct thread_info *tp = inferior_thread (); - int thread = tp->num; + struct itset *apply_itset = NULL; + struct itset *run_free_itset = NULL; struct cleanup *old_chain; + struct thread_info *thr; + int thr_count = 0; - clear_proceed_status (); - set_step_frame (); + old_chain = make_cleanup (itset_free_p, &apply_itset); + make_cleanup (itset_free_p, &run_free_itset); - frame = get_current_frame (); + arg = parse_execution_args (arg, 0, &apply_itset, &run_free_itset); - /* Step until either exited from this function or greater - than the current line (if in symbolic section) or pc (if - not). */ + ALL_THREADS (thr) + if (itset_contains_thread (apply_itset, thr)) + { + struct frame_info *frame; + CORE_ADDR pc; + struct symbol *func; + struct symtab_and_line sal; - pc = get_frame_pc (frame); - func = find_pc_function (pc); + ++thr_count; - if (!func) - { - struct minimal_symbol *msymbol = lookup_minimal_symbol_by_pc (pc); + ensure_runnable (thr); - if (msymbol == NULL) - error (_("Execution is not within a known function.")); + if (!ptid_equal (inferior_ptid, thr->ptid)) + switch_to_thread (thr->ptid); - tp->control.step_range_start = SYMBOL_VALUE_ADDRESS (msymbol); - tp->control.step_range_end = pc; - } - else - { - sal = find_pc_line (pc, 0); + clear_proceed_status_thread (thr); + set_step_frame (); - tp->control.step_range_start = BLOCK_START (SYMBOL_BLOCK_VALUE (func)); - tp->control.step_range_end = sal.end; - } + frame = get_current_frame (); + + /* Step until either exited from this function or greater than + the current line (if in symbolic section) or pc (if + not). */ + + pc = get_frame_pc (frame); + func = find_pc_function (pc); + + if (!func) + { + struct minimal_symbol *msymbol = lookup_minimal_symbol_by_pc (pc); + + if (msymbol == NULL) + error (_("Execution is not within a known function.")); + + thr->control.step_range_start = SYMBOL_VALUE_ADDRESS (msymbol); + thr->control.step_range_end = pc; + } + else + { + sal = find_pc_line (pc, 0); + + thr->control.step_range_start = BLOCK_START (SYMBOL_BLOCK_VALUE (func)); + thr->control.step_range_end = sal.end; + } + + thr->control.step_over_calls = STEP_OVER_ALL; + + thr->step_multi = 0; /* Only one call to proceed */ + } - tp->control.step_over_calls = STEP_OVER_ALL; + if (thr_count == 0) + error (_("Set of threads to until is empty.")); - tp->step_multi = 0; /* Only one call to proceed */ + apply_execution_command (apply_itset, run_free_itset, + until_next_aec_callback, NULL); + do_cleanups (old_chain); +} + + + +static void +until_next_aec_callback (struct thread_info *tp, void *data) +{ + int thread = tp->num; + struct cleanup *old_chain; + struct frame_info *frame; + + frame = get_current_frame (); set_longjmp_breakpoint (tp, get_frame_id (frame)); old_chain = make_cleanup (delete_longjmp_breakpoint_cleanup, &thread); @@ -1354,8 +1785,6 @@ until_command (char *arg, int from_tty) ERROR_NO_INFERIOR; ensure_not_tfind_mode (); - ensure_valid_thread (); - ensure_not_running (); /* Find out whether we must run in the background. */ if (arg != NULL) @@ -1377,7 +1806,7 @@ until_command (char *arg, int from_tty) if (arg) until_break_command (arg, from_tty, 0); else - until_next_command (from_tty); + until_next_command (arg, from_tty); } static void @@ -1387,8 +1816,6 @@ advance_command (char *arg, int from_tty) ERROR_NO_INFERIOR; ensure_not_tfind_mode (); - ensure_valid_thread (); - ensure_not_running (); if (arg == NULL) error_no_arg (_("a location")); @@ -1648,21 +2075,34 @@ finish_forward (struct symbol *function, struct frame_info *frame) do_all_continuations (0); } +struct finish_cmd_data +{ + struct frame_id selected_frame_id; +}; + +struct finish_aec_callback_data +{ + int from_tty; +}; + +static void finish_aec_callback (struct thread_info *thread, void *data); + /* "finish": Set a temporary breakpoint at the place the selected frame will return to, then continue. */ static void finish_command (char *arg, int from_tty) { - struct frame_info *frame; - struct symbol *function; - + struct itset *apply_itset = NULL; + struct itset *run_free_itset = NULL; + struct cleanup *old_chain; + struct thread_info *thr; + int thr_count = 0; + struct finish_aec_callback_data cb_data; int async_exec = 0; ERROR_NO_INFERIOR; ensure_not_tfind_mode (); - ensure_valid_thread (); - ensure_not_running (); /* Find out whether we must run in the background. */ if (arg != NULL) @@ -1673,6 +2113,11 @@ finish_command (char *arg, int from_tty) if (async_exec && !target_can_async_p ()) error (_("Asynchronous execution not supported on this target.")); + old_chain = make_cleanup (itset_free_p, &apply_itset); + make_cleanup (itset_free_p, &run_free_itset); + + arg = parse_execution_args (arg, 0, &apply_itset, &run_free_itset); + /* If we are not asked to run in the bg, then prepare to run in the foreground, synchronously. */ if (!async_exec && target_can_async_p ()) @@ -1684,29 +2129,73 @@ finish_command (char *arg, int from_tty) if (arg) error (_("The \"finish\" command does not take any arguments.")); - frame = get_prev_frame (get_selected_frame (_("No selected frame."))); - if (frame == 0) - error (_("\"finish\" not meaningful in the outermost frame.")); + ALL_THREADS (thr) + if (itset_contains_thread (apply_itset, thr)) + { + struct frame_info *frame; + struct frame_info *prev; + struct finish_cmd_data *cmd_data; + + ++thr_count; + + ensure_runnable (thr); + + if (!ptid_equal (inferior_ptid, thr->ptid)) + switch_to_thread (thr->ptid); + + frame = get_selected_frame (_("No selected frame.")); + prev = get_prev_frame (frame); + if (prev == NULL) + error (_("\"finish\" not meaningful in the outermost frame.")); + + cmd_data = XNEW (struct finish_cmd_data); + cmd_data->selected_frame_id = get_frame_id (frame); + thr->cmd_data = cmd_data; + } + + if (thr_count == 0) + error (_("Set of threads to finish is empty.")); + + cb_data.from_tty = from_tty; + apply_execution_command (apply_itset, run_free_itset, + finish_aec_callback, &cb_data); + + do_cleanups (old_chain); +} + +static void +finish_aec_callback (struct thread_info *tp, void *data) +{ + struct finish_aec_callback_data *d = data; + int from_tty = d->from_tty; + struct finish_cmd_data *cmd_data = tp->cmd_data; + int ix; + struct frame_info *frame, *prev; + struct symbol *function; clear_proceed_status (); + frame = frame_find_by_id (cmd_data->selected_frame_id); + gdb_assert (frame != NULL); + select_frame (frame); + prev = get_prev_frame (frame); + gdb_assert (prev != NULL); + /* Finishing from an inline frame is completely different. We don't try to show the "return value" - no way to locate it. So we do not need a completion. */ - if (get_frame_type (get_selected_frame (_("No selected frame."))) - == INLINE_FRAME) + if (get_frame_type (frame) == INLINE_FRAME) { /* Claim we are stepping in the calling frame. An empty step range means that we will stop once we aren't in a function called by that frame. We don't use the magic "1" value for step_range_end, because then infrun will think this is nexti, and not step over the rest of this inlined function call. */ - struct thread_info *tp = inferior_thread (); struct symtab_and_line empty_sal; init_sal (&empty_sal); - set_step_info (frame, empty_sal); - tp->control.step_range_start = get_frame_pc (frame); + set_step_info (prev, empty_sal); + tp->control.step_range_start = get_frame_pc (prev); tp->control.step_range_end = tp->control.step_range_start; tp->control.step_over_calls = STEP_OVER_ALL; @@ -1715,7 +2204,7 @@ finish_command (char *arg, int from_tty) if (from_tty) { printf_filtered (_("Run till exit from ")); - print_stack_frame (get_selected_frame (NULL), 1, LOCATION); + print_stack_frame (frame, 1, LOCATION); } proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 1); @@ -1724,7 +2213,7 @@ finish_command (char *arg, int from_tty) /* Find the function we will return from. */ - function = find_pc_function (get_frame_pc (get_selected_frame (NULL))); + function = find_pc_function (get_frame_pc (frame)); /* Print info on the selected frame, including level number but not source. */ @@ -1735,13 +2224,13 @@ finish_command (char *arg, int from_tty) else printf_filtered (_("Run till exit from ")); - print_stack_frame (get_selected_frame (NULL), 1, LOCATION); + print_stack_frame (frame, 1, LOCATION); } if (execution_direction == EXEC_REVERSE) finish_backward (function); else - finish_forward (function, frame); + finish_forward (function, prev); } @@ -2693,10 +3182,31 @@ interrupt_target_1 (int all_threads) ptid_t ptid; if (all_threads) - ptid = minus_one_ptid; + { + if (target_is_non_stop_p ()) + { + struct thread_info *t; + + ALL_LIVE_THREADS (t) + if (itset_contains_thread (current_itset, t)) + { + target_stop (t->ptid); + set_stop_requested (t->ptid, 1); + } + + return; + } + else + { + ptid = minus_one_ptid; + target_stop (ptid); + } + } else - ptid = inferior_ptid; - target_stop (ptid); + { + ptid = inferior_ptid; + target_stop (ptid); + } /* Tag the thread as having been explicitly requested to stop, so other parts of gdb know not to resume this thread automatically, @@ -2704,7 +3214,7 @@ interrupt_target_1 (int all_threads) non-stop mode, as when debugging a multi-threaded application in all-stop mode, we will only get one stop event --- it's undefined which thread will report the event. */ - if (non_stop) + if (target_is_non_stop_p ()) set_stop_requested (ptid, 1); } @@ -2727,9 +3237,6 @@ interrupt_target_command (char *args, int from_tty) && strncmp (args, "-a", sizeof ("-a") - 1) == 0) all_threads = 1; - if (!non_stop && all_threads) - error (_("-a is meaningless in all-stop mode.")); - interrupt_target_1 (all_threads); } } diff --git a/gdb/inferior.h b/gdb/inferior.h index 559b4f1..add97e7 100644 --- a/gdb/inferior.h +++ b/gdb/inferior.h @@ -105,6 +105,8 @@ extern int sync_execution; extern void clear_proceed_status (void); +extern void clear_proceed_status_thread (struct thread_info *tp); + extern void proceed (CORE_ADDR, enum target_signal, int); extern int sched_multi; @@ -168,6 +170,11 @@ extern void resume (int, enum target_signal); extern ptid_t user_visible_resume_ptid (int step); +extern const char schedlock_off[]; +extern const char schedlock_on[]; +extern const char schedlock_step[]; +extern const char *scheduler_mode; + extern void insert_step_resume_breakpoint_at_sal (struct gdbarch *, struct symtab_and_line , struct frame_id); diff --git a/gdb/infrun.c b/gdb/infrun.c index c279508..6b16184 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -81,7 +81,7 @@ static int hook_stop_stub (void *); static int restore_selected_frame (void *); -static int follow_fork (void); +int follow_fork (int); static void set_schedlock_func (char *args, int from_tty, struct cmd_list_element *c); @@ -416,28 +416,14 @@ show_follow_fork_mode_string (struct ui_file *file, int from_tty, } -/* Tell the target to follow the fork we're stopped at. Returns true - if the inferior should be resumed; false, if the target for some - reason decided it's best not to resume. */ +/* Check if we lost stopped at a fork event, but switched over to + another thread since. If so, switch back to the event thread, and + return false. Otherwise, return true. */ static int -follow_fork (void) +prepare_to_follow_fork (void) { - int follow_child = (follow_fork_mode_string == follow_fork_mode_child); - int should_resume = 1; - struct thread_info *tp; - - /* Copy user stepping state to the new inferior thread. FIXME: the - followed fork child thread should have a copy of most of the - parent thread structure's run control related fields, not just these. - Initialized to avoid "may be used uninitialized" warnings from gcc. */ - struct breakpoint *step_resume_breakpoint = NULL; - struct breakpoint *exception_resume_breakpoint = NULL; - CORE_ADDR step_range_start = 0; - CORE_ADDR step_range_end = 0; - struct frame_id step_frame_id = { 0 }; - - if (!non_stop) + if (!target_is_non_stop_p ()) { ptid_t wait_ptid; struct target_waitstatus wait_status; @@ -461,10 +447,32 @@ follow_fork (void) afterwards refuse to resume, and inform the user what happened. */ switch_to_thread (wait_ptid); - should_resume = 0; + return 0; } } + return 1; +} + +/* Tell the target to follow the fork we're stopped at. Returns true + if the inferior should be resumed; false, if the target for some + reason decided it's best not to resume. */ + +int +follow_fork (int should_resume) +{ + int follow_child = (follow_fork_mode_string == follow_fork_mode_child); + struct thread_info *tp; + /* Copy user stepping state to the new inferior thread. FIXME: the + followed fork child thread should have a copy of most of the + parent thread structure's run control related fields, not just these. + Initialized to avoid "may be used uninitialized" warnings from gcc. */ + struct breakpoint *step_resume_breakpoint = NULL; + struct breakpoint *exception_resume_breakpoint = NULL; + CORE_ADDR step_range_start = 0; + CORE_ADDR step_range_end = 0; + struct frame_id step_frame_id = { 0 }; + tp = inferior_thread (); /* If there were any forks/vforks that were caught and are now to be @@ -1420,7 +1428,7 @@ displaced_step_restore (struct displaced_step_inferior_state *displaced, displaced->step_copy)); } -static void do_target_resume (ptid_t ptid, int step, enum target_signal signo); +void do_target_resume (ptid_t ptid, int step, enum target_signal signo); static void displaced_step_fixup (ptid_t event_ptid, enum target_signal signal) @@ -1587,16 +1595,16 @@ resume_cleanups (void *ignore) normal_stop (); } -static const char schedlock_off[] = "off"; -static const char schedlock_on[] = "on"; -static const char schedlock_step[] = "step"; +const char schedlock_off[] = "off"; +const char schedlock_on[] = "on"; +const char schedlock_step[] = "step"; static const char *scheduler_enums[] = { schedlock_off, schedlock_on, schedlock_step, NULL }; -static const char *scheduler_mode = schedlock_off; +const char *scheduler_mode = schedlock_off; static void show_scheduler_mode (struct ui_file *file, int from_tty, struct cmd_list_element *c, const char *value) @@ -1669,9 +1677,9 @@ user_visible_resume_ptid (int step) } /* Maybe resume a single thread after all. */ - if (non_stop) + if (target_is_non_stop_p ()) { - /* With non-stop mode on, threads are always handled + /* In non-stop mode, threads are always handled individually. */ resume_ptid = inferior_ptid; } @@ -1686,7 +1694,7 @@ user_visible_resume_ptid (int step) return resume_ptid; } -static void +void do_target_resume (ptid_t ptid, int step, enum target_signal signo) { int resume_many; @@ -1734,6 +1742,7 @@ do_target_resume (ptid_t ptid, int step, enum target_signal signo) happens to apply to another thread. */ tp->suspend.stop_signal = TARGET_SIGNAL_0; tp->control.resumed = 1; + tp->reported_event = 0; if (tp->suspend.waitstatus_pending_p) { @@ -2074,7 +2083,7 @@ a command like `return' or `jump' to continue execution.")); /* Clear out all variables saying what to do when inferior is continued. First do this, then set the ones you want, then call `proceed'. */ -static void +void clear_proceed_status_thread (struct thread_info *tp) { if (tp->state == THREAD_RUNNING) @@ -2120,7 +2129,7 @@ clear_proceed_status_callback (struct thread_info *tp, void *data) void clear_proceed_status (void) { - if (!non_stop) + if (!target_is_non_stop_p ()) { /* In all-stop mode, delete the per-thread status of all threads, even if inferior_ptid is null_ptid, there may be @@ -2133,7 +2142,7 @@ clear_proceed_status (void) { struct inferior *inferior; - if (non_stop) + if (target_is_non_stop_p ()) { /* If in non-stop mode, only delete the per-thread status of the current thread. */ @@ -2255,7 +2264,7 @@ proceed (CORE_ADDR addr, enum target_signal siggnal, int step) /* If we're stopped at a fork/vfork, follow the branch set by the "set follow-fork-mode" command; otherwise, we'll just proceed resuming the current thread. */ - if (!follow_fork ()) + if (prepare_to_follow_fork () && !follow_fork (1)) { /* The target for some reason decided not to resume. */ normal_stop (); @@ -2308,7 +2317,7 @@ proceed (CORE_ADDR addr, enum target_signal siggnal, int step) "infrun: proceed (addr=%s, signal=%d, step=%d)\n", paddress (gdbarch, addr), siggnal, step); - if (non_stop) + if (target_is_non_stop_p ()) /* In non-stop, each thread is handled individually. The context must already be set to the right thread here. */ ; @@ -2676,7 +2685,7 @@ delete_step_thread_step_resume_breakpoint (void) resume breakpoints out of GDB's lists. */ return; - if (non_stop) + if (target_is_non_stop_p ()) { /* If in non-stop mode, only delete the step-resume or longjmp-resume breakpoint of the thread that just stopped @@ -2879,7 +2888,7 @@ prepare_for_detach (void) /* In non-stop mode, each thread is handled individually. Switch early, so the global state is set correctly for this thread. */ - if (non_stop + if (target_is_non_stop_p () && ecs->ws.kind != TARGET_WAITKIND_EXITED && ecs->ws.kind != TARGET_WAITKIND_SIGNALLED) context_switch (ecs->ptid); @@ -3181,20 +3190,18 @@ fetch_inferior_event (void *client_data) /* cancel breakpoints */ } - if (non_stop - && ecs->ws.kind != TARGET_WAITKIND_IGNORE + if (ecs->ws.kind != TARGET_WAITKIND_IGNORE && ecs->ws.kind != TARGET_WAITKIND_NO_RESUMED && ecs->ws.kind != TARGET_WAITKIND_EXITED && ecs->ws.kind != TARGET_WAITKIND_SIGNALLED) - /* In non-stop mode, each thread is handled individually. Switch - early, so the global state is set correctly for this + /* Switch early, so the global state is set correctly for this thread. */ context_switch (ecs->ptid); /* If an error happens while handling the event, propagate GDB's knowledge of the executing state to the frontend/user running state. */ - if (!non_stop) + if (!target_is_non_stop_p ()) ts_old_chain = make_cleanup (finish_thread_state_cleanup, &minus_one_ptid); else ts_old_chain = make_cleanup (finish_thread_state_cleanup, &ecs->ptid); @@ -4158,7 +4165,7 @@ handle_inferior_event (struct execution_control_state *ecs) ecs->event_thread->suspend.stop_signal = TARGET_SIGNAL_0; - should_resume = follow_fork (); + should_resume = prepare_to_follow_fork() && follow_fork (1); parent = ecs->ptid; child = ecs->ws.value.related_pid; @@ -4567,7 +4574,7 @@ handle_inferior_event (struct execution_control_state *ecs) error (_("Cannot step over breakpoint hit in wrong thread")); else { /* Single step */ - if (!non_stop) + if (!target_is_non_stop_p ()) { /* Only need to require the next event from this thread in all-stop mode. */ @@ -5207,7 +5214,7 @@ process_event_stop_test: /* In all-stop mode, if we're currently stepping but have stopped in some other thread, we need to switch back to the stepped thread. */ - if (!non_stop) + if (!target_is_non_stop_p ()) { struct thread_info *tp; @@ -6191,6 +6198,11 @@ stop_stepping (struct execution_control_state *ecs) /* Let callers know we don't want to wait for the inferior anymore. */ ecs->wait_some_more = 0; + if (ecs->event_thread) + { + ecs->event_thread->reported_event = 1; + } + if (target_is_non_stop_p () && stop_only_if_needed) { diff --git a/gdb/itset.c b/gdb/itset.c index bd2c74a..717d261 100644 --- a/gdb/itset.c +++ b/gdb/itset.c @@ -1492,6 +1492,14 @@ itset_create_stopped (void) return itset_create (&spec); } +struct itset * +itset_create_empty (void) +{ + char *spec = "[]"; + + return itset_create (&spec); +} + /* Return 1 if SET contains INF, 0 otherwise. */ int diff --git a/gdb/itset.h b/gdb/itset.h index 9574275..da08477 100644 --- a/gdb/itset.h +++ b/gdb/itset.h @@ -67,6 +67,8 @@ const char *itset_spec (const struct itset *itset); int itset_is_empty (const struct itset *itset); +struct itset *itset_create_empty (void); + /* Acquire a new reference to an I/T set. Returns the I/T set, for convenience. */ diff --git a/gdb/testsuite/gdb.base/jump.exp b/gdb/testsuite/gdb.base/jump.exp index adf50c6..a9b8711 100644 --- a/gdb/testsuite/gdb.base/jump.exp +++ b/gdb/testsuite/gdb.base/jump.exp @@ -116,7 +116,7 @@ gdb_test "jump 12" \ "n" gdb_test "jump 12" \ - "Continuing at.*" \ + "Continuing .* \(.*\) at.*" \ "jump out of current function" \ "Line 12 is not in `main'. Jump anyway.*y or n. $" \ "y" diff --git a/gdb/thread.c b/gdb/thread.c index b42c8c3..846d2d3 100644 --- a/gdb/thread.c +++ b/gdb/thread.c @@ -134,6 +134,8 @@ clear_thread_inferior_resources (struct thread_info *tp) static void free_thread (struct thread_info *tp) { + xfree (tp->cmd_data); + if (tp->private) { if (tp->private_dtor)