mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-27 04:52:05 +08:00
ddfe970e6b
This patch essentially causes GDB to treat inlined frames like "normal" frames from the user's perspective. This means, for example, that when a user sets a breakpoint in an inlined function, GDB will now actually stop "in" that function. 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); Because skip_inline_frames currently skips every inlined frame, GDB "stops" in the caller. This patch adds a new parameter to skip_inline_frames that allows us to pass in a bpstat stop chain. The breakpoint locations on the stop chain can be used to determine if we've stopped inside an inline function (due to a user breakpoint). If we have, we do not elide the frame. With this patch, GDB now 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; Many thanks to Jan and Pedro for guidance on this. gdb/ChangeLog: * breakpoint.c (build_bpstat_chain): New function, moved from bpstat_stop_status. (bpstat_stop_status): Add optional parameter, `stop_chain'. If no stop chain is passed, call build_bpstat_chain to build it. * breakpoint.h (build_bpstat_chain): Declare. (bpstat_stop_status): Move documentation here from breakpoint.c. * infrun.c (handle_signal_stop): Before eliding inlined frames, build the stop chain and pass it to skip_inline_frames. Pass this stop chain to bpstat_stop_status. * inline-frame.c: Include breakpoint.h. (stopped_by_user_bp_inline_frame): New function. (skip_inline_frames): Add parameter `stop_chain'. Move documention to inline-frame.h. If non-NULL, use stopped_by_user_bp_inline_frame to determine whether the frame should be elided. * inline-frame.h (skip_inline_frames): Add parameter `stop_chain'. Add moved documentation and update for new parameter. gdb/testsuite/ChangeLog: * gdb.ada/bp_inlined_func.exp: Update inlined frame locations in expected breakpoint stop locations. * gdb.dwarf2/implptr.exp (implptr_test_baz): Use up/down to move to proper scope to test variable values. * 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. Repeat tests for temporary breakpoints.
68 lines
2.3 KiB
C
68 lines
2.3 KiB
C
/* Definitions for inline frame support.
|
|
|
|
Copyright (C) 2008-2018 Free Software Foundation, Inc.
|
|
|
|
This file is part of GDB.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
#if !defined (INLINE_FRAME_H)
|
|
#define INLINE_FRAME_H 1
|
|
|
|
struct frame_info;
|
|
struct frame_unwind;
|
|
struct bpstats;
|
|
|
|
/* The inline frame unwinder. */
|
|
|
|
extern const struct frame_unwind inline_frame_unwind;
|
|
|
|
/* Skip all inlined functions whose call sites are at the current PC.
|
|
|
|
If non-NULL, STOP_CHAIN is used to determine whether a stop was caused by
|
|
a user breakpoint. In that case, do not skip that inlined frame. This
|
|
allows the inlined frame to be treated as if it were non-inlined from the
|
|
user's perspective. GDB will stop "in" the inlined frame instead of
|
|
the caller. */
|
|
|
|
void skip_inline_frames (ptid_t ptid, struct bpstats *stop_chain);
|
|
|
|
/* 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
|
|
hidden inlined functions. */
|
|
|
|
void clear_inline_frame_state (ptid_t ptid);
|
|
|
|
/* Step into an inlined function by unhiding it. */
|
|
|
|
void step_into_inline_frame (ptid_t ptid);
|
|
|
|
/* Return the number of hidden functions inlined into the current
|
|
frame. */
|
|
|
|
int inline_skipped_frames (ptid_t ptid);
|
|
|
|
/* If one or more inlined functions are hidden, return the symbol for
|
|
the function inlined into the current frame. */
|
|
|
|
struct symbol *inline_skipped_symbol (ptid_t ptid);
|
|
|
|
/* Return the number of functions inlined into THIS_FRAME. Some of
|
|
the callees may not have associated frames (see
|
|
skip_inline_frames). */
|
|
|
|
int frame_inlined_callees (struct frame_info *this_frame);
|
|
|
|
#endif /* !defined (INLINE_FRAME_H) */
|