From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 15164 invoked by alias); 28 Nov 2011 15:40:31 -0000 Received: (qmail 13997 invoked by uid 22791); 28 Nov 2011 15:40:16 -0000 X-SWARE-Spam-Status: No, hits=4.1 required=5.0 tests=AWL,BAYES_00,BOTNET,FROM_12LTRDOM,RDNS_DYNAMIC,TW_FC 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:39:46 +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 pASFdgRZ018050 for ; Mon, 28 Nov 2011 15:39:42 GMT Subject: [RFC/WIP PATCH 09/14] I/T set support for breakpoints - trigger set, and stop set To: gdb-patches@sourceware.org From: Pedro Alves Date: Mon, 28 Nov 2011 15:40:00 -0000 Message-ID: <20111128153942.17761.96028.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/msg00770.txt.bz2 This adds support for setting a breakpoint that only triggers on a given set (a superset of the current thread specific breakpoints support). In addition, it adds support for specifying the set of threads that are suspended when the breakpoint is triggered. Breakpoints need two sets. The trigger set, which is a generalization of the "break foo thread N", meaning the set of inferiors/threads where the breakpoint should fire, and, a suspend/stop set, which is the set of inferiors/threads that should be suspended when the breakpoint fires. The trigger set of breakpoints is set from the current set at the time the breakpoint is created. The stop set is passed explicitly as optional switch. E.g.,: [TRIGGER-SET] break [-stop [STOP-SET]] LINESPEC This leaves LINESPEC last, so that we can keep supporting the current form, but avoid more hacks in linespecs like the special termination for "thread/task/if" in the lexers --- that wouldn't work for `['. So the old: (gdb) break LINESPEC still works just the same. The breakpoint's trigger set will be inferred from the current set as set by itfocus, or a [SET] prefix, and, the stop set is inferred from the "set non-stop" global option. If non-stop is on, only the thread that triggers the breakpoint should be suspended; if non-stop is off, then all threads will be suspended when the breakpoint fires. E.g., (gdb) info threads Id Target Id Frame 3 Thread 0x7ffff7028700 (LWP 2296) "threads" (running) * 2 Thread 0x7ffff7829700 (LWP 2295) "threads" thread_function0 (arg=0x0) at threads.c:63 1 Thread 0x7ffff7fcb720 (LWP 2290) "threads" (running) (gdb) [.2] break -stop [.3] 63 Breakpoint 4 at 0x40076d: file threads.c, line 63. Breakpoint 4 triggers on thread 2 (equivalent to break 63 thread 2), and when the breakpoint fires, thread 3 is suspended. Like so: (gdb) c -a& Continuing. (gdb) Breakpoint 4, thread_function0 (arg=0x0) at threads.c:63 63 (*myp) ++; info threads Id Target Id Frame 3 Thread 0x7ffff7028700 (LWP 2296) "threads" 0x00007ffff78d75ad in nanosleep () from /lib/x86_64-linux-gnu/libc.so.6 * 2 Thread 0x7ffff7829700 (LWP 2295) "threads" thread_function0 (arg=0x0) at threads.c:63 1 Thread 0x7ffff7fcb720 (LWP 2290) "threads" (running) (gdb) info breakpoints Num Type Disp Enb Address What 4 breakpoint keep y 0x000000000040076d in thread_function0 at threads.c:63 stop only in trigger-set: [.2] suspend all in stop-set: [.3] breakpoint already hit 1 time We can make a breakpoint that stops the world with (recall I had non-stop on): (gdb) del 4 (gdb) [.2] break -stop [all] 63 Breakpoint 5 at 0x40076d: file threads.c, line 63. (gdb) c -a& Continuing. (gdb) Breakpoint 5, thread_function0 (arg=0x0) at threads.c:63 63 (*myp) ++; info threads Id Target Id Frame 3 Thread 0x7ffff7028700 (LWP 2296) "threads" 0x00007ffff78d75ad in nanosleep () from /lib/x86_64-linux-gnu/libc.so.6 * 2 Thread 0x7ffff7829700 (LWP 2295) "threads" thread_function0 (arg=0x0) at threads.c:63 1 Thread 0x7ffff7fcb720 (LWP 2290) "threads" 0x00007ffff7bc606d in pthread_join () from /lib/x86_64-linux-gnu/libpthread.so.0 Since all-stop was reimplemented on top of the target running in non-stop mode, when "set non-stop" is off, not specifying a stop set (or specifying it as empty, like -stop []) means that all threads stop by default, e.g., `[.2] break 63', but you can still override that explicitly by specifying "-stop [set]", so the breakpoint only forces suspension of a given itset, with: (gdb) [.2] break -stop [.3] 63 Breakpoint 5 at 0x40076d: file threads.c, line 63. (gdb) c & Continuing. (gdb) Breakpoint 5, thread_function0 (arg=0x0) at threads.c:63 63 (*myp) ++; info threads Id Target Id Frame 3 Thread 0x7ffff7028700 (LWP 2645) "threads" 0x00007ffff78d75ad in nanosleep () from /lib/x86_64-linux-gnu/libc.so.6 * 2 Thread 0x7ffff7829700 (LWP 2644) "threads" thread_function0 (arg=0x0) at threads.c:63 1 Thread 0x7ffff7fcb720 (LWP 2610) "threads" (running) --- gdb/breakpoint.c | 135 ++++++++++++++++++++++++++++++++++++++++++++++++++---- gdb/breakpoint.h | 13 +++++ gdb/infrun.c | 52 +++++++++++++++++---- 3 files changed, 182 insertions(+), 18 deletions(-) diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index bdc5d38..23bae18 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -34,6 +34,7 @@ #include "value.h" #include "command.h" #include "inferior.h" +#include "itset.h" #include "gdbthread.h" #include "target.h" #include "language.h" @@ -3035,6 +3036,21 @@ hardware_watchpoint_inserted_in_range (struct address_space *aspace, return 0; } +/* Test whether this stopping thread is in the I/T set for this + breakpoint. */ + +static int +bpstat_check_trigger_set (const struct breakpoint *b, struct thread_info *thread) +{ + if (b->trigger_set == NULL) + return 1; + + if (itset_contains_thread (b->trigger_set, thread)) + return 1; + + return 0; +} + /* breakpoint_thread_match (PC, PTID) returns true if the breakpoint at PC is valid for process/thread PTID. */ @@ -3044,7 +3060,7 @@ breakpoint_thread_match (struct address_space *aspace, CORE_ADDR pc, { struct bp_location *bl, **blp_tmp; /* The thread and task IDs associated to PTID, computed lazily. */ - int thread = -1; + struct thread_info *thread = NULL; int task = 0; ALL_BP_LOCATIONS (bl, blp_tmp) @@ -3066,9 +3082,9 @@ breakpoint_thread_match (struct address_space *aspace, CORE_ADDR pc, /* This is a thread-specific breakpoint. Check that ptid matches that thread. If thread hasn't been computed yet, it is now time to do so. */ - if (thread == -1) - thread = pid_to_thread_id (ptid); - if (bl->owner->thread != thread) + if (thread == NULL) + thread = find_thread_ptid (ptid); + if (bl->owner->thread != thread->num) continue; } @@ -3083,6 +3099,16 @@ breakpoint_thread_match (struct address_space *aspace, CORE_ADDR pc, continue; } + if (bl->owner->trigger_set != NULL) + { + /* A breakpoint with a trigger itset. Check that ptid + matches that set. */ + if (thread == NULL) + thread = find_thread_ptid (ptid); + if (!bpstat_check_trigger_set (bl->owner, thread)) + continue; + } + if (overlay_debugging && section_is_overlay (bl->section) && !section_is_mapped (bl->section)) @@ -3974,7 +4000,7 @@ bpstat_check_watchpoint (bpstat bs) static void bpstat_check_breakpoint_conditions (bpstat bs, ptid_t ptid) { - int thread_id = pid_to_thread_id (ptid); + struct thread_info *thread = find_thread_ptid (ptid); const struct bp_location *bl; struct breakpoint *b; @@ -4076,7 +4102,12 @@ bpstat_check_breakpoint_conditions (bpstat bs, ptid_t ptid) { bs->stop = 0; } - else if (b->thread != -1 && b->thread != thread_id) + else if (b->thread != -1 && b->thread != thread->num) + { + bs->stop = 0; + } + else if (b->trigger_set != NULL + && !bpstat_check_trigger_set (b, thread)) { bs->stop = 0; } @@ -4504,6 +4535,33 @@ bpstat_what (bpstat bs_head) return retval; } +/* Tell us what we should suspend. */ + +struct itset * +bpstat_stop_set (bpstat bs_head) +{ + bpstat bs; + + for (bs = bs_head; bs != NULL; bs = bs->next) + { + if (bs->breakpoint_at == NULL) + { + /* I suspect this can happen if it was a momentary + breakpoint which has since been deleted. */ + continue; + } + + if (!bs->stop) + continue; + + /* XXX: Should be the union of the sets of all breakpoints that + caused a stop? */ + return itset_reference (bs->breakpoint_at->stop_set); + } + + return NULL; +} + /* Nonzero if we should step constantly (e.g. watchpoints on machines without hardware support). This isn't related to a specific bpstat, just to things like whether watchpoints are set. */ @@ -4903,7 +4961,27 @@ print_one_breakpoint_location (struct breakpoint *b, ui_out_field_int (uiout, "thread", b->thread); ui_out_text (uiout, "\n"); } - + + if (!part_of_multiple && b->trigger_set != NULL) + { + ui_out_text (uiout, "\tstop only in trigger-set: ["); + if (itset_name (b->trigger_set) != NULL) + ui_out_field_string (uiout, "trigger-set", itset_name (b->trigger_set)); + else + ui_out_field_string (uiout, "trigger-set", itset_spec (b->trigger_set)); + ui_out_text (uiout, "]\n"); + } + + if (!part_of_multiple && b->stop_set != NULL) + { + ui_out_text (uiout, "\tsuspend all in stop-set: ["); + if (itset_name (b->stop_set) != NULL) + ui_out_field_string (uiout, "stop-set", itset_name (b->stop_set)); + else + ui_out_field_string (uiout, "stop-set", itset_spec (b->stop_set)); + ui_out_text (uiout, "]\n"); + } + if (!part_of_multiple && b->hit_count) { /* FIXME should make an annotation for this. */ @@ -5731,6 +5809,7 @@ init_raw_breakpoint_without_location (struct breakpoint *b, b->language = current_language->la_language; b->input_radix = input_radix; b->thread = -1; + b->trigger_set = NULL; b->enable_state = bp_enabled; b->next = 0; b->silent = 0; @@ -7274,6 +7353,7 @@ init_breakpoint_sal (struct breakpoint *b, struct gdbarch *gdbarch, struct symtabs_and_lines sals, char *addr_string, char *cond_string, enum bptype type, enum bpdisp disposition, + struct itset *trigger_set, struct itset *stop_set, int thread, int task, int ignore_count, const struct breakpoint_ops *ops, int from_tty, int enabled, int internal, int display_canonical) @@ -7314,6 +7394,8 @@ init_breakpoint_sal (struct breakpoint *b, struct gdbarch *gdbarch, init_raw_breakpoint (b, gdbarch, sal, type, ops); b->thread = thread; b->task = task; + b->trigger_set = trigger_set; + b->stop_set = stop_set; b->cond_string = cond_string; b->ignore_count = ignore_count; @@ -7399,6 +7481,7 @@ create_breakpoint_sal (struct gdbarch *gdbarch, struct symtabs_and_lines sals, char *addr_string, char *cond_string, enum bptype type, enum bpdisp disposition, + struct itset *trigger_set, struct itset *stop_set, int thread, int task, int ignore_count, const struct breakpoint_ops *ops, int from_tty, int enabled, int internal, int display_canonical) @@ -7422,6 +7505,7 @@ create_breakpoint_sal (struct gdbarch *gdbarch, sals, addr_string, cond_string, type, disposition, + trigger_set, stop_set, thread, task, ignore_count, ops, from_tty, enabled, internal, display_canonical); @@ -7583,6 +7667,7 @@ create_breakpoints_sal (struct gdbarch *gdbarch, struct linespec_result *canonical, char *cond_string, enum bptype type, enum bpdisp disposition, + struct itset *trigger_set, struct itset *stop_set, int thread, int task, int ignore_count, const struct breakpoint_ops *ops, int from_tty, int enabled, int internal) @@ -7596,6 +7681,7 @@ create_breakpoints_sal (struct gdbarch *gdbarch, create_breakpoint_sal (gdbarch, expanded, canonical->canonical[i], cond_string, type, disposition, + trigger_set, stop_set, thread, task, ignore_count, ops, from_tty, enabled, internal, canonical->special_display); @@ -7619,7 +7705,8 @@ parse_breakpoint_sals (char **address, /* If no arg given, or if first arg is 'if ', use the default breakpoint. */ - if ((*address) == NULL + if (*address == NULL + || **address == '\0' || (strncmp ((*address), "if", 2) == 0 && isspace ((*address)[2]))) { /* The last displayed codepoint, if it's valid, is our default breakpoint @@ -7884,6 +7971,9 @@ create_breakpoint (struct gdbarch *gdbarch, int pending = 0; int task = 0; int prev_bkpt_count = breakpoint_count; + char *p; + struct itset *trigger_set = itset_reference (current_itset); + struct itset *stop_set = itset_reference (trigger_set); gdb_assert (ops != NULL); @@ -7904,6 +7994,30 @@ create_breakpoint (struct gdbarch *gdbarch, goto done; } + if (arg != NULL) + { + while (*arg) + { + arg = skip_spaces (arg); + p = skip_to_space (arg); + + if (strncmp (arg, "-stop", p - arg) == 0) + { + p = skip_spaces (p); + itset_free (stop_set); + stop_set = itset_create (&p); + arg = p; + } + else if (strcmp (arg, "--") == 0) + { + arg += 2; + break; + } + else + break; + } + } + TRY_CATCH (e, RETURN_MASK_ALL) { parse_breakpoint_sals (&arg, &sals, &canonical); @@ -8046,7 +8160,7 @@ create_breakpoint (struct gdbarch *gdbarch, canonical.canonical[i], cond_string, type_wanted, tempflag ? disp_del : disp_donttouch, - thread, task, ignore_count, ops, + NULL, NULL, thread, task, ignore_count, ops, from_tty, enabled, internal, canonical.special_display); /* Given that its possible to have multiple markers with @@ -8066,6 +8180,7 @@ create_breakpoint (struct gdbarch *gdbarch, create_breakpoints_sal (gdbarch, sals, &canonical, cond_string, type_wanted, tempflag ? disp_del : disp_donttouch, + trigger_set, stop_set, thread, task, ignore_count, ops, from_tty, enabled, internal); } @@ -10941,6 +11056,8 @@ base_breakpoint_dtor (struct breakpoint *self) xfree (self->addr_string); xfree (self->addr_string_range_end); xfree (self->source_file); + itset_free (self->trigger_set); + itset_free (self->stop_set); } static struct bp_location * diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h index df50438..45c98cb 100644 --- a/gdb/breakpoint.h +++ b/gdb/breakpoint.h @@ -32,6 +32,7 @@ struct get_number_or_range_state; struct thread_info; struct bpstats; struct bp_location; +struct itset; /* This is the maximum number of bytes a breakpoint instruction can take. Feel free to increase it. It's just used in a few places to @@ -609,6 +610,15 @@ struct breakpoint or 0 if don't care. */ int task; + /* I/T set controlling where this breakpoint will stop. */ + struct itset *trigger_set; + + /* The set that should be suspended once the breakpoint has + triggered. If empty, defaults to consulting the "non-stop" + setting: if that is on, just the triggering thread should stop; + otherwise, all threads in the TRIGGER_SET set stop. */ + struct itset *stop_set; + /* Count of the number of times this breakpoint was taken, dumped with the info, but not used for anything else. Useful for seeing how many times you hit a break prior to the program @@ -860,6 +870,9 @@ enum print_stop_action /* Tell what to do about this bpstat. */ struct bpstat_what bpstat_what (bpstat); + +/* Tell us what we should suspend. */ +extern struct itset *bpstat_stop_set (bpstat); /* Find the bpstat associated with a breakpoint. NULL otherwise. */ bpstat bpstat_find_breakpoint (bpstat, struct breakpoint *); diff --git a/gdb/infrun.c b/gdb/infrun.c index e0b282f..c279508 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -26,6 +26,7 @@ #include "symtab.h" #include "frame.h" #include "inferior.h" +#include "itset.h" #include "exceptions.h" #include "breakpoint.h" #include "gdb_wait.h" @@ -2527,6 +2528,8 @@ struct execution_control_state char *stop_func_name; int new_thread_event; int wait_some_more; + + struct itset *stop_set; }; static void handle_inferior_event (struct execution_control_state *ecs); @@ -2900,7 +2903,7 @@ prepare_for_detach (void) discard_cleanups (old_chain_1); } -static void stop_all_threads (void); +static void stop_all_threads (struct itset *stop_set); static int adjust_pc_after_break (struct thread_info *thread, struct target_waitstatus *ws); @@ -3002,7 +3005,7 @@ wait_for_inferior (void) if (!non_stop && target_is_non_stop_p () && !stop_only_if_needed) { - stop_all_threads (); + stop_all_threads (NULL); if (ecs->ws.kind != TARGET_WAITKIND_NO_RESUMED && ecs->ws.kind != TARGET_WAITKIND_EXITED @@ -3173,7 +3176,7 @@ fetch_inferior_event (void *client_data) if (!non_stop && target_is_non_stop_p () && !stop_only_if_needed && ecs->ws.kind != TARGET_WAITKIND_IGNORE) { - stop_all_threads (); + stop_all_threads (NULL); /* select event thread */ /* cancel breakpoints */ } @@ -3580,8 +3583,25 @@ wait_one (ptid_t wait_ptid, struct target_waitstatus *ws) return event_ptid; } +#define itset_spec_empty(SET) \ + ((SET) == NULL || itset_spec (SET)[0] == '\0') + +static int +stop_set_match (struct itset *stop_set, struct thread_info *t) +{ + int inf_id; + + if (itset_spec_empty (stop_set)) + return !non_stop; + + if (itset_contains_thread (stop_set, t)) + return 1; + + return 0; +} + static void -stop_all_threads (void) +stop_all_threads (struct itset *stop_set) { /* We may need multiple passes to discover all threads. */ int pass; @@ -3611,6 +3631,7 @@ stop_all_threads (void) /* Go through all threads looking for threads that we need to tell the target to stop. */ ALL_LIVE_THREADS (t) + if (stop_set_match (stop_set, t)) { if (t->executing) { @@ -3655,6 +3676,11 @@ stop_all_threads (void) pass = -1; event_ptid = wait_one (minus_one_ptid, &ws); + + /* FIXME: if EVENT_PTID isn't in the trigger or stop sets, + we'll need to re-resume or handle the event + afterwards. */ + if (ws.kind == TARGET_WAITKIND_NO_RESUMED) /* All resumed threads exited. */ ; @@ -5129,7 +5155,11 @@ process_event_stop_test: /* We are about to nuke the step_resume_breakpointt via the cleanup chain, so no need to worry about it here. */ + ecs->stop_set + = bpstat_stop_set (ecs->event_thread->control.stop_bpstat); stop_stepping (ecs); + itset_free (ecs->stop_set); + ecs->stop_set = NULL; return; case BPSTAT_WHAT_STOP_SILENT: @@ -5140,7 +5170,11 @@ process_event_stop_test: /* We are about to nuke the step_resume_breakpoin via the cleanup chain, so no need to worry about it here. */ + ecs->stop_set + = bpstat_stop_set (ecs->event_thread->control.stop_bpstat); stop_stepping (ecs); + itset_free (ecs->stop_set); + ecs->stop_set = NULL; return; case BPSTAT_WHAT_HP_STEP_RESUME: @@ -6157,13 +6191,12 @@ 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 (!non_stop - && target_is_non_stop_p () + if (target_is_non_stop_p () && stop_only_if_needed) { struct thread_info *t; - stop_all_threads (); + stop_all_threads (ecs->stop_set); cancel_breakpoints (); @@ -6171,7 +6204,8 @@ stop_stepping (struct execution_control_state *ecs) are now stopped until a new resume action is sent over. */ ALL_LIVE_THREADS (t) - t->control.resumed = 0; + if (stop_set_match (ecs->stop_set, t)) + t->control.resumed = 0; } } @@ -6230,7 +6264,7 @@ keep_going (struct execution_control_state *ecs) /* Since we can't do a displaced step, we have to remove the breakpoint while we step it. To keep things simple, we remove them all. */ - stop_all_threads (); + stop_all_threads (NULL); cancel_breakpoints (); /* In all-stop, from the core's perspective, all threads