Compare commits

...

2 Commits

Author SHA1 Message Date
Keith Seitz
a62f06ebb3 More minimal patch
Same thing but using the exception, leaving bpstat_stop_status call
where it is.

This and the previous commit are intended to be exported to an stgit branch,
where one can easily switch between the two approaches by push/pop'ing
patches. Diffing against origin/master will therefore always give a complete
patch sans busywork.
2017-11-28 13:23:10 -08:00
Keith Seitz
83b9557361 Report stop locations in inlined functions
This is a patch for a very related inline function problem.  Using the
test case from breakpoints/17534,

3	static inline void NVIC_EnableIRQ(int IRQn)
4	{
5	  volatile int y;
6	  y = IRQn;
7	}
8
9	__attribute__( ( always_inline ) ) static inline void __WFI(void)
10	{
11	    __asm volatile ("nop");
12	}
13
14	int main(void) {
15
16	    x= 42;
17
18	    if (x)
19	      NVIC_EnableIRQ(16);
20	    else
21	      NVIC_EnableIRQ(18);
(gdb) b NVIC_EnableIRQ
Breakpoint 1 at 0x4003e4: NVIC_EnableIRQ. (2 locations)
(gdb) r
Starting program: 17534

Breakpoint 1, main () at 17534.c:19
19	      NVIC_EnableIRQ(16);

This happens because skip_inline_frames automatically skips every inlined
frame.  Based on a suggestion by Jan, this patch introduces a new function,
breakpoint_for_stop, which attempts to ascertain which breakpoint, if any,
caused a particular stop in the inferior.  That breakpoint is then passed
to skip_inline_frames so that it can decide if a particular inlined frame
should be skipped.

I've had to separate the bpstat chain building from bpstat_stop_status --
py-finish-breakpoint.exp did not like me calling bpstat_stop_status multiple
times.  So I've added the ability to allocate the chain separately and
optionally pass it to bpstat_stop_status, which remains otherwise unchanged.

With this patch, GDB now correctly reports that the inferior has stopped
inside the inlined function:

(gdb) r
Starting program: 17534

Breakpoint 1, NVIC_EnableIRQ (IRQn=16) at 17534.c:6
6	  y = IRQn;


gdb/ChangeLog:

	* breakpoint.c (bpstat_explains_signal): Add output parameter for
	breakpoint and save the breakpoint if one is found to explain
	the signal.
	All callers updated.
	(build_bpstat_chain): New function, moved from bpstat_stop_status.
	(breakpoint_for_stop): New function.
	(bpstat_stop_status): Add new optional parameter for the bpstat chain.
	If this new parameter is NULL, call build_bpstat_chain.
	All callers updated.
	* breakpoint.h (breakpoint_for_stop): Declare.
	(bpstat_explains_signal): Update declaration.
	* infrun.c (handle_signal_stop): Before calling skip_inline_frames,
	use breakpoint_for_stop to find the breakpoint that caused us
	to stop.
	Save the bpstat chain for later invocation of bpstat_stop_status.
	* inline-frame.c: Include linespec.h.
	(skip_inline_frames): Add struct breakpoint parameter.
	Re-parse the location of the breakpoint causing the stop, if any,
	and only skip frames that did not cause the stop.
	* inline-frame.h (skip_inline_frames): Update declaration.

gdb/testsuite/ChangeLog:

	* gdb.opt/inline-break.c (inline_func1, not_inline_func1)
	(inline_func2, not_inline_func2, inline_func3, not_inline_func3):
	New functions.
	(main): Call not_inline_func3.
	* gdb.opt/inline-break.exp: Start inferior and set breakpoints at
	inline_func1, inline_func2, and inline_func3.  Test that when each
	breakpoint is hit, GDB properly reports both the stop location
	and the backtrace.
2017-11-28 12:39:19 -08:00
7 changed files with 139 additions and 25 deletions

View File

@@ -68,6 +68,7 @@
#include "format.h"
#include "thread-fsm.h"
#include "tid-parse.h"
#include "inline-frame.h"
/* readline include files */
#include "readline/readline.h"
@@ -5315,6 +5316,7 @@ bpstat_check_breakpoint_conditions (bpstat bs, ptid_t ptid)
}
CATCH (ex, RETURN_MASK_ALL)
{
clear_inline_frame_state (ptid);
exception_fprintf (gdb_stderr, ex,
"Error in testing breakpoint condition:\n");
}
@@ -6780,22 +6782,9 @@ describe_other_breakpoints (struct gdbarch *gdbarch,
}
/* Return true iff it is meaningful to use the address member of
BPT locations. For some breakpoint types, the locations' address members
are irrelevant and it makes no sense to attempt to compare them to other
addresses (or use them for any other purpose either).
/* See breakpoint.h. */
More specifically, each of the following breakpoint types will
always have a zero valued location address and we don't want to mark
breakpoints of any of these types to be a duplicate of an actual
breakpoint location at address zero:
bp_watchpoint
bp_catchpoint
*/
static int
bool
breakpoint_address_is_meaningful (struct breakpoint *bpt)
{
enum bptype type = bpt->type;

View File

@@ -920,6 +920,23 @@ extern bpstat bpstat_copy (bpstat);
extern bpstat bpstat_stop_status (const address_space *aspace,
CORE_ADDR pc, ptid_t ptid,
const struct target_waitstatus *ws);
/* Return true iff it is meaningful to use the address member of
BPT locations. For some breakpoint types, the locations' address members
are irrelevant and it makes no sense to attempt to compare them to other
addresses (or use them for any other purpose either).
More specifically, each of the following breakpoint types will
always have a zero valued location address and we don't want to mark
breakpoints of any of these types to be a duplicate of an actual
breakpoint location at address zero:
bp_watchpoint
bp_catchpoint
*/
extern bool breakpoint_address_is_meaningful (struct breakpoint *bpt);
/* This bpstat_what stuff tells wait_for_inferior what to do with a
breakpoint (a challenging task).

View File

@@ -5840,6 +5840,12 @@ handle_signal_stop (struct execution_control_state *ecs)
stop_print_frame = 1;
stopped_by_random_signal = 0;
/* See if there is a breakpoint/watchpoint/catchpoint/etc. that
handles this event. */
ecs->event_thread->control.stop_bpstat
= bpstat_stop_status (get_current_regcache ()->aspace (),
stop_pc, ecs->ptid, &ecs->ws);
/* Hide inlined functions starting here, unless we just performed stepi or
nexti. After stepi and nexti, always show the innermost frame (not any
inline function call sites). */
@@ -5870,7 +5876,12 @@ handle_signal_stop (struct execution_control_state *ecs)
ecs->event_thread->prev_pc,
&ecs->ws)))
{
skip_inline_frames (ecs->ptid);
struct breakpoint *bpt = NULL;
if (ecs->event_thread->control.stop_bpstat != NULL)
bpt = ecs->event_thread->control.stop_bpstat->breakpoint_at;
skip_inline_frames (ecs->ptid, bpt);
/* Re-fetch current thread's frame in case that invalidated
the frame cache. */
@@ -5915,12 +5926,6 @@ handle_signal_stop (struct execution_control_state *ecs)
}
}
/* See if there is a breakpoint/watchpoint/catchpoint/etc. that
handles this event. */
ecs->event_thread->control.stop_bpstat
= bpstat_stop_status (get_current_regcache ()->aspace (),
stop_pc, ecs->ptid, &ecs->ws);
/* Following in case break condition called a
function. */
stop_print_frame = 1;

View File

@@ -301,7 +301,7 @@ block_starting_point_at (CORE_ADDR pc, const struct block *block)
user steps into them. */
void
skip_inline_frames (ptid_t ptid)
skip_inline_frames (ptid_t ptid, struct breakpoint *bpt)
{
CORE_ADDR this_pc;
const struct block *frame_block, *cur_block;
@@ -327,7 +327,25 @@ skip_inline_frames (ptid_t ptid)
if (BLOCK_START (cur_block) == this_pc
|| block_starting_point_at (this_pc, cur_block))
{
skip_count++;
bool skip_this_frame = true;
if (bpt != NULL
&& user_breakpoint_p (bpt)
&& breakpoint_address_is_meaningful (bpt))
{
for (bp_location *loc = bpt->loc; loc != NULL;
loc = loc->next)
{
if (loc->address == this_pc)
{
skip_this_frame = false;
break;
}
}
}
if (skip_this_frame)
skip_count++;
last_sym = BLOCK_FUNCTION (cur_block);
}
else

View File

@@ -31,7 +31,7 @@ extern const struct frame_unwind inline_frame_unwind;
Frames for the hidden functions will not appear in the backtrace until the
user steps into them. */
void skip_inline_frames (ptid_t ptid);
void skip_inline_frames (ptid_t ptid, struct breakpoint *bpt);
/* Forget about any hidden inlined functions in PTID, which is new or
about to be resumed. If PTID is minus_one_ptid, forget about all

View File

@@ -128,6 +128,54 @@ func8a (int x)
return func8b (x * 31);
}
static inline ATTR int
inline_func1 (int x)
{
int y = 1; /* inline_func1 */
return y + x;
}
static int
not_inline_func1 (int x)
{
int y = 2; /* not_inline_func1 */
return y + inline_func1 (x);
}
inline ATTR int
inline_func2 (int x)
{
int y = 3; /* inline_func2 */
return y + not_inline_func1 (x);
}
int
not_inline_func2 (int x)
{
int y = 4; /* not_inline_func2 */
return y + inline_func2 (x);
}
static inline ATTR int
inline_func3 (int x)
{
int y = 5; /* inline_func3 */
return y + not_inline_func2 (x);
}
static int
not_inline_func3 (int x)
{
int y = 6; /* not_inline_func3 */
return y + inline_func3 (x);
}
/* Entry point. */
int
@@ -155,5 +203,7 @@ main (int argc, char *argv[])
x = func8a (x) + func8b (x);
x = not_inline_func3 (-21);
return x;
}

View File

@@ -185,4 +185,39 @@ for {set i 1} {$i <= [array size results]} {incr i} {
gdb_test "info break $i" $results($i)
}
# Start us running.
if {![runto main]} {
untested "could not run to main"
return -1
}
# Insert breakpoints for all inline_func? and not_inline_func? and check
# that we actually stop where we think we should.
for {set i 1} {$i < 4} {incr i} {
foreach inline {"not_inline" "inline"} {
gdb_breakpoint "${inline}_func$i" message
}
}
set ws {[\r\n\t ]+}
set backtrace [list "(in|at)? main"]
for {set i 3} {$i > 0} {incr i -1} {
foreach inline {"not_inline" "inline"} {
# Check that we stop at the correct location and print out
# the (possibly) inlined frames.
set num [gdb_get_line_number "/* ${inline}_func$i */"]
set pattern ".*/$srcfile:$num${ws}.*$num${ws}int y = $decimal;"
append pattern "${ws}/\\\* ${inline}_func$i \\\*/"
send_log "Expecting $pattern\n"
gdb_continue_to_breakpoint "${inline}_func$i" $pattern
# Also check for the correct backtrace.
set backtrace [linsert $backtrace 0 "(in|at)?${ws}${inline}_func$i"]
gdb_test_sequence "bt" "bt stopped in ${inline}_func$i" $backtrace
}
}
unset -nocomplain results