From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 19132 invoked by alias); 9 Sep 2014 17:33:02 -0000 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 Received: (qmail 19071 invoked by uid 89); 9 Sep 2014 17:33:01 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-3.2 required=5.0 tests=AWL,BAYES_00,RP_MATCHES_RCVD,SPF_HELO_PASS,SPF_PASS autolearn=ham version=3.3.2 X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES256-GCM-SHA384 encrypted) ESMTPS; Tue, 09 Sep 2014 17:32:56 +0000 Received: from int-mx14.intmail.prod.int.phx2.redhat.com (int-mx14.intmail.prod.int.phx2.redhat.com [10.5.11.27]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id s89HWs1o030576 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL) for ; Tue, 9 Sep 2014 13:32:55 -0400 Received: from [127.0.0.1] (ovpn01.gateway.prod.ext.ams2.redhat.com [10.39.146.11]) by int-mx14.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id s89HWqtO007880 for ; Tue, 9 Sep 2014 13:32:53 -0400 Message-ID: <540F39C4.6080702@redhat.com> Date: Tue, 09 Sep 2014 17:33:00 -0000 From: Pedro Alves User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:24.0) Gecko/20100101 Thunderbird/24.7.0 MIME-Version: 1.0 To: GDB Patches Subject: Re: eliminate deprecated_insert_raw_breakpoint. what's left. References: <540DEB7E.3000100@redhat.com> In-Reply-To: <540DEB7E.3000100@redhat.com> Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit X-SW-Source: 2014-09/txt/msg00242.txt.bz2 On 09/08/2014 06:46 PM, Pedro Alves wrote: > Hey, > > I'm working on a patch that converts software single-step breakpoints > to real breakpoints. It's largely done, but needs a bit of cleaning up > before I'll post it. One issue is that software single-step breakpoints > were implemented on top of deprecated_insert_raw_breakpoint. They no > longer are after my patch, but we're still left with some ugly code in > breakpoint.c related to deprecated_insert_raw_breakpoints on top of other > breakpoints that I'd like to eliminate completely, but that can only > be done once all users of deprecated_insert_raw_breakpoint are either converted > to some more modern mechanism, or eliminated. Here's my current WIP patch. This runs regression free on x86_64 Fedora 20, and also on top of my software-single-step-on-x86 series. I've tested this on ppc64 F18 as well, to cover the step-over-watchpoints changes. The ultimate goal was to eliminate a bunch of globals and make it possible to have multiple threads in parallel doing software single-steps (it works!). Currently in non-stop, on software single-step targets, we force all single-steps as displaced-stepping operations, which means single-step requests are serialized, because there's only one scratch pad. Note this also removes the limitation that you can only have at most two software single-step breakpoints, because each thread's single sss breakpoint can hold an arbitrary number of locations. As mentioned before, this removes deprecated_insert_raw_breakpoint completely, which we can't so while AIX and IRIX aren't cleaned up. Or maybe we can. :-) If something horrible jumps back at you, please shout! :-) --- >From c62b21283aef99bd495aea9b6c6680a4219ca2b2 Mon Sep 17 00:00:00 2001 From: Pedro Alves Date: Tue, 9 Sep 2014 17:28:45 +0100 Subject: [PATCH] make single-step breakpoints real breakpoints --- gdb/breakpoint.c | 418 ++++++++++++++---------------------------------------- gdb/breakpoint.h | 33 ++--- gdb/gdbthread.h | 25 ++++ gdb/infrun.c | 360 +++++++++++++++++++++++----------------------- gdb/infrun.h | 4 + gdb/record-full.c | 8 +- gdb/thread.c | 73 +++++++--- 7 files changed, 387 insertions(+), 534 deletions(-) diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index 683ed2b..93f4ad2 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -226,11 +226,6 @@ static void stopat_command (char *arg, int from_tty); static void tcatch_command (char *arg, int from_tty); -static void detach_single_step_breakpoints (void); - -static int find_single_step_breakpoint (struct address_space *aspace, - CORE_ADDR pc); - static void free_bp_location (struct bp_location *loc); static void incref_bp_location (struct bp_location *loc); static void decref_bp_location (struct bp_location **loc); @@ -296,12 +291,6 @@ static struct breakpoint_ops bkpt_probe_breakpoint_ops; /* Dynamic printf class type. */ struct breakpoint_ops dprintf_breakpoint_ops; -/* One (or perhaps two) breakpoints used for software single - stepping. */ - -static void *single_step_breakpoints[2]; -static struct gdbarch *single_step_gdbarch[2]; - /* The style in which to perform a dynamic printf. This is a user option because different output options have different tradeoffs; if GDB does the printing, there is better error handling if there @@ -1616,21 +1605,6 @@ breakpoint_xfer_memory (gdb_byte *readbuf, gdb_byte *writebuf, one_breakpoint_xfer_memory (readbuf, writebuf, writebuf_org, memaddr, len, &bl->target_info, bl->gdbarch); } - - /* Now process single-step breakpoints. These are not found in the - bp_location array. */ - for (i = 0; i < 2; i++) - { - struct bp_target_info *bp_tgt = single_step_breakpoints[i]; - - if (bp_tgt != NULL) - { - struct gdbarch *gdbarch = single_step_gdbarch[i]; - - one_breakpoint_xfer_memory (readbuf, writebuf, writebuf_org, - memaddr, len, bp_tgt, gdbarch); - } - } } @@ -2089,7 +2063,32 @@ should_be_inserted (struct bp_location *bl) || bl->loc_type == bp_loc_hardware_breakpoint) && stepping_past_instruction_at (bl->pspace->aspace, bl->address)) - return 0; + { + if (debug_infrun) + { + fprintf_unfiltered (gdb_stdlog, + "infrun: skipping breakpoint: " + "stepping past insn at: %s\n", + paddress (bl->gdbarch, bl->address)); + } + return 0; + } + + /* Don't insert watchpoints if we're trying to step past the + instruction that triggered one. */ + if ((bl->loc_type == bp_loc_hardware_watchpoint) + && stepping_past_nonsteppable_watchpoint ()) + { + if (debug_infrun) + { + fprintf_unfiltered (gdb_stdlog, + "infrun: stepping past non-steppable watchpoint. " + "skipping watchpoint at %s:%d\n", + paddress (bl->gdbarch, bl->address), + bl->length); + } + return 0; + } return 1; } @@ -3753,9 +3752,6 @@ detach_breakpoints (ptid_t ptid) val |= remove_breakpoint_1 (bl, mark_inserted); } - /* Detach single-step breakpoints as well. */ - detach_single_step_breakpoints (); - do_cleanups (old_chain); return val; } @@ -4025,6 +4021,10 @@ breakpoint_init_inferior (enum inf_context context) /* Also remove step-resume breakpoints. */ + case bp_single_step: + + /* Also remove single-step breakpoints. */ + delete_breakpoint (b); break; @@ -4121,14 +4121,10 @@ moribund_breakpoint_here_p (struct address_space *aspace, CORE_ADDR pc) return 0; } -/* Returns non-zero if there's a breakpoint inserted at PC, which is - inserted using regular breakpoint_chain / bp_location array - mechanism. This does not check for single-step breakpoints, which - are inserted and removed using direct target manipulation. */ +/* Returns non-zero iff there's a breakpoint inserted at PC. */ int -regular_breakpoint_inserted_here_p (struct address_space *aspace, - CORE_ADDR pc) +breakpoint_inserted_here_p (struct address_space *aspace, CORE_ADDR pc) { struct bp_location *bl, **blp_tmp; @@ -4152,27 +4148,12 @@ regular_breakpoint_inserted_here_p (struct address_space *aspace, return 0; } -/* Returns non-zero iff there's either regular breakpoint - or a single step breakpoint inserted at PC. */ +/* This function returns non-zero iff there is a software breakpoint + inserted at PC. */ int -breakpoint_inserted_here_p (struct address_space *aspace, CORE_ADDR pc) -{ - if (regular_breakpoint_inserted_here_p (aspace, pc)) - return 1; - - if (single_step_breakpoint_inserted_here_p (aspace, pc)) - return 1; - - return 0; -} - -/* Ignoring deprecated raw breakpoints, return non-zero iff there is a - software breakpoint inserted at PC. */ - -static struct bp_location * -find_non_raw_software_breakpoint_inserted_here (struct address_space *aspace, - CORE_ADDR pc) +software_breakpoint_inserted_here_p (struct address_space *aspace, + CORE_ADDR pc) { struct bp_location *bl, **blp_tmp; @@ -4190,27 +4171,10 @@ find_non_raw_software_breakpoint_inserted_here (struct address_space *aspace, && !section_is_mapped (bl->section)) continue; /* unmapped overlay -- can't be a match */ else - return bl; + return 1; } } - return NULL; -} - -/* This function returns non-zero iff there is a software breakpoint - inserted at PC. */ - -int -software_breakpoint_inserted_here_p (struct address_space *aspace, - CORE_ADDR pc) -{ - if (find_non_raw_software_breakpoint_inserted_here (aspace, pc) != NULL) - return 1; - - /* Also check for software single-step breakpoints. */ - if (single_step_breakpoint_inserted_here_p (aspace, pc)) - return 1; - return 0; } @@ -5498,6 +5462,7 @@ bpstat_stop_status (struct address_space *aspace, } } + /* Check if a moribund breakpoint explains the stop. */ for (ix = 0; VEC_iterate (bp_location_p, moribund_locations, ix, loc); ++ix) { if (breakpoint_location_address_match (loc, aspace, bp_addr)) @@ -5653,6 +5618,7 @@ bpstat_what (bpstat bs_head) break; case bp_breakpoint: case bp_hardware_breakpoint: + case bp_single_step: case bp_until: case bp_finish: case bp_shlib_event: @@ -6011,6 +5977,7 @@ bptype_string (enum bptype type) {bp_none, "?deleted?"}, {bp_breakpoint, "breakpoint"}, {bp_hardware_breakpoint, "hw breakpoint"}, + {bp_single_step, "sw single-step"}, {bp_until, "until"}, {bp_finish, "finish"}, {bp_watchpoint, "watchpoint"}, @@ -6202,6 +6169,7 @@ print_one_breakpoint_location (struct breakpoint *b, case bp_breakpoint: case bp_hardware_breakpoint: + case bp_single_step: case bp_until: case bp_finish: case bp_longjmp: @@ -7080,6 +7048,7 @@ init_bp_location (struct bp_location *loc, const struct bp_location_ops *ops, switch (owner->type) { case bp_breakpoint: + case bp_single_step: case bp_until: case bp_finish: case bp_longjmp: @@ -9020,10 +8989,31 @@ enable_breakpoints_after_startup (void) breakpoint_re_set (); } +/* Create a new single-step breakpoint for thread THREAD, with no + locations. */ + +static struct breakpoint * +new_single_step_breakpoint (int thread, struct gdbarch *gdbarch) +{ + struct breakpoint *b = XNEW (struct breakpoint); + + init_raw_breakpoint_without_location (b, gdbarch, bp_single_step, + &momentary_breakpoint_ops); -/* Set a breakpoint that will evaporate an end of command - at address specified by SAL. - Restrict it to frame FRAME if FRAME is nonzero. */ + b->disposition = disp_donttouch; + b->frame_id = null_frame_id; + + b->thread = thread; + gdb_assert (b->thread != 0); + + add_to_breakpoint_chain (b); + + return b; +} + +/* Set a momentary breakpoint of type TYPE at address specified by + SAL. If FRAME_ID is valid, the breakpoint is restricted to that + frame. */ struct breakpoint * set_momentary_breakpoint (struct gdbarch *gdbarch, struct symtab_and_line sal, @@ -13129,45 +13119,13 @@ bkpt_re_set (struct breakpoint *b) breakpoint_re_set_default (b); } -/* Copy SRC's shadow buffer and whatever else we'd set if we actually - inserted DEST, so we can remove it later, in case SRC is removed - first. */ - -static void -bp_target_info_copy_insertion_state (struct bp_target_info *dest, - const struct bp_target_info *src) -{ - dest->shadow_len = src->shadow_len; - memcpy (dest->shadow_contents, src->shadow_contents, src->shadow_len); - dest->placed_size = src->placed_size; -} - static int bkpt_insert_location (struct bp_location *bl) { if (bl->loc_type == bp_loc_hardware_breakpoint) - return target_insert_hw_breakpoint (bl->gdbarch, - &bl->target_info); + return target_insert_hw_breakpoint (bl->gdbarch, &bl->target_info); else - { - struct bp_target_info *bp_tgt = &bl->target_info; - int ret; - int sss_slot; - - /* There is no need to insert a breakpoint if an unconditional - raw/sss breakpoint is already inserted at that location. */ - sss_slot = find_single_step_breakpoint (bp_tgt->placed_address_space, - bp_tgt->placed_address); - if (sss_slot >= 0) - { - struct bp_target_info *sss_bp_tgt = single_step_breakpoints[sss_slot]; - - bp_target_info_copy_insertion_state (bp_tgt, sss_bp_tgt); - return 0; - } - - return target_insert_breakpoint (bl->gdbarch, bp_tgt); - } + return target_insert_breakpoint (bl->gdbarch, &bl->target_info); } static int @@ -13176,19 +13134,7 @@ bkpt_remove_location (struct bp_location *bl) if (bl->loc_type == bp_loc_hardware_breakpoint) return target_remove_hw_breakpoint (bl->gdbarch, &bl->target_info); else - { - struct bp_target_info *bp_tgt = &bl->target_info; - struct address_space *aspace = bp_tgt->placed_address_space; - CORE_ADDR address = bp_tgt->placed_address; - - /* Only remove the breakpoint if there is no raw/sss breakpoint - still inserted at this location. Otherwise, we would be - effectively disabling the raw/sss breakpoint. */ - if (single_step_breakpoint_inserted_here_p (aspace, address)) - return 0; - - return target_remove_breakpoint (bl->gdbarch, bp_tgt); - } + return target_remove_breakpoint (bl->gdbarch, &bl->target_info); } static int @@ -15186,217 +15132,73 @@ invalidate_bp_value_on_memory_change (struct inferior *inferior, } } -/* Create and insert a raw software breakpoint at PC. Return an - identifier, which should be used to remove the breakpoint later. - In general, places which call this should be using something on the - breakpoint chain instead; this function should be eliminated - someday. */ - -void * -deprecated_insert_raw_breakpoint (struct gdbarch *gdbarch, - struct address_space *aspace, CORE_ADDR pc) -{ - struct bp_target_info *bp_tgt; - struct bp_location *bl; - - bp_tgt = XCNEW (struct bp_target_info); - - bp_tgt->placed_address_space = aspace; - bp_tgt->placed_address = pc; - - /* If an unconditional non-raw breakpoint is already inserted at - that location, there's no need to insert another. However, with - target-side evaluation of breakpoint conditions, if the - breakpoint that is currently inserted on the target is - conditional, we need to make it unconditional. Note that a - breakpoint with target-side commands is not reported even if - unconditional, so we need to remove the commands from the target - as well. */ - bl = find_non_raw_software_breakpoint_inserted_here (aspace, pc); - if (bl != NULL - && VEC_empty (agent_expr_p, bl->target_info.conditions) - && VEC_empty (agent_expr_p, bl->target_info.tcommands)) - { - bp_target_info_copy_insertion_state (bp_tgt, &bl->target_info); - return bp_tgt; - } - - if (target_insert_breakpoint (gdbarch, bp_tgt) != 0) - { - /* Could not insert the breakpoint. */ - xfree (bp_tgt); - return NULL; - } - - return bp_tgt; -} - -/* Remove a breakpoint BP inserted by - deprecated_insert_raw_breakpoint. */ - -int -deprecated_remove_raw_breakpoint (struct gdbarch *gdbarch, void *bp) -{ - struct bp_target_info *bp_tgt = bp; - struct address_space *aspace = bp_tgt->placed_address_space; - CORE_ADDR address = bp_tgt->placed_address; - struct bp_location *bl; - int ret; - - bl = find_non_raw_software_breakpoint_inserted_here (aspace, address); - - /* Only remove the raw breakpoint if there are no other non-raw - breakpoints still inserted at this location. Otherwise, we would - be effectively disabling those breakpoints. */ - if (bl == NULL) - ret = target_remove_breakpoint (gdbarch, bp_tgt); - else if (!VEC_empty (agent_expr_p, bl->target_info.conditions) - || !VEC_empty (agent_expr_p, bl->target_info.tcommands)) - { - /* The target is evaluating conditions, and when we inserted the - software single-step breakpoint, we had made the breakpoint - unconditional and command-less on the target side. Reinsert - to restore the conditions/commands. */ - ret = target_insert_breakpoint (bl->gdbarch, &bl->target_info); - } - else - ret = 0; - - xfree (bp_tgt); - - return ret; -} - -/* Create and insert a breakpoint for software single step. */ +/* See breakpoint.h. */ void insert_single_step_breakpoint (struct gdbarch *gdbarch, struct address_space *aspace, CORE_ADDR next_pc) { - void **bpt_p; + struct thread_info *tp = inferior_thread (); + struct symtab_and_line sal; + CORE_ADDR pc = next_pc; - if (single_step_breakpoints[0] == NULL) - { - bpt_p = &single_step_breakpoints[0]; - single_step_gdbarch[0] = gdbarch; - } - else + if (tp->control.single_step_breakpoints == NULL) { - gdb_assert (single_step_breakpoints[1] == NULL); - bpt_p = &single_step_breakpoints[1]; - single_step_gdbarch[1] = gdbarch; + tp->control.single_step_breakpoints + = new_single_step_breakpoint (tp->num, gdbarch); } - /* NOTE drow/2006-04-11: A future improvement to this function would - be to only create the breakpoints once, and actually put them on - the breakpoint chain. That would let us use set_raw_breakpoint. - We could adjust the addresses each time they were needed. Doing - this requires corresponding changes elsewhere where single step - breakpoints are handled, however. So, for now, we use this. */ - - *bpt_p = deprecated_insert_raw_breakpoint (gdbarch, aspace, next_pc); - if (*bpt_p == NULL) - error (_("Could not insert single-step breakpoint at %s"), - paddress (gdbarch, next_pc)); -} - -/* Check if the breakpoints used for software single stepping - were inserted or not. */ - -int -single_step_breakpoints_inserted (void) -{ - return (single_step_breakpoints[0] != NULL - || single_step_breakpoints[1] != NULL); -} - -/* Remove and delete any breakpoints used for software single step. */ - -void -remove_single_step_breakpoints (void) -{ - gdb_assert (single_step_breakpoints[0] != NULL); + sal = find_pc_line (pc, 0); + sal.pc = pc; + sal.section = find_pc_overlay (pc); + sal.explicit_pc = 1; + add_location_to_breakpoint (tp->control.single_step_breakpoints, &sal); - /* See insert_single_step_breakpoint for more about this deprecated - call. */ - deprecated_remove_raw_breakpoint (single_step_gdbarch[0], - single_step_breakpoints[0]); - single_step_gdbarch[0] = NULL; - single_step_breakpoints[0] = NULL; + update_global_location_list_nothrow (1); - if (single_step_breakpoints[1] != NULL) - { - deprecated_remove_raw_breakpoint (single_step_gdbarch[1], - single_step_breakpoints[1]); - single_step_gdbarch[1] = NULL; - single_step_breakpoints[1] = NULL; - } + /* update_global_location_list does not insert breakpoints when + always_inserted_mode is not enabled. Explicitly insert them + now. */ + if (!breakpoints_always_inserted_mode ()) + insert_breakpoint_locations (); } -/* Delete software single step breakpoints without removing them from - the inferior. This is intended to be used if the inferior's address - space where they were inserted is already gone, e.g. after exit or - exec. */ +/* See breakpoint.h. */ -void -cancel_single_step_breakpoints (void) +int +breakpoint_is_inserted_here (struct breakpoint *bp, + struct address_space *aspace, CORE_ADDR pc) { - int i; - - for (i = 0; i < 2; i++) - if (single_step_breakpoints[i]) - { - xfree (single_step_breakpoints[i]); - single_step_breakpoints[i] = NULL; - single_step_gdbarch[i] = NULL; - } -} - -/* Detach software single-step breakpoints from INFERIOR_PTID without - removing them. */ + struct bp_location *loc; -static void -detach_single_step_breakpoints (void) -{ - int i; + for (loc = bp->loc; loc != NULL; loc = loc->next) + if (loc->inserted + && breakpoint_location_address_match (loc, aspace, pc)) + return 1; - for (i = 0; i < 2; i++) - if (single_step_breakpoints[i]) - target_remove_breakpoint (single_step_gdbarch[i], - single_step_breakpoints[i]); + return 0; } -/* Find the software single-step breakpoint that inserted at PC. - Returns its slot if found, and -1 if not found. */ +/* See breakpoint.h. */ -static int -find_single_step_breakpoint (struct address_space *aspace, - CORE_ADDR pc) +int +single_step_breakpoint_inserted_here_p (struct address_space *aspace, + CORE_ADDR pc) { - int i; + struct breakpoint *bpt; - for (i = 0; i < 2; i++) + ALL_BREAKPOINTS (bpt) { - struct bp_target_info *bp_tgt = single_step_breakpoints[i]; - if (bp_tgt - && breakpoint_address_match (bp_tgt->placed_address_space, - bp_tgt->placed_address, - aspace, pc)) - return i; - } - - return -1; -} + struct bp_location *loc; -/* Check whether a software single-step breakpoint is inserted at - PC. */ + if (bpt->type != bp_single_step) + continue; -int -single_step_breakpoint_inserted_here_p (struct address_space *aspace, - CORE_ADDR pc) -{ - return find_single_step_breakpoint (aspace, pc) >= 0; + if (breakpoint_is_inserted_here (bpt, aspace, pc)) + return 1; + } + return 0; } /* Returns 0 if 'bp' is NOT a syscall catchpoint, diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h index f6d06ce..5920181 100644 --- a/gdb/breakpoint.h +++ b/gdb/breakpoint.h @@ -47,18 +47,13 @@ struct linespec_sals; /* Type of breakpoint. */ -/* FIXME In the future, we should fold all other breakpoint-like - things into here. This includes: - - * single-step (for machines where we have to simulate single - stepping) (probably, though perhaps it is better for it to look as - much as possible like a single-step to wait_for_inferior). */ enum bptype { bp_none = 0, /* Eventpoint has been deleted */ bp_breakpoint, /* Normal breakpoint */ bp_hardware_breakpoint, /* Hardware assisted breakpoint */ + bp_single_step, /* Software single-step */ bp_until, /* used by until command */ bp_finish, /* used by finish command */ bp_watchpoint, /* Watchpoint */ @@ -1126,6 +1121,15 @@ extern int regular_breakpoint_inserted_here_p (struct address_space *, extern int software_breakpoint_inserted_here_p (struct address_space *, CORE_ADDR); +/* Check whether any location of BP is inserted at PC. */ + +extern int breakpoint_is_inserted_here (struct breakpoint *bp, + struct address_space *aspace, + CORE_ADDR pc); + +/* Check whether any software single-step breakpoint is inserted at + PC. */ + extern int single_step_breakpoint_inserted_here_p (struct address_space *, CORE_ADDR); @@ -1442,22 +1446,13 @@ extern void add_solib_catchpoint (char *arg, int is_load, int is_temp, deletes all breakpoints. */ extern void delete_command (char *arg, int from_tty); -/* Manage a software single step breakpoint (or two). Insert may be - called twice before remove is called. */ +/* Insert a new software single step breakpoint for the current + thread. Insert may be called multiple times; each time will add a + new location to the set of potential addresses the next instruction + is at. */ extern void insert_single_step_breakpoint (struct gdbarch *, struct address_space *, CORE_ADDR); -extern int single_step_breakpoints_inserted (void); -extern void remove_single_step_breakpoints (void); -extern void cancel_single_step_breakpoints (void); - -/* Manage manual breakpoints, separate from the normal chain of - breakpoints. These functions are used in murky target-specific - ways. Please do not add more uses! */ -extern void *deprecated_insert_raw_breakpoint (struct gdbarch *, - struct address_space *, - CORE_ADDR); -extern int deprecated_remove_raw_breakpoint (struct gdbarch *, void *); /* Check if any hardware watchpoints have triggered, according to the target. */ diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h index 522b674..77bbbde 100644 --- a/gdb/gdbthread.h +++ b/gdb/gdbthread.h @@ -52,6 +52,13 @@ struct thread_control_state /* Exception-resume breakpoint. */ struct breakpoint *exception_resume_breakpoint; + /* Breakpoints used for software single stepping. Plural, because + it may have multiple locations. E.g., if stepping over a + conditional branch instruction we can't decode the condition for, + we'll need to put a breakpoint at the branch destination, and + another at the instruction after the branch. */ + struct breakpoint *single_step_breakpoints; + /* Range to single step within. If this is nonzero, respond to a single-step signal by continuing @@ -197,6 +204,11 @@ struct thread_info /* Should we step over breakpoint next time keep_going is called? */ int stepping_over_breakpoint; + /* Should we step over a watchpoint next time keep_going is called? + This is needed on targets with non-continuable and non-steppable + watchpoints. */ + int stepping_over_watchpoint; + /* Set to TRUE if we should finish single-stepping over a breakpoint after hitting the current step-resume breakpoint. The context here is that GDB is to do `next' or `step' while signal arrives. @@ -280,6 +292,19 @@ extern void delete_step_resume_breakpoint (struct thread_info *); /* Delete an exception_resume_breakpoint from the thread database. */ extern void delete_exception_resume_breakpoint (struct thread_info *); +/* Delete the single-step breakpoints of thread TP, if any. */ +extern void delete_single_step_breakpoints (struct thread_info *tp); + +/* Check if the thread has software single stepping breakpoints + set. */ +extern int thread_has_single_step_breakpoints_set (struct thread_info *tp); + +/* Check whether the thread has software single stepping breakpoints + set at PC. */ +extern int thread_has_single_step_breakpoint_here (struct thread_info *tp, + struct address_space *aspace, + CORE_ADDR addr); + /* Translate the integer thread id (GDB's homegrown id, not the system's) into a "pid" (which may be overloaded with extra thread information). */ extern ptid_t thread_id_to_pid (int); diff --git a/gdb/infrun.c b/gdb/infrun.c index c18267f..2f3a848 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -373,8 +373,6 @@ static void context_switch (ptid_t ptid); void init_thread_stepping_state (struct thread_info *tss); -static void init_infwait_state (void); - static const char follow_fork_mode_child[] = "child"; static const char follow_fork_mode_parent[] = "parent"; @@ -844,6 +842,7 @@ follow_exec (ptid_t pid, char *execd_pathname) statement through an exec(). */ th->control.step_resume_breakpoint = NULL; th->control.exception_resume_breakpoint = NULL; + th->control.single_step_breakpoints = NULL; th->control.step_range_start = 0; th->control.step_range_end = 0; @@ -957,17 +956,6 @@ follow_exec (ptid_t pid, char *execd_pathname) matically get reset there in the new process.). */ } -/* Non-zero if we just simulating a single-step. This is needed - because we cannot remove the breakpoints in the inferior process - until after the `wait' in `wait_for_inferior'. */ -static int singlestep_breakpoints_inserted_p = 0; - -/* The thread we inserted single-step breakpoints for. */ -static ptid_t singlestep_ptid; - -/* PC when we started this single-step. */ -static CORE_ADDR singlestep_pc; - /* Info about an instruction that is being stepped over. Invalid if ASPACE is NULL. */ @@ -978,6 +966,10 @@ struct step_over_info /* The instruction's address. */ CORE_ADDR address; + + /* The instruction being stepped over triggers a nonsteppable + watchpoint. */ + int nonsteppable_watchpoint; }; /* The step-over info of the location that is being stepped over. @@ -1010,10 +1002,12 @@ static struct step_over_info step_over_info; stepping over. */ static void -set_step_over_info (struct address_space *aspace, CORE_ADDR address) +set_step_over_info (struct address_space *aspace, CORE_ADDR address, + int nonsteppable_watchpoint) { step_over_info.aspace = aspace; step_over_info.address = address; + step_over_info.nonsteppable_watchpoint = nonsteppable_watchpoint; } /* Called when we're not longer stepping over a breakpoint / an @@ -1024,6 +1018,7 @@ clear_step_over_info (void) { step_over_info.aspace = NULL; step_over_info.address = 0; + step_over_info.nonsteppable_watchpoint = 0; } /* See inferior.h. */ @@ -1032,10 +1027,35 @@ int stepping_past_instruction_at (struct address_space *aspace, CORE_ADDR address) { + if (step_over_info.aspace != NULL + && breakpoint_address_match (aspace, address, + step_over_info.aspace, + step_over_info.address)) + { + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, + "infrun: stepping over breakpoint's address: %s\n", + paddress (target_gdbarch (), address)); + return 1; + } + + return 0; +} + +int +stepping_past_nonsteppable_watchpoint (void) +{ + return step_over_info.nonsteppable_watchpoint; +} + +/* Returns true if any breakpoint/watchpoint that would normally be + inserted can't be inserted due to run control. */ + +static int +any_breakpoint_lifted (void) +{ return (step_over_info.aspace != NULL - && breakpoint_address_match (aspace, address, - step_over_info.aspace, - step_over_info.address)); + || stepping_past_nonsteppable_watchpoint ()); } @@ -1633,9 +1653,6 @@ infrun_thread_ptid_changed (ptid_t old_ptid, ptid_t new_ptid) if (ptid_equal (inferior_ptid, old_ptid)) inferior_ptid = new_ptid; - if (ptid_equal (singlestep_ptid, old_ptid)) - singlestep_ptid = new_ptid; - for (displaced = displaced_step_inferior_states; displaced; displaced = displaced->next) @@ -1694,29 +1711,17 @@ set_schedlock_func (char *args, int from_tty, struct cmd_list_element *c) process. */ int sched_multi = 0; -/* Try to setup for software single stepping over the specified location. - Return 1 if target_resume() should use hardware single step. - - GDBARCH the current gdbarch. - PC the location to step over. */ +/* Try to setup for software single stepping over the specified + location. Return 0 if target_resume() should use hardware single + step. GDBARCH is the current frame's gdbarch. PC is the location + to step over. */ static int maybe_software_singlestep (struct gdbarch *gdbarch, CORE_ADDR pc) { - int hw_step = 1; - - if (execution_direction == EXEC_FORWARD - && gdbarch_software_single_step_p (gdbarch) - && gdbarch_software_single_step (gdbarch, get_current_frame ())) - { - hw_step = 0; - /* Do not pull these breakpoints until after a `wait' in - `wait_for_inferior'. */ - singlestep_breakpoints_inserted_p = 1; - singlestep_ptid = inferior_ptid; - singlestep_pc = pc; - } - return hw_step; + return (execution_direction == EXEC_FORWARD + && gdbarch_software_single_step_p (gdbarch) + && gdbarch_software_single_step (gdbarch, get_current_frame ())); } ptid_t @@ -1739,8 +1744,7 @@ user_visible_resume_ptid (int step) resume_ptid = inferior_ptid; } else if ((scheduler_mode == schedlock_on) - || (scheduler_mode == schedlock_step - && (step || singlestep_breakpoints_inserted_p))) + || (scheduler_mode == schedlock_step && step)) { /* User-settable 'scheduler' mode requires solo thread resume. */ resume_ptid = inferior_ptid; @@ -1843,8 +1847,7 @@ a command like `return' or `jump' to continue execution.")); event, displaced stepping breaks the vfork child similarly as single step software breakpoint. */ if (use_displaced_stepping (gdbarch) - && (tp->control.trap_expected - || (step && gdbarch_software_single_step_p (gdbarch))) + && tp->control.trap_expected && sig == GDB_SIGNAL_0 && !current_inferior ()->waiting_for_vfork_done) { @@ -1877,7 +1880,7 @@ a command like `return' or `jump' to continue execution.")); /* Do we need to do it the hard way, w/temp breakpoints? */ else if (step) - step = maybe_software_singlestep (gdbarch, pc); + step = !maybe_software_singlestep (gdbarch, pc); /* Currently, our software single-step implementation leads to different results than hardware single-stepping in one situation: when stepping @@ -1903,9 +1906,17 @@ a command like `return' or `jump' to continue execution.")); at the current address, deliver the signal without stepping, and once we arrive back at the step-resume breakpoint, actually step over the breakpoint we originally wanted to step over. */ - if (singlestep_breakpoints_inserted_p - && tp->control.trap_expected && sig != GDB_SIGNAL_0) + if (thread_has_single_step_breakpoints_set (tp) + && sig != GDB_SIGNAL_0 + && any_breakpoint_lifted ()) { + if (debug_infrun) + { + fprintf_unfiltered (gdb_stdlog, + "infrun: delivering signal with breakpoints lifted. " + "replacing sss bp and letting handler run\n"); + } + /* If we have nested signals or a pending signal is delivered immediately after a handler returns, might might already have a step-resume breakpoint set on the earlier handler. We cannot @@ -1917,8 +1928,7 @@ a command like `return' or `jump' to continue execution.")); tp->step_after_step_resume_breakpoint = 1; } - remove_single_step_breakpoints (); - singlestep_breakpoints_inserted_p = 0; + delete_single_step_breakpoints (tp); clear_step_over_info (); tp->control.trap_expected = 0; @@ -1929,7 +1939,7 @@ a command like `return' or `jump' to continue execution.")); /* If STEP is set, it's a request to use hardware stepping facilities. But in that case, we should never use singlestep breakpoint. */ - gdb_assert (!(singlestep_breakpoints_inserted_p && step)); + gdb_assert (!(thread_has_single_step_breakpoints_set (tp) && step)); /* Decide the set of threads to ask the target to resume. Start by assuming everything will be resumed, than narrow the set @@ -1945,7 +1955,7 @@ a command like `return' or `jump' to continue execution.")); set_running (resume_ptid, 1); /* Maybe resume a single thread after all. */ - if ((step || singlestep_breakpoints_inserted_p) + if ((step || thread_has_single_step_breakpoints_set (tp)) && tp->control.trap_expected) { /* We're allowing a thread to run past a breakpoint it has @@ -1998,13 +2008,10 @@ a command like `return' or `jump' to continue execution.")); tp->suspend.stop_signal = GDB_SIGNAL_0; /* Advise target which signals may be handled silently. If we have - removed breakpoints because we are stepping over one (which can - happen only if we are not using displaced stepping), we need to - receive all signals to avoid accidentally skipping a breakpoint - during execution of a signal handler. */ - if ((step || singlestep_breakpoints_inserted_p) - && tp->control.trap_expected - && !use_displaced_stepping (gdbarch)) + removed breakpoints, we need to receive all signals to avoid + accidentally skipping a breakpoint during execution of a signal + handler. */ + if (any_breakpoint_lifted ()) target_pass_signals (0, NULL); else target_pass_signals ((int) GDB_SIGNAL_LAST, signal_pass); @@ -2310,7 +2317,7 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal, int step) struct regcache *regcache = get_current_regcache (); set_step_over_info (get_regcache_aspace (regcache), - regcache_read_pc (regcache)); + regcache_read_pc (regcache), 0); } else clear_step_over_info (); @@ -2350,9 +2357,6 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal, int step) correctly when the inferior is stopped. */ tp->prev_pc = regcache_read_pc (get_current_regcache ()); - /* Reset to normal state. */ - init_infwait_state (); - /* Resume inferior. */ resume (tp->control.trap_expected || step || bpstat_should_step (), tp->suspend.stop_signal); @@ -2417,13 +2421,9 @@ init_wait_for_inferior (void) target_last_wait_ptid = minus_one_ptid; previous_inferior_ptid = inferior_ptid; - init_infwait_state (); /* Discard any skipped inlined frames. */ clear_inline_frame_state (minus_one_ptid); - - singlestep_ptid = null_ptid; - singlestep_pc = 0; } @@ -2438,9 +2438,6 @@ enum infwait_states infwait_nonstep_watch_state }; -/* The PTID we'll do a target_wait on.*/ -ptid_t waiton_ptid; - /* Current inferior wait state. */ static enum infwait_states infwait_state; @@ -2460,11 +2457,6 @@ struct execution_control_state const char *stop_func_name; int wait_some_more; - /* We were in infwait_step_watch_state or - infwait_nonstep_watch_state state, and the thread reported an - event. */ - int stepped_after_stopped_by_watchpoint; - /* True if the event thread hit the single-step breakpoint of another thread. Thus the event doesn't cause a stop, the thread needs to be single-stepped past the single-step breakpoint before @@ -2609,6 +2601,7 @@ delete_step_resume_breakpoint_callback (struct thread_info *info, void *data) delete_step_resume_breakpoint (info); delete_exception_resume_breakpoint (info); + delete_single_step_breakpoints (info); return 0; } @@ -2634,6 +2627,7 @@ delete_step_thread_step_resume_breakpoint (void) delete_step_resume_breakpoint (tp); delete_exception_resume_breakpoint (tp); + delete_single_step_breakpoints (tp); } else /* In all-stop mode, delete all step-resume and longjmp-resume @@ -2641,6 +2635,39 @@ delete_step_thread_step_resume_breakpoint (void) iterate_over_threads (delete_step_resume_breakpoint_callback, NULL); } +static void +delete_stopped_threads_single_step_breakpoints (void) +{ + if (!target_has_execution + || ptid_equal (inferior_ptid, null_ptid)) + { + /* If the inferior has exited, we have already deleted the + single-step breakpoints out of GDB's lists. */ + return; + } + + if (non_stop) + { + /* If in non-stop mode, only delete the step-resume or + longjmp-resume breakpoint of the thread that just stopped + stepping. */ + struct thread_info *tp = inferior_thread (); + + delete_single_step_breakpoints (tp); + } + else + { + struct thread_info *tp; + + ALL_NON_EXITED_THREADS (tp) + { + /* In all-stop mode, delete single-step breakpoints of any + thread that had them. */ + delete_single_step_breakpoints (tp); + } + } +} + /* A cleanup wrapper. */ static void @@ -2789,6 +2816,7 @@ wait_for_inferior (void) struct execution_control_state ecss; struct execution_control_state *ecs = &ecss; struct cleanup *old_chain; + ptid_t waiton_ptid = minus_one_ptid; memset (ecs, 0, sizeof (*ecs)); @@ -2844,6 +2872,7 @@ fetch_inferior_event (void *client_data) struct cleanup *ts_old_chain; int was_sync = sync_execution; int cmd_done = 0; + ptid_t waiton_ptid = minus_one_ptid; memset (ecs, 0, sizeof (*ecs)); @@ -2961,6 +2990,7 @@ void init_thread_stepping_state (struct thread_info *tss) { tss->stepping_over_breakpoint = 0; + tss->stepping_over_watchpoint = 0; tss->step_after_step_resume_breakpoint = 0; } @@ -3120,7 +3150,7 @@ adjust_pc_after_break (struct execution_control_state *ecs) software breakpoint. In this case (prev_pc == breakpoint_pc), we also need to back up to the breakpoint address. */ - if (singlestep_breakpoints_inserted_p + if (thread_has_single_step_breakpoints_set (ecs->event_thread) || !ptid_equal (ecs->ptid, inferior_ptid) || !currently_stepping (ecs->event_thread) || ecs->event_thread->prev_pc == breakpoint_pc) @@ -3130,13 +3160,6 @@ adjust_pc_after_break (struct execution_control_state *ecs) } } -static void -init_infwait_state (void) -{ - waiton_ptid = pid_to_ptid (-1); - infwait_state = infwait_normal_state; -} - static int stepped_in_from (struct frame_info *frame, struct frame_id step_frame_id) { @@ -3357,40 +3380,6 @@ handle_inferior_event (struct execution_control_state *ecs) && ecs->ws.kind != TARGET_WAITKIND_EXITED) set_executing (ecs->ptid, 0); - switch (infwait_state) - { - case infwait_normal_state: - if (debug_infrun) - fprintf_unfiltered (gdb_stdlog, "infrun: infwait_normal_state\n"); - break; - - case infwait_step_watch_state: - if (debug_infrun) - fprintf_unfiltered (gdb_stdlog, - "infrun: infwait_step_watch_state\n"); - - ecs->stepped_after_stopped_by_watchpoint = 1; - break; - - case infwait_nonstep_watch_state: - if (debug_infrun) - fprintf_unfiltered (gdb_stdlog, - "infrun: infwait_nonstep_watch_state\n"); - insert_breakpoints (); - - /* FIXME-maybe: is this cleaner than setting a flag? Does it - handle things like signals arriving and other things happening - in combination correctly? */ - ecs->stepped_after_stopped_by_watchpoint = 1; - break; - - default: - internal_error (__FILE__, __LINE__, _("bad switch")); - } - - infwait_state = infwait_normal_state; - waiton_ptid = pid_to_ptid (-1); - switch (ecs->ws.kind) { case TARGET_WAITKIND_LOADED: @@ -3548,8 +3537,6 @@ Cannot fill $_exitsignal with the correct signal number.\n")); gdb_flush (gdb_stdout); target_mourn_inferior (); - singlestep_breakpoints_inserted_p = 0; - cancel_single_step_breakpoints (); stop_print_frame = 0; stop_waiting (ecs); return; @@ -3642,12 +3629,7 @@ Cannot fill $_exitsignal with the correct signal number.\n")); detach_breakpoints (ecs->ws.value.related_pid); } - if (singlestep_breakpoints_inserted_p) - { - /* Pull the single step breakpoints out of the target. */ - remove_single_step_breakpoints (); - singlestep_breakpoints_inserted_p = 0; - } + delete_stopped_threads_single_step_breakpoints (); /* In case the event is caught by a catchpoint, remember that the event is to be followed at the next resume of the thread, @@ -3734,9 +3716,6 @@ Cannot fill $_exitsignal with the correct signal number.\n")); if (!ptid_equal (ecs->ptid, inferior_ptid)) context_switch (ecs->ptid); - singlestep_breakpoints_inserted_p = 0; - cancel_single_step_breakpoints (); - stop_pc = regcache_read_pc (get_thread_regcache (ecs->ptid)); /* Do whatever is necessary to the parent branch of the vfork. */ @@ -3802,14 +3781,7 @@ Cannot fill $_exitsignal with the correct signal number.\n")); fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_NO_HISTORY\n"); /* Reverse execution: target ran out of history info. */ - /* Pull the single step breakpoints out of the target. */ - if (singlestep_breakpoints_inserted_p) - { - if (!ptid_equal (ecs->ptid, inferior_ptid)) - context_switch (ecs->ptid); - remove_single_step_breakpoints (); - singlestep_breakpoints_inserted_p = 0; - } + delete_stopped_threads_single_step_breakpoints (); stop_pc = regcache_read_pc (get_thread_regcache (ecs->ptid)); observer_notify_no_history (); stop_waiting (ecs); @@ -3948,43 +3920,59 @@ handle_signal_stop (struct execution_control_state *ecs) gdbarch = get_frame_arch (frame); /* Pull the single step breakpoints out of the target. */ - if (singlestep_breakpoints_inserted_p) + if (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP + && gdbarch_software_single_step_p (gdbarch)) { + struct regcache *regcache; + struct address_space *aspace; + CORE_ADDR pc; + + regcache = get_thread_regcache (ecs->ptid); + aspace = get_regcache_aspace (regcache); + pc = regcache_read_pc (regcache); + /* However, before doing so, if this single-step breakpoint was actually for another thread, set this thread up for moving past it. */ - if (!ptid_equal (ecs->ptid, singlestep_ptid) - && ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP) + if (!thread_has_single_step_breakpoint_here (ecs->event_thread, + aspace, pc)) { - struct regcache *regcache; - struct address_space *aspace; - CORE_ADDR pc; - - regcache = get_thread_regcache (ecs->ptid); - aspace = get_regcache_aspace (regcache); - pc = regcache_read_pc (regcache); if (single_step_breakpoint_inserted_here_p (aspace, pc)) { if (debug_infrun) { fprintf_unfiltered (gdb_stdlog, - "infrun: [%s] hit step over single-step" - " breakpoint of [%s]\n", - target_pid_to_str (ecs->ptid), - target_pid_to_str (singlestep_ptid)); + "infrun: [%s] hit another thread's " + "single-step breakpoint\n", + target_pid_to_str (ecs->ptid)); } ecs->hit_singlestep_breakpoint = 1; } } + else + { + if (debug_infrun) + { + fprintf_unfiltered (gdb_stdlog, + "infrun: [%s] hit its " + "single-step breakpoint\n", + target_pid_to_str (ecs->ptid)); + } + } - remove_single_step_breakpoints (); - singlestep_breakpoints_inserted_p = 0; + delete_stopped_threads_single_step_breakpoints (); } - if (ecs->stepped_after_stopped_by_watchpoint) - stopped_by_watchpoint = 0; + if (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP + && ecs->event_thread->control.trap_expected + && ecs->event_thread->stepping_over_watchpoint) + { + stopped_by_watchpoint = 0; + } else - stopped_by_watchpoint = watchpoints_triggered (&ecs->ws); + { + stopped_by_watchpoint = watchpoints_triggered (&ecs->ws); + } /* If necessary, step over this watchpoint. We'll be back to display it in a moment. */ @@ -4004,36 +3992,28 @@ handle_signal_stop (struct execution_control_state *ecs) watchpoint expression. We do this by single-stepping the target. - It may not be necessary to disable the watchpoint to stop over + It may not be necessary to disable the watchpoint to step over it. For example, the PA can (with some kernel cooperation) single step over a watchpoint without disabling the watchpoint. It is far more common to need to disable a watchpoint to step the inferior over it. If we have non-steppable watchpoints, we must disable the current watchpoint; it's simplest to - disable all watchpoints and breakpoints. */ - int hw_step = 1; - - if (!target_have_steppable_watchpoint) - { - remove_breakpoints (); - /* See comment in resume why we need to stop bypassing signals - while breakpoints have been removed. */ - target_pass_signals (0, NULL); - } - /* Single step */ - hw_step = maybe_software_singlestep (gdbarch, stop_pc); - target_resume (ecs->ptid, hw_step, GDB_SIGNAL_0); - waiton_ptid = ecs->ptid; - if (target_have_steppable_watchpoint) - infwait_state = infwait_step_watch_state; - else - infwait_state = infwait_nonstep_watch_state; - prepare_to_wait (ecs); + disable all watchpoints. + + Any breakpoint at PC must also be stepped over -- if there's + one, it will have already triggered before the watchpoint + triggered, and we either already reported it to the user, or + it didn't cause a stop and we called keep_going. In either + case, if there was a breakpoint at PC, we must be trying to + step past it. */ + ecs->event_thread->stepping_over_watchpoint = 1; + keep_going (ecs); return; } ecs->event_thread->stepping_over_breakpoint = 0; + ecs->event_thread->stepping_over_watchpoint = 0; bpstat_clear (&ecs->event_thread->control.stop_bpstat); ecs->event_thread->control.stop_step = 0; stop_print_frame = 1; @@ -5311,15 +5291,21 @@ switch_back_to_stepped_thread (struct execution_control_state *ecs) { if (debug_infrun) fprintf_unfiltered (gdb_stdlog, - "infrun: expected thread advanced also\n"); + "infrun: expected thread advanced also: prev_pc=%s, stop_pc=%s\n", + paddress (gdbarch, tp->prev_pc), + paddress (gdbarch, stop_pc)); + + /* Clear this before trying to insert the sss + breakpoint, in case we were previously trying to step + over this location in another thread, otherwise the + breakpoint ends up _not_ installed. It's what + keep_going would do too, if we called it. */ + clear_step_over_info (); insert_single_step_breakpoint (get_frame_arch (frame), get_frame_address_space (frame), stop_pc); - singlestep_breakpoints_inserted_p = 1; ecs->event_thread->control.trap_expected = 1; - singlestep_ptid = inferior_ptid; - singlestep_pc = stop_pc; resume (0, GDB_SIGNAL_0); prepare_to_wait (ecs); @@ -5769,6 +5755,8 @@ keep_going (struct execution_control_state *ecs) { volatile struct gdb_exception e; struct regcache *regcache = get_current_regcache (); + int remove_bp; + int remove_wps; /* Either the trap was not expected, but we are continuing anyway (if we got a signal, the user asked it be passed to @@ -5788,13 +5776,19 @@ keep_going (struct execution_control_state *ecs) (watchpoints, etc.) but the one we're stepping over, step one instruction, and then re-insert the breakpoint when that step is finished. */ - if ((ecs->hit_singlestep_breakpoint - || thread_still_needs_step_over (ecs->event_thread)) - && !use_displaced_stepping (get_regcache_arch (regcache))) + + remove_bp = (ecs->hit_singlestep_breakpoint + || thread_still_needs_step_over (ecs->event_thread)); + remove_wps = (ecs->event_thread->stepping_over_watchpoint + && !target_have_steppable_watchpoint); + + if (remove_bp && !use_displaced_stepping (get_regcache_arch (regcache))) { set_step_over_info (get_regcache_aspace (regcache), - regcache_read_pc (regcache)); + regcache_read_pc (regcache), remove_wps); } + else if (remove_wps) + set_step_over_info (NULL, 0, remove_wps); else clear_step_over_info (); @@ -5810,9 +5804,7 @@ keep_going (struct execution_control_state *ecs) return; } - ecs->event_thread->control.trap_expected - = (ecs->event_thread->stepping_over_breakpoint - || ecs->hit_singlestep_breakpoint); + ecs->event_thread->control.trap_expected = (remove_bp || remove_wps); /* Do not deliver GDB_SIGNAL_TRAP (except when the user explicitly specifies that such a signal should be delivered diff --git a/gdb/infrun.h b/gdb/infrun.h index cc9cb33..13127ed 100644 --- a/gdb/infrun.h +++ b/gdb/infrun.h @@ -122,6 +122,10 @@ extern void follow_inferior_reset_breakpoints (void); extern int stepping_past_instruction_at (struct address_space *aspace, CORE_ADDR address); +/* Returns true if we're trying to step past an instruction that + triggers a non-steppable watchpoint. */ +extern int stepping_past_nonsteppable_watchpoint (void); + extern void set_step_info (struct frame_info *frame, struct symtab_and_line sal); diff --git a/gdb/record-full.c b/gdb/record-full.c index 119361f..a2257ed 100644 --- a/gdb/record-full.c +++ b/gdb/record-full.c @@ -961,7 +961,7 @@ record_full_resume (struct target_ops *ops, ptid_t ptid, int step, else { /* This arch support soft sigle step. */ - if (single_step_breakpoints_inserted ()) + if (thread_has_single_step_breakpoints_set (inferior_thread ())) { /* This is a soft single step. */ record_full_resume_step = 1; @@ -1085,6 +1085,8 @@ record_full_wait_1 (struct target_ops *ops, while (1) { + struct thread_info *tp; + ret = ops->beneath->to_wait (ops->beneath, ptid, status, options); if (status->kind == TARGET_WAITKIND_IGNORE) { @@ -1095,8 +1097,8 @@ record_full_wait_1 (struct target_ops *ops, return ret; } - if (single_step_breakpoints_inserted ()) - remove_single_step_breakpoints (); + ALL_NON_EXITED_THREADS (tp) + delete_single_step_breakpoints (tp); if (record_full_resume_step) return ret; diff --git a/gdb/thread.c b/gdb/thread.c index 65890e1..1cdbaf4 100644 --- a/gdb/thread.c +++ b/gdb/thread.c @@ -85,26 +85,68 @@ inferior_thread (void) return tp; } -void -delete_step_resume_breakpoint (struct thread_info *tp) +/* Delete the breakpoint pointed at by BP_P, if there's one. */ + +static void +delete_thread_breakpoint (struct breakpoint **bp_p) { - if (tp && tp->control.step_resume_breakpoint) + if (*bp_p != NULL) { - delete_breakpoint (tp->control.step_resume_breakpoint); - tp->control.step_resume_breakpoint = NULL; + delete_breakpoint (*bp_p); + *bp_p = NULL; } } void +delete_step_resume_breakpoint (struct thread_info *tp) +{ + if (tp != NULL) + delete_thread_breakpoint (&tp->control.step_resume_breakpoint); +} + +void delete_exception_resume_breakpoint (struct thread_info *tp) { - if (tp && tp->control.exception_resume_breakpoint) + if (tp != NULL) + delete_thread_breakpoint (&tp->control.exception_resume_breakpoint); +} + +void +delete_single_step_breakpoints (struct thread_info *tp) +{ + if (tp != NULL) + delete_thread_breakpoint (&tp->control.single_step_breakpoints); +} + +/* Delete the breakpoint pointed at by BP_P at the next stop, if + there's one. */ + +static void +delete_at_next_stop (struct breakpoint **bp) +{ + if (*bp != NULL) { - delete_breakpoint (tp->control.exception_resume_breakpoint); - tp->control.exception_resume_breakpoint = NULL; + (*bp)->disposition = disp_del_at_next_stop; + *bp = NULL; } } +int +thread_has_single_step_breakpoints_set (struct thread_info *tp) +{ + return tp->control.single_step_breakpoints != NULL; +} + +int +thread_has_single_step_breakpoint_here (struct thread_info *tp, + struct address_space *aspace, + CORE_ADDR addr) +{ + return (tp->control.single_step_breakpoints != NULL + && breakpoint_is_inserted_here (tp->control.single_step_breakpoints, + aspace, addr)); +} + static void clear_thread_inferior_resources (struct thread_info *tp) { @@ -112,18 +154,9 @@ clear_thread_inferior_resources (struct thread_info *tp) but not any user-specified thread-specific breakpoints. We can not delete the breakpoint straight-off, because the inferior might not be stopped at the moment. */ - if (tp->control.step_resume_breakpoint) - { - tp->control.step_resume_breakpoint->disposition = disp_del_at_next_stop; - tp->control.step_resume_breakpoint = NULL; - } - - if (tp->control.exception_resume_breakpoint) - { - tp->control.exception_resume_breakpoint->disposition - = disp_del_at_next_stop; - tp->control.exception_resume_breakpoint = NULL; - } + delete_at_next_stop (&tp->control.step_resume_breakpoint); + delete_at_next_stop (&tp->control.exception_resume_breakpoint); + delete_at_next_stop (&tp->control.single_step_breakpoints); delete_longjmp_breakpoint_at_next_stop (tp->num); -- 1.9.3