gdb/
* NEWS: Document inlined function support. * Makefile.in (SFILES): Add inline-frame.c. (COMMON_OBS): Add inline-frame.o. * block.c (contained_in): Rewrite to use lexical nesting. (block_linkage_function): Skip inlined function blocks. (block_inlined_p): New. * block.h (struct block): Update comment. (block_inlined_p): New prototype. * blockframe.c (get_frame_block): Handle inlined functions. (get_frame_function): Do not use block_linkage_function. (block_innermost_frame): Use get_frame_block and contained_in. * breakpoint.c (watchpoint_check): Remove extra reinit_frame_cache. Skip over inlined functions. Simplify epilogue check. (bpstat_check_breakpoint_conditions): Use get_stack_frame_id. Update comments. (set_momentary_breakpoint): Only accept non-inlined frames. (watch_command_1): Use frame_unwind_caller_pc and frame_unwind_caller_id instead of get_prev_frame. (until_break_command): Likewise. Use get_stack_frame_id. * buildsym.c (end_symtab): Set SYMBOL_SYMTAB for block functions. * dwarf2loc.c (dwarf_expr_frame_base): Use block_linkage_function. * dwarf2read.c (process_die): Handle DW_TAG_inlined_subroutine. (read_func_scope, new_symbol): Likewise. Handle arguments specially for inlined functions without call site information. (inherit_abstract_dies): Allow tag mismatch for inlined subroutines. (die_specification): Treat DW_AT_abstract_origin as a specification. (read_type_die): Handle DW_TAG_inlined_subroutine. * frame-unwind.c (frame_unwind_init): Add inline_frame_unwind. * frame.c (fprint_frame_id): Print inline depth. (fprint_frame_type): Handle INLINE_FRAME and SENTINEL_FRAME. (skip_inlined_frames, get_stack_frame_id): New. (frame_unwind_caller_id): Use skip_inlined_frames. (frame_id_inlined_p): New. (frame_id_eq): Make the logic match the comments. Add inline_depth check. (frame_id_inner): Handle inlined functions. (frame_unwind_pc): New function, copied from frame_unwind_caller_pc. (frame_unwind_caller_pc): Use skip_inlined_frames and frame_unwind_pc. (get_prev_frame_1): Check for inline frames. Split out frame allocation to get_prev_frame_raw. (get_prev_frame_raw): New function. (get_prev_frame): Handle inline frames. (get_frame_pc): Use frame_unwind_pc. (get_frame_address_in_block): Skip inlined frames on both sides. (pc_notcurrent): Delete. (find_frame_sal): Rewrite to handle inline call sites. Use get_frame_address_in_block. (deprecated_update_frame_pc_hack): Make static. * frame.h: Update comments. (struct frame_id): Add inline_depth. (enum frame_type): Add INLINE_FRAME. (frame_id_inlined_p, get_stack_frame_id): New prototypes. * gdbthread.h (struct thread_info): Add step_stack_frame_id field. * infcmd.c (set_step_frame): New function. (step_once): Use set_step_frame. Handle inlined functions. (until_next_command): Use set_step_frame. (finish_backward), finish_forward): Use get_stack_frame_id. (finish_command): Support inlined functions. * inferior.h (set_step_info): New prototype. * infrun.c (RESUME_ALL): Use minus_one_ptid. (clear_proceed_status): Clear step_stack_frame_id. (init_wait_for_inferior): Call clear_inline_frame_state. (init_execution_control_state): Make static. (set_step_info): New function. (init_thread_stepping_state): Do not set the symtab or line here. (stepped_in_from): New function. (handle_inferior_event): Handle inlined functions. Use set_step_info. (insert_step_resume_breakpoint_at_frame): Use get_stack_frame_id. (struct inferior_status): Add step_stack_frame_id. (save_inferior_status, restore_inferior_status): Save and restore step_stack_frame_id. * inline-frame.c, inline-frame.h: New files. * minsyms.c (prim_record_minimal_symbol_and_info): Use XCALLOC. * regcache.c (regcache_write_pc): Call reinit_frame_cache. * s390-tdep.c (s390_prologue_frame_unwind_cache): Handle INLINE_FRAME. * stack.c (frame_show_address): New. (print_frame_info, print_frame): Use it. (find_frame_funname): Use get_frame_function. Handle inlined blocks. (frame_info): Mark inlined functions. (backtrace_command_1): Use get_current_user_frame. (print_frame_local_vars, print_frame_label_vars): Update comments. (return_command): Refuse inlined functions. * symtab.c (lookup_symbol_aux_local): Stop at inlined function boundaries. (find_function_start_sal): Avoid inlined functions. (completion_list_add_fields): New function. (default_make_symbol_completion_list): Use it. Use block_static_block and block_global_block. Check for inlined functions. (skip_prologue_using_sal): Avoid line number comparison across inlining. * symtab.h (struct symbol): Add is_inlined. (SYMBOL_INLINED): New. * target.c (target_resume): Call clear_inline_frame_state. * valops.c (value_of_variable): Check block_inlined_p. gdb/doc/ * gdb.texinfo (Debugging Optimized Code): New chapter. (Compiling for Debugging): Reference it. Move some text to the new section. gdb/testsuite/ * gdb.base/break.exp: Add an XFAIL for gcc/36748. * gdb.cp/annota2.exp: Accept frames-invalid in more places. * gdb.opt/Makefile.in (EXECUTABLES): Update. * gdb.opt/clobbered-registers-O2.exp: Update to GPL v3. * gdb.opt/inline-bt.c, gdb.opt/inline-bt.exp, gdb.opt/inline-cmds.c, gdb.opt/inline-cmds.exp, gdb.opt/inline-locals.c, gdb.opt/inline-locals.exp, gdb.opt/inline-markers.c: New files. * lib/gdb.exp (skip_inline_frame_tests): New function. (skip_inline_var_tests): New function.
This commit is contained in:
parent
c7ce8faacb
commit
edb3359dff
|
@ -1,3 +1,101 @@
|
|||
2009-06-27 Daniel Jacobowitz <dan@codesourcery.com>
|
||||
Jan Kratochvil <jan.kratochvil@redhat.com>
|
||||
|
||||
* NEWS: Document inlined function support.
|
||||
* Makefile.in (SFILES): Add inline-frame.c.
|
||||
(COMMON_OBS): Add inline-frame.o.
|
||||
* block.c (contained_in): Rewrite to use lexical nesting.
|
||||
(block_linkage_function): Skip inlined function blocks.
|
||||
(block_inlined_p): New.
|
||||
* block.h (struct block): Update comment.
|
||||
(block_inlined_p): New prototype.
|
||||
* blockframe.c (get_frame_block): Handle inlined functions.
|
||||
(get_frame_function): Do not use block_linkage_function.
|
||||
(block_innermost_frame): Use get_frame_block and contained_in.
|
||||
* breakpoint.c (watchpoint_check): Remove extra reinit_frame_cache.
|
||||
Skip over inlined functions. Simplify epilogue check.
|
||||
(bpstat_check_breakpoint_conditions): Use get_stack_frame_id.
|
||||
Update comments.
|
||||
(set_momentary_breakpoint): Only accept non-inlined frames.
|
||||
(watch_command_1): Use frame_unwind_caller_pc and
|
||||
frame_unwind_caller_id instead of get_prev_frame.
|
||||
(until_break_command): Likewise. Use get_stack_frame_id.
|
||||
* buildsym.c (end_symtab): Set SYMBOL_SYMTAB for block functions.
|
||||
* dwarf2loc.c (dwarf_expr_frame_base): Use block_linkage_function.
|
||||
* dwarf2read.c (process_die): Handle DW_TAG_inlined_subroutine.
|
||||
(read_func_scope, new_symbol): Likewise. Handle arguments specially
|
||||
for inlined functions without call site information.
|
||||
(inherit_abstract_dies): Allow tag mismatch for inlined subroutines.
|
||||
(die_specification): Treat DW_AT_abstract_origin as a specification.
|
||||
(read_type_die): Handle DW_TAG_inlined_subroutine.
|
||||
* frame-unwind.c (frame_unwind_init): Add inline_frame_unwind.
|
||||
* frame.c (fprint_frame_id): Print inline depth.
|
||||
(fprint_frame_type): Handle INLINE_FRAME and SENTINEL_FRAME.
|
||||
(skip_inlined_frames, get_stack_frame_id): New.
|
||||
(frame_unwind_caller_id): Use skip_inlined_frames.
|
||||
(frame_id_inlined_p): New.
|
||||
(frame_id_eq): Make the logic match the comments. Add inline_depth
|
||||
check.
|
||||
(frame_id_inner): Handle inlined functions.
|
||||
(frame_unwind_pc): New function, copied from frame_unwind_caller_pc.
|
||||
(frame_unwind_caller_pc): Use skip_inlined_frames and frame_unwind_pc.
|
||||
(get_prev_frame_1): Check for inline frames. Split out frame
|
||||
allocation to get_prev_frame_raw.
|
||||
(get_prev_frame_raw): New function.
|
||||
(get_prev_frame): Handle inline frames.
|
||||
(get_frame_pc): Use frame_unwind_pc.
|
||||
(get_frame_address_in_block): Skip inlined frames on both sides.
|
||||
(pc_notcurrent): Delete.
|
||||
(find_frame_sal): Rewrite to handle inline call sites. Use
|
||||
get_frame_address_in_block.
|
||||
(deprecated_update_frame_pc_hack): Make static.
|
||||
* frame.h: Update comments.
|
||||
(struct frame_id): Add inline_depth.
|
||||
(enum frame_type): Add INLINE_FRAME.
|
||||
(frame_id_inlined_p, get_stack_frame_id): New prototypes.
|
||||
* gdbthread.h (struct thread_info): Add step_stack_frame_id field.
|
||||
* infcmd.c (set_step_frame): New function.
|
||||
(step_once): Use set_step_frame. Handle inlined functions.
|
||||
(until_next_command): Use set_step_frame.
|
||||
(finish_backward), finish_forward): Use get_stack_frame_id.
|
||||
(finish_command): Support inlined functions.
|
||||
* inferior.h (set_step_info): New prototype.
|
||||
* infrun.c (RESUME_ALL): Use minus_one_ptid.
|
||||
(clear_proceed_status): Clear step_stack_frame_id.
|
||||
(init_wait_for_inferior): Call clear_inline_frame_state.
|
||||
(init_execution_control_state): Make static.
|
||||
(set_step_info): New function.
|
||||
(init_thread_stepping_state): Do not set the symtab or line here.
|
||||
(stepped_in_from): New function.
|
||||
(handle_inferior_event): Handle inlined functions. Use set_step_info.
|
||||
(insert_step_resume_breakpoint_at_frame): Use get_stack_frame_id.
|
||||
(struct inferior_status): Add step_stack_frame_id.
|
||||
(save_inferior_status, restore_inferior_status): Save and restore
|
||||
step_stack_frame_id.
|
||||
* inline-frame.c, inline-frame.h: New files.
|
||||
* minsyms.c (prim_record_minimal_symbol_and_info): Use XCALLOC.
|
||||
* regcache.c (regcache_write_pc): Call reinit_frame_cache.
|
||||
* s390-tdep.c (s390_prologue_frame_unwind_cache): Handle INLINE_FRAME.
|
||||
* stack.c (frame_show_address): New.
|
||||
(print_frame_info, print_frame): Use it.
|
||||
(find_frame_funname): Use get_frame_function. Handle inlined blocks.
|
||||
(frame_info): Mark inlined functions.
|
||||
(backtrace_command_1): Use get_current_user_frame.
|
||||
(print_frame_local_vars, print_frame_label_vars): Update comments.
|
||||
(return_command): Refuse inlined functions.
|
||||
* symtab.c (lookup_symbol_aux_local): Stop at inlined function
|
||||
boundaries.
|
||||
(find_function_start_sal): Avoid inlined functions.
|
||||
(completion_list_add_fields): New function.
|
||||
(default_make_symbol_completion_list): Use it. Use block_static_block
|
||||
and block_global_block. Check for inlined functions.
|
||||
(skip_prologue_using_sal): Avoid line number comparison across
|
||||
inlining.
|
||||
* symtab.h (struct symbol): Add is_inlined.
|
||||
(SYMBOL_INLINED): New.
|
||||
* target.c (target_resume): Call clear_inline_frame_state.
|
||||
* valops.c (value_of_variable): Check block_inlined_p.
|
||||
|
||||
2009-06-27 Daniel Jacobowitz <dan@codesourcery.com>
|
||||
|
||||
* frame.c (frame_unwind_id): Renamed to ...
|
||||
|
|
|
@ -645,6 +645,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
|
|||
inf-loop.c \
|
||||
infcall.c \
|
||||
infcmd.c inflow.c infrun.c \
|
||||
inline-frame.c \
|
||||
interps.c \
|
||||
jv-exp.y jv-lang.c jv-valprint.c jv-typeprint.c \
|
||||
language.c linespec.c \
|
||||
|
@ -817,6 +818,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
|
|||
user-regs.o \
|
||||
frame.o frame-unwind.o doublest.o \
|
||||
frame-base.o \
|
||||
inline-frame.o \
|
||||
gnu-v2-abi.o gnu-v3-abi.o cp-abi.o cp-support.o \
|
||||
cp-namespace.o \
|
||||
reggroups.o regset.o \
|
||||
|
|
3
gdb/NEWS
3
gdb/NEWS
|
@ -47,6 +47,9 @@ For instance, consider:
|
|||
If the user types TAB at the end of this command line, the available
|
||||
completions will be "f1" and "f2".
|
||||
|
||||
* Inlined functions are now supported. They show up in backtraces, and
|
||||
the "step", "next", and "finish" commands handle them automatically.
|
||||
|
||||
* GDB now supports the token-splicing (##) and stringification (#)
|
||||
operators when expanding macros. It also supports variable-arity
|
||||
macros.
|
||||
|
|
23
gdb/block.c
23
gdb/block.c
|
@ -47,8 +47,16 @@ contained_in (const struct block *a, const struct block *b)
|
|||
{
|
||||
if (!a || !b)
|
||||
return 0;
|
||||
return BLOCK_START (a) >= BLOCK_START (b)
|
||||
&& BLOCK_END (a) <= BLOCK_END (b);
|
||||
|
||||
do
|
||||
{
|
||||
if (a == b)
|
||||
return 1;
|
||||
a = BLOCK_SUPERBLOCK (a);
|
||||
}
|
||||
while (a != NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -60,12 +68,21 @@ contained_in (const struct block *a, const struct block *b)
|
|||
struct symbol *
|
||||
block_linkage_function (const struct block *bl)
|
||||
{
|
||||
while (BLOCK_FUNCTION (bl) == 0 && BLOCK_SUPERBLOCK (bl) != 0)
|
||||
while ((BLOCK_FUNCTION (bl) == NULL || block_inlined_p (bl))
|
||||
&& BLOCK_SUPERBLOCK (bl) != NULL)
|
||||
bl = BLOCK_SUPERBLOCK (bl);
|
||||
|
||||
return BLOCK_FUNCTION (bl);
|
||||
}
|
||||
|
||||
/* Return one if BL represents an inlined function. */
|
||||
|
||||
int
|
||||
block_inlined_p (const struct block *bl)
|
||||
{
|
||||
return BLOCK_FUNCTION (bl) != NULL && SYMBOL_INLINED (BLOCK_FUNCTION (bl));
|
||||
}
|
||||
|
||||
/* Return the blockvector immediately containing the innermost lexical
|
||||
block containing the specified pc value and section, or 0 if there
|
||||
is none. PBLOCK is a pointer to the block. If PBLOCK is NULL, we
|
||||
|
|
|
@ -65,7 +65,7 @@ struct block
|
|||
CORE_ADDR endaddr;
|
||||
|
||||
/* The symbol that names this block, if the block is the body of a
|
||||
function; otherwise, zero. */
|
||||
function (real or inlined); otherwise, zero. */
|
||||
|
||||
struct symbol *function;
|
||||
|
||||
|
@ -134,6 +134,8 @@ enum { GLOBAL_BLOCK = 0, STATIC_BLOCK = 1, FIRST_LOCAL_BLOCK = 2 };
|
|||
|
||||
extern struct symbol *block_linkage_function (const struct block *);
|
||||
|
||||
extern int block_inlined_p (const struct block *block);
|
||||
|
||||
extern int contained_in (const struct block *, const struct block *);
|
||||
|
||||
extern struct blockvector *blockvector_for_pc (CORE_ADDR, struct block **);
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include "command.h"
|
||||
#include "gdbcmd.h"
|
||||
#include "block.h"
|
||||
#include "inline-frame.h"
|
||||
|
||||
/* Prototypes for exported functions. */
|
||||
|
||||
|
@ -61,11 +62,29 @@ struct block *
|
|||
get_frame_block (struct frame_info *frame, CORE_ADDR *addr_in_block)
|
||||
{
|
||||
const CORE_ADDR pc = get_frame_address_in_block (frame);
|
||||
struct frame_info *next_frame;
|
||||
struct block *bl;
|
||||
int inline_count;
|
||||
|
||||
if (addr_in_block)
|
||||
*addr_in_block = pc;
|
||||
|
||||
return block_for_pc (pc);
|
||||
bl = block_for_pc (pc);
|
||||
if (bl == NULL)
|
||||
return NULL;
|
||||
|
||||
inline_count = frame_inlined_callees (frame);
|
||||
|
||||
while (inline_count > 0)
|
||||
{
|
||||
if (block_inlined_p (bl))
|
||||
inline_count--;
|
||||
|
||||
bl = BLOCK_SUPERBLOCK (bl);
|
||||
gdb_assert (bl != NULL);
|
||||
}
|
||||
|
||||
return bl;
|
||||
}
|
||||
|
||||
CORE_ADDR
|
||||
|
@ -104,9 +123,14 @@ struct symbol *
|
|||
get_frame_function (struct frame_info *frame)
|
||||
{
|
||||
struct block *bl = get_frame_block (frame, 0);
|
||||
if (bl == 0)
|
||||
return 0;
|
||||
return block_linkage_function (bl);
|
||||
|
||||
if (bl == NULL)
|
||||
return NULL;
|
||||
|
||||
while (BLOCK_FUNCTION (bl) == NULL && BLOCK_SUPERBLOCK (bl) != NULL)
|
||||
bl = BLOCK_SUPERBLOCK (bl);
|
||||
|
||||
return BLOCK_FUNCTION (bl);
|
||||
}
|
||||
|
||||
|
||||
|
@ -350,8 +374,8 @@ block_innermost_frame (struct block *block)
|
|||
frame = get_current_frame ();
|
||||
while (frame != NULL)
|
||||
{
|
||||
calling_pc = get_frame_address_in_block (frame);
|
||||
if (calling_pc >= start && calling_pc < end)
|
||||
struct block *frame_block = get_frame_block (frame, NULL);
|
||||
if (frame_block != NULL && contained_in (frame_block, block))
|
||||
return frame;
|
||||
|
||||
frame = get_prev_frame (frame);
|
||||
|
|
111
gdb/breakpoint.c
111
gdb/breakpoint.c
|
@ -2772,40 +2772,38 @@ watchpoint_check (void *p)
|
|||
within_current_scope = 1;
|
||||
else
|
||||
{
|
||||
/* There is no current frame at this moment. If we're going to have
|
||||
any chance of handling watchpoints on local variables, we'll need
|
||||
the frame chain (so we can determine if we're in scope). */
|
||||
reinit_frame_cache ();
|
||||
struct frame_info *frame = get_current_frame ();
|
||||
struct gdbarch *frame_arch = get_frame_arch (frame);
|
||||
CORE_ADDR frame_pc = get_frame_pc (frame);
|
||||
|
||||
fr = frame_find_by_id (b->watchpoint_frame);
|
||||
within_current_scope = (fr != NULL);
|
||||
|
||||
/* If we've gotten confused in the unwinder, we might have
|
||||
returned a frame that can't describe this variable. */
|
||||
if (within_current_scope
|
||||
&& (block_linkage_function (b->exp_valid_block)
|
||||
!= get_frame_function (fr)))
|
||||
within_current_scope = 0;
|
||||
if (within_current_scope)
|
||||
{
|
||||
struct symbol *function;
|
||||
|
||||
function = get_frame_function (fr);
|
||||
if (function == NULL
|
||||
|| !contained_in (b->exp_valid_block,
|
||||
SYMBOL_BLOCK_VALUE (function)))
|
||||
within_current_scope = 0;
|
||||
}
|
||||
|
||||
/* in_function_epilogue_p() returns a non-zero value if we're still
|
||||
in the function but the stack frame has already been invalidated.
|
||||
Since we can't rely on the values of local variables after the
|
||||
stack has been destroyed, we are treating the watchpoint in that
|
||||
state as `not changed' without further checking.
|
||||
|
||||
vinschen/2003-09-04: The former implementation left out the case
|
||||
that the watchpoint frame couldn't be found by frame_find_by_id()
|
||||
because the current PC is currently in an epilogue. Calling
|
||||
gdbarch_in_function_epilogue_p() also when fr == NULL fixes that. */
|
||||
if (!within_current_scope || fr == get_current_frame ())
|
||||
{
|
||||
struct frame_info *frame = get_current_frame ();
|
||||
struct gdbarch *frame_arch = get_frame_arch (frame);
|
||||
CORE_ADDR frame_pc = get_frame_pc (frame);
|
||||
state as `not changed' without further checking. Don't mark
|
||||
watchpoints as changed if the current frame is in an epilogue -
|
||||
even if they are in some other frame, our view of the stack
|
||||
is likely to be wrong. */
|
||||
if (gdbarch_in_function_epilogue_p (frame_arch, frame_pc))
|
||||
return WP_VALUE_NOT_CHANGED;
|
||||
|
||||
if (gdbarch_in_function_epilogue_p (frame_arch, frame_pc))
|
||||
return WP_VALUE_NOT_CHANGED;
|
||||
}
|
||||
if (fr && within_current_scope)
|
||||
if (within_current_scope)
|
||||
/* If we end up stopping, the current frame will get selected
|
||||
in normal_stop. So this call to select_frame won't affect
|
||||
the user. */
|
||||
|
@ -3039,7 +3037,7 @@ bpstat_check_breakpoint_conditions (bpstat bs, ptid_t ptid)
|
|||
struct breakpoint *b = bl->owner;
|
||||
|
||||
if (frame_id_p (b->frame_id)
|
||||
&& !frame_id_eq (b->frame_id, get_frame_id (get_current_frame ())))
|
||||
&& !frame_id_eq (b->frame_id, get_stack_frame_id (get_current_frame ())))
|
||||
bs->stop = 0;
|
||||
else if (bs->stop)
|
||||
{
|
||||
|
@ -3061,8 +3059,12 @@ bpstat_check_breakpoint_conditions (bpstat bs, ptid_t ptid)
|
|||
function call. */
|
||||
struct value *mark = value_mark ();
|
||||
|
||||
/* Need to select the frame, with all that implies
|
||||
so that the conditions will have the right context. */
|
||||
/* Need to select the frame, with all that implies so that
|
||||
the conditions will have the right context. Because we
|
||||
use the frame, we will not see an inlined function's
|
||||
variables when we arrive at a breakpoint at the start
|
||||
of the inlined function; the current frame will be the
|
||||
call site. */
|
||||
select_frame (get_current_frame ());
|
||||
value_is_zero
|
||||
= catch_errors (breakpoint_cond_eval, (bl->cond),
|
||||
|
@ -5032,6 +5034,11 @@ set_momentary_breakpoint (struct symtab_and_line sal, struct frame_id frame_id,
|
|||
enum bptype type)
|
||||
{
|
||||
struct breakpoint *b;
|
||||
|
||||
/* If FRAME_ID is valid, it should be a real frame, not an inlined
|
||||
one. */
|
||||
gdb_assert (!frame_id_inlined_p (frame_id));
|
||||
|
||||
b = set_raw_breakpoint (sal, type);
|
||||
b->enable_state = bp_enabled;
|
||||
b->disposition = disp_donttouch;
|
||||
|
@ -6143,7 +6150,6 @@ watch_command_1 (char *arg, int accessflag, int from_tty)
|
|||
struct block *exp_valid_block;
|
||||
struct value *val, *mark;
|
||||
struct frame_info *frame;
|
||||
struct frame_info *prev_frame = NULL;
|
||||
char *exp_start = NULL;
|
||||
char *exp_end = NULL;
|
||||
char *tok, *id_tok_start, *end_tok;
|
||||
|
@ -6283,34 +6289,34 @@ watch_command_1 (char *arg, int accessflag, int from_tty)
|
|||
bp_type = bp_watchpoint;
|
||||
|
||||
frame = block_innermost_frame (exp_valid_block);
|
||||
if (frame)
|
||||
prev_frame = get_prev_frame (frame);
|
||||
else
|
||||
prev_frame = NULL;
|
||||
|
||||
/* If the expression is "local", then set up a "watchpoint scope"
|
||||
breakpoint at the point where we've left the scope of the watchpoint
|
||||
expression. Create the scope breakpoint before the watchpoint, so
|
||||
that we will encounter it first in bpstat_stop_status. */
|
||||
if (innermost_block && prev_frame)
|
||||
if (innermost_block && frame)
|
||||
{
|
||||
scope_breakpoint = create_internal_breakpoint (get_frame_pc (prev_frame),
|
||||
bp_watchpoint_scope);
|
||||
if (frame_id_p (frame_unwind_caller_id (frame)))
|
||||
{
|
||||
scope_breakpoint
|
||||
= create_internal_breakpoint (frame_unwind_caller_pc (frame),
|
||||
bp_watchpoint_scope);
|
||||
|
||||
scope_breakpoint->enable_state = bp_enabled;
|
||||
scope_breakpoint->enable_state = bp_enabled;
|
||||
|
||||
/* Automatically delete the breakpoint when it hits. */
|
||||
scope_breakpoint->disposition = disp_del;
|
||||
/* Automatically delete the breakpoint when it hits. */
|
||||
scope_breakpoint->disposition = disp_del;
|
||||
|
||||
/* Only break in the proper frame (help with recursion). */
|
||||
scope_breakpoint->frame_id = get_frame_id (prev_frame);
|
||||
/* Only break in the proper frame (help with recursion). */
|
||||
scope_breakpoint->frame_id = frame_unwind_caller_id (frame);
|
||||
|
||||
/* Set the address at which we will stop. */
|
||||
scope_breakpoint->loc->requested_address
|
||||
= get_frame_pc (prev_frame);
|
||||
scope_breakpoint->loc->address
|
||||
= adjust_breakpoint_address (scope_breakpoint->loc->requested_address,
|
||||
scope_breakpoint->type);
|
||||
/* Set the address at which we will stop. */
|
||||
scope_breakpoint->loc->requested_address
|
||||
= frame_unwind_caller_pc (frame);
|
||||
scope_breakpoint->loc->address
|
||||
= adjust_breakpoint_address (scope_breakpoint->loc->requested_address,
|
||||
scope_breakpoint->type);
|
||||
}
|
||||
}
|
||||
|
||||
/* Now set up the breakpoint. */
|
||||
|
@ -6491,7 +6497,6 @@ 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 frame_info *prev_frame = get_prev_frame (frame);
|
||||
struct breakpoint *breakpoint;
|
||||
struct breakpoint *breakpoint2 = NULL;
|
||||
struct cleanup *old_chain;
|
||||
|
@ -6524,20 +6529,22 @@ until_break_command (char *arg, int from_tty, int anywhere)
|
|||
we don't specify a frame at which we need to stop. */
|
||||
breakpoint = set_momentary_breakpoint (sal, null_frame_id, bp_until);
|
||||
else
|
||||
/* Otherwise, specify the current frame, because we want to stop only
|
||||
/* Otherwise, specify the selected frame, because we want to stop only
|
||||
at the very same frame. */
|
||||
breakpoint = set_momentary_breakpoint (sal, get_frame_id (frame),
|
||||
breakpoint = set_momentary_breakpoint (sal, get_stack_frame_id (frame),
|
||||
bp_until);
|
||||
|
||||
old_chain = make_cleanup_delete_breakpoint (breakpoint);
|
||||
|
||||
/* Keep within the current frame, or in frames called by the current
|
||||
one. */
|
||||
if (prev_frame)
|
||||
|
||||
if (frame_id_p (frame_unwind_caller_id (frame)))
|
||||
{
|
||||
sal = find_pc_line (get_frame_pc (prev_frame), 0);
|
||||
sal.pc = get_frame_pc (prev_frame);
|
||||
breakpoint2 = set_momentary_breakpoint (sal, get_frame_id (prev_frame),
|
||||
sal = find_pc_line (frame_unwind_caller_pc (frame), 0);
|
||||
sal.pc = frame_unwind_caller_pc (frame);
|
||||
breakpoint2 = set_momentary_breakpoint (sal,
|
||||
frame_unwind_caller_id (frame),
|
||||
bp_until);
|
||||
make_cleanup_delete_breakpoint (breakpoint2);
|
||||
}
|
||||
|
|
|
@ -1187,6 +1187,12 @@ end_symtab (CORE_ADDR end_addr, struct objfile *objfile, int section)
|
|||
struct symbol *sym;
|
||||
struct dict_iterator iter;
|
||||
|
||||
/* Inlined functions may have symbols not in the global or static
|
||||
symbol lists. */
|
||||
if (BLOCK_FUNCTION (block) != NULL)
|
||||
if (SYMBOL_SYMTAB (BLOCK_FUNCTION (block)) == NULL)
|
||||
SYMBOL_SYMTAB (BLOCK_FUNCTION (block)) = symtab;
|
||||
|
||||
for (sym = dict_iterator_first (BLOCK_DICT (block), &iter);
|
||||
sym != NULL;
|
||||
sym = dict_iterator_next (&iter))
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
2009-06-27 Daniel Jacobowitz <dan@codesourcery.com>
|
||||
|
||||
* gdb.texinfo (Debugging Optimized Code): New chapter.
|
||||
(Compiling for Debugging): Reference it. Move some
|
||||
text to the new section.
|
||||
|
||||
2009-06-15 Phil Muldoon <pmuldoon@redhat.com>
|
||||
|
||||
* doc/gdb.texinfo (Calling): Document
|
||||
|
|
|
@ -139,6 +139,7 @@ software in general. We will miss him.
|
|||
* Stack:: Examining the stack
|
||||
* Source:: Examining source files
|
||||
* Data:: Examining data
|
||||
* Optimized Code:: Debugging optimized code
|
||||
* Macros:: Preprocessor Macros
|
||||
* Tracepoints:: Debugging remote targets non-intrusively
|
||||
* Overlays:: Debugging programs that use overlays
|
||||
|
@ -1808,7 +1809,7 @@ To request debugging information, specify the @samp{-g} option when you run
|
|||
the compiler.
|
||||
|
||||
Programs that are to be shipped to your customers are compiled with
|
||||
optimizations, using the @samp{-O} compiler option. However, many
|
||||
optimizations, using the @samp{-O} compiler option. However, some
|
||||
compilers are unable to handle the @samp{-g} and @samp{-O} options
|
||||
together. Using those compilers, you cannot generate optimized
|
||||
executables containing debugging information.
|
||||
|
@ -1817,22 +1818,7 @@ executables containing debugging information.
|
|||
without @samp{-O}, making it possible to debug optimized code. We
|
||||
recommend that you @emph{always} use @samp{-g} whenever you compile a
|
||||
program. You may think your program is correct, but there is no sense
|
||||
in pushing your luck.
|
||||
|
||||
@cindex optimized code, debugging
|
||||
@cindex debugging optimized code
|
||||
When you debug a program compiled with @samp{-g -O}, remember that the
|
||||
optimizer is rearranging your code; the debugger shows you what is
|
||||
really there. Do not be too surprised when the execution path does not
|
||||
exactly match your source file! An extreme example: if you define a
|
||||
variable, but never use it, @value{GDBN} never sees that
|
||||
variable---because the compiler optimizes it out of existence.
|
||||
|
||||
Some things do not work as well with @samp{-g -O} as with just
|
||||
@samp{-g}, particularly on machines with instruction scheduling. If in
|
||||
doubt, recompile with @samp{-g} alone, and if this fixes the problem,
|
||||
please report it to us as a bug (including a test case!).
|
||||
@xref{Variables}, for more information about debugging optimized code.
|
||||
in pushing your luck. For more information, see @ref{Optimized Code}.
|
||||
|
||||
Older versions of the @sc{gnu} C compiler permitted a variant option
|
||||
@w{@samp{-gg}} for debugging information. @value{GDBN} no longer supports this
|
||||
|
@ -8538,6 +8524,107 @@ $1 = 1
|
|||
$2 = (void *) 0x8049560
|
||||
@end smallexample
|
||||
|
||||
@node Optimized Code
|
||||
@chapter Debugging Optimized Code
|
||||
@cindex optimized code, debugging
|
||||
@cindex debugging optimized code
|
||||
|
||||
Almost all compilers support optimization. With optimization
|
||||
disabled, the compiler generates assembly code that corresponds
|
||||
directly to your source code, in a simplistic way. As the compiler
|
||||
applies more powerful optimizations, the generated assembly code
|
||||
diverges from your original source code. With help from debugging
|
||||
information generated by the compiler, @value{GDBN} can map from
|
||||
the running program back to constructs from your original source.
|
||||
|
||||
@value{GDBN} is more accurate with optimization disabled. If you
|
||||
can recompile without optimization, it is easier to follow the
|
||||
progress of your program during debugging. But, there are many cases
|
||||
where you may need to debug an optimized version.
|
||||
|
||||
When you debug a program compiled with @samp{-g -O}, remember that the
|
||||
optimizer has rearranged your code; the debugger shows you what is
|
||||
really there. Do not be too surprised when the execution path does not
|
||||
exactly match your source file! An extreme example: if you define a
|
||||
variable, but never use it, @value{GDBN} never sees that
|
||||
variable---because the compiler optimizes it out of existence.
|
||||
|
||||
Some things do not work as well with @samp{-g -O} as with just
|
||||
@samp{-g}, particularly on machines with instruction scheduling. If in
|
||||
doubt, recompile with @samp{-g} alone, and if this fixes the problem,
|
||||
please report it to us as a bug (including a test case!).
|
||||
@xref{Variables}, for more information about debugging optimized code.
|
||||
|
||||
@menu
|
||||
* Inline Functions:: How @value{GDBN} presents inlining
|
||||
@end menu
|
||||
|
||||
@node Inline Functions
|
||||
@section Inline Functions
|
||||
@cindex inline functions, debugging
|
||||
|
||||
@dfn{Inlining} is an optimization that inserts a copy of the function
|
||||
body directly at each call site, instead of jumping to a shared
|
||||
routine. @value{GDBN} displays inlined functions just like
|
||||
non-inlined functions. They appear in backtraces. You can view their
|
||||
arguments and local variables, step into them with @code{step}, skip
|
||||
them with @code{next}, and escape from them with @code{finish}.
|
||||
You can check whether a function was inlined by using the
|
||||
@code{info frame} command.
|
||||
|
||||
For @value{GDBN} to support inlined functions, the compiler must
|
||||
record information about inlining in the debug information ---
|
||||
@value{NGCC} using the @sc{dwarf 2} format does this, and several
|
||||
other compilers do also. @value{GDBN} only supports inlined functions
|
||||
when using @sc{dwarf 2}. Versions of @value{NGCC} before 4.1
|
||||
do not emit two required attributes (@samp{DW_AT_call_file} and
|
||||
@samp{DW_AT_call_line}); @value{GDBN} does not display inlined
|
||||
function calls with earlier versions of @value{NGCC}. It instead
|
||||
displays the arguments and local variables of inlined functions as
|
||||
local variables in the caller.
|
||||
|
||||
The body of an inlined function is directly included at its call site;
|
||||
unlike a non-inlined function, there are no instructions devoted to
|
||||
the call. @value{GDBN} still pretends that the call site and the
|
||||
start of the inlined function are different instructions. Stepping to
|
||||
the call site shows the call site, and then stepping again shows
|
||||
the first line of the inlined function, even though no additional
|
||||
instructions are executed.
|
||||
|
||||
This makes source-level debugging much clearer; you can see both the
|
||||
context of the call and then the effect of the call. Only stepping by
|
||||
a single instruction using @code{stepi} or @code{nexti} does not do
|
||||
this; single instruction steps always show the inlined body.
|
||||
|
||||
There are some ways that @value{GDBN} does not pretend that inlined
|
||||
function calls are the same as normal calls:
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
You cannot set breakpoints on inlined functions. @value{GDBN}
|
||||
either reports that there is no symbol with that name, or else sets the
|
||||
breakpoint only on non-inlined copies of the function. This limitation
|
||||
will be removed in a future version of @value{GDBN}; until then,
|
||||
set a breakpoint by line number on the first line of the inlined
|
||||
function instead.
|
||||
|
||||
@item
|
||||
Setting breakpoints at the call site of an inlined function may not
|
||||
work, because the call site does not contain any code. @value{GDBN}
|
||||
may incorrectly move the breakpoint to the next line of the enclosing
|
||||
function, after the call. This limitation will be removed in a future
|
||||
version of @value{GDBN}; until then, set a breakpoint on an earlier line
|
||||
or inside the inlined function instead.
|
||||
|
||||
@item
|
||||
@value{GDBN} cannot locate the return value of inlined calls after
|
||||
using the @code{finish} command. This is a limitation of compiler-generated
|
||||
debugging information; after @code{finish}, you can step to the next line
|
||||
and print a variable where your program stored the return value.
|
||||
|
||||
@end itemize
|
||||
|
||||
|
||||
@node Macros
|
||||
@chapter C Preprocessor Macros
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "regcache.h"
|
||||
#include "objfiles.h"
|
||||
#include "exceptions.h"
|
||||
#include "block.h"
|
||||
|
||||
#include "elf/dwarf2.h"
|
||||
#include "dwarf2expr.h"
|
||||
|
@ -147,14 +148,19 @@ dwarf_expr_frame_base (void *baton, gdb_byte **start, size_t * length)
|
|||
struct symbol *framefunc;
|
||||
struct dwarf_expr_baton *debaton = (struct dwarf_expr_baton *) baton;
|
||||
|
||||
framefunc = get_frame_function (debaton->frame);
|
||||
/* Use block_linkage_function, which returns a real (not inlined)
|
||||
function, instead of get_frame_function, which may return an
|
||||
inlined function. */
|
||||
framefunc = block_linkage_function (get_frame_block (debaton->frame, NULL));
|
||||
|
||||
/* If we found a frame-relative symbol then it was certainly within
|
||||
some function associated with a frame. If we can't find the frame,
|
||||
something has gone wrong. */
|
||||
gdb_assert (framefunc != NULL);
|
||||
|
||||
if (SYMBOL_COMPUTED_OPS (framefunc) == &dwarf2_loclist_funcs)
|
||||
if (SYMBOL_LOCATION_BATON (framefunc) == NULL)
|
||||
*start = NULL;
|
||||
else if (SYMBOL_COMPUTED_OPS (framefunc) == &dwarf2_loclist_funcs)
|
||||
{
|
||||
struct dwarf2_loclist_baton *symbaton;
|
||||
struct frame_info *frame = debaton->frame;
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include "hashtab.h"
|
||||
#include "command.h"
|
||||
#include "gdbcmd.h"
|
||||
#include "block.h"
|
||||
#include "addrmap.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
|
@ -2932,12 +2933,8 @@ process_die (struct die_info *die, struct dwarf2_cu *cu)
|
|||
read_file_scope (die, cu);
|
||||
break;
|
||||
case DW_TAG_subprogram:
|
||||
read_func_scope (die, cu);
|
||||
break;
|
||||
case DW_TAG_inlined_subroutine:
|
||||
/* FIXME: These are ignored for now.
|
||||
They could be used to set breakpoints on all inlined instances
|
||||
of a function and make GDB `next' properly over inlined functions. */
|
||||
read_func_scope (die, cu);
|
||||
break;
|
||||
case DW_TAG_lexical_block:
|
||||
case DW_TAG_try_block:
|
||||
|
@ -3291,7 +3288,9 @@ inherit_abstract_dies (struct die_info *die, struct dwarf2_cu *cu)
|
|||
return;
|
||||
|
||||
origin_die = follow_die_ref (die, attr, &cu);
|
||||
if (die->tag != origin_die->tag)
|
||||
if (die->tag != origin_die->tag
|
||||
&& !(die->tag == DW_TAG_inlined_subroutine
|
||||
&& origin_die->tag == DW_TAG_subprogram))
|
||||
complaint (&symfile_complaints,
|
||||
_("DIE 0x%x and its abstract origin 0x%x have different tags"),
|
||||
die->offset, origin_die->offset);
|
||||
|
@ -3318,7 +3317,9 @@ inherit_abstract_dies (struct die_info *die, struct dwarf2_cu *cu)
|
|||
struct die_info *child_origin_die;
|
||||
|
||||
child_origin_die = follow_die_ref (child_die, attr, &cu);
|
||||
if (child_die->tag != child_origin_die->tag)
|
||||
if (child_die->tag != child_origin_die->tag
|
||||
&& !(child_die->tag == DW_TAG_inlined_subroutine
|
||||
&& child_origin_die->tag == DW_TAG_subprogram))
|
||||
complaint (&symfile_complaints,
|
||||
_("Child DIE 0x%x and its abstract origin 0x%x have "
|
||||
"different tags"), child_die->offset,
|
||||
|
@ -3361,10 +3362,25 @@ read_func_scope (struct die_info *die, struct dwarf2_cu *cu)
|
|||
CORE_ADDR lowpc;
|
||||
CORE_ADDR highpc;
|
||||
struct die_info *child_die;
|
||||
struct attribute *attr;
|
||||
struct attribute *attr, *call_line, *call_file;
|
||||
char *name;
|
||||
CORE_ADDR baseaddr;
|
||||
struct block *block;
|
||||
int inlined_func = (die->tag == DW_TAG_inlined_subroutine);
|
||||
|
||||
if (inlined_func)
|
||||
{
|
||||
/* If we do not have call site information, we can't show the
|
||||
caller of this inlined function. That's too confusing, so
|
||||
only use the scope for local variables. */
|
||||
call_line = dwarf2_attr (die, DW_AT_call_line, cu);
|
||||
call_file = dwarf2_attr (die, DW_AT_call_file, cu);
|
||||
if (call_line == NULL || call_file == NULL)
|
||||
{
|
||||
read_lexical_block_scope (die, cu);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
baseaddr = ANOFFSET (objfile->section_offsets, SECT_OFF_TEXT (objfile));
|
||||
|
||||
|
@ -7016,7 +7032,9 @@ die_is_declaration (struct die_info *die, struct dwarf2_cu *cu)
|
|||
|
||||
/* Return the die giving the specification for DIE, if there is
|
||||
one. *SPEC_CU is the CU containing DIE on input, and the CU
|
||||
containing the return value on output. */
|
||||
containing the return value on output. If there is no
|
||||
specification, but there is an abstract origin, that is
|
||||
returned. */
|
||||
|
||||
static struct die_info *
|
||||
die_specification (struct die_info *die, struct dwarf2_cu **spec_cu)
|
||||
|
@ -7024,6 +7042,9 @@ die_specification (struct die_info *die, struct dwarf2_cu **spec_cu)
|
|||
struct attribute *spec_attr = dwarf2_attr (die, DW_AT_specification,
|
||||
*spec_cu);
|
||||
|
||||
if (spec_attr == NULL)
|
||||
spec_attr = dwarf2_attr (die, DW_AT_abstract_origin, *spec_cu);
|
||||
|
||||
if (spec_attr == NULL)
|
||||
return NULL;
|
||||
else
|
||||
|
@ -7720,6 +7741,7 @@ new_symbol (struct die_info *die, struct type *type, struct dwarf2_cu *cu)
|
|||
struct attribute *attr = NULL;
|
||||
struct attribute *attr2 = NULL;
|
||||
CORE_ADDR baseaddr;
|
||||
int inlined_func = (die->tag == DW_TAG_inlined_subroutine);
|
||||
|
||||
baseaddr = ANOFFSET (objfile->section_offsets, SECT_OFF_TEXT (objfile));
|
||||
|
||||
|
@ -7747,13 +7769,17 @@ new_symbol (struct die_info *die, struct type *type, struct dwarf2_cu *cu)
|
|||
SYMBOL_TYPE (sym) = type;
|
||||
else
|
||||
SYMBOL_TYPE (sym) = die_type (die, cu);
|
||||
attr = dwarf2_attr (die, DW_AT_decl_line, cu);
|
||||
attr = dwarf2_attr (die,
|
||||
inlined_func ? DW_AT_call_line : DW_AT_decl_line,
|
||||
cu);
|
||||
if (attr)
|
||||
{
|
||||
SYMBOL_LINE (sym) = DW_UNSND (attr);
|
||||
}
|
||||
|
||||
attr = dwarf2_attr (die, DW_AT_decl_file, cu);
|
||||
attr = dwarf2_attr (die,
|
||||
inlined_func ? DW_AT_call_file : DW_AT_decl_file,
|
||||
cu);
|
||||
if (attr)
|
||||
{
|
||||
int file_index = DW_UNSND (attr);
|
||||
|
@ -7800,6 +7826,14 @@ new_symbol (struct die_info *die, struct type *type, struct dwarf2_cu *cu)
|
|||
add_symbol_to_list (sym, cu->list_in_scope);
|
||||
}
|
||||
break;
|
||||
case DW_TAG_inlined_subroutine:
|
||||
/* SYMBOL_BLOCK_VALUE (sym) will be filled in later by
|
||||
finish_block. */
|
||||
SYMBOL_CLASS (sym) = LOC_BLOCK;
|
||||
SYMBOL_INLINED (sym) = 1;
|
||||
/* Do not add the symbol to any lists. It will be found via
|
||||
BLOCK_FUNCTION from the blockvector. */
|
||||
break;
|
||||
case DW_TAG_variable:
|
||||
/* Compilation with minimal debug info may result in variables
|
||||
with missing type entries. Change the misleading `void' type
|
||||
|
@ -7853,7 +7887,14 @@ new_symbol (struct die_info *die, struct type *type, struct dwarf2_cu *cu)
|
|||
}
|
||||
break;
|
||||
case DW_TAG_formal_parameter:
|
||||
SYMBOL_IS_ARGUMENT (sym) = 1;
|
||||
/* If we are inside a function, mark this as an argument. If
|
||||
not, we might be looking at an argument to an inlined function
|
||||
when we do not have enough information to show inlined frames;
|
||||
pretend it's a local variable in that case so that the user can
|
||||
still see it. */
|
||||
if (context_stack_depth > 0
|
||||
&& context_stack[context_stack_depth - 1].name != NULL)
|
||||
SYMBOL_IS_ARGUMENT (sym) = 1;
|
||||
attr = dwarf2_attr (die, DW_AT_location, cu);
|
||||
if (attr)
|
||||
{
|
||||
|
@ -8194,6 +8235,7 @@ read_type_die (struct die_info *die, struct dwarf2_cu *cu)
|
|||
break;
|
||||
case DW_TAG_subprogram:
|
||||
case DW_TAG_subroutine_type:
|
||||
case DW_TAG_inlined_subroutine:
|
||||
this_type = read_subroutine_type (die, cu);
|
||||
break;
|
||||
case DW_TAG_array_type:
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "frame.h"
|
||||
#include "frame-unwind.h"
|
||||
#include "dummy-frame.h"
|
||||
#include "inline-frame.h"
|
||||
#include "value.h"
|
||||
#include "regcache.h"
|
||||
|
||||
|
@ -51,8 +52,10 @@ frame_unwind_init (struct obstack *obstack)
|
|||
can't override this. */
|
||||
table->list = OBSTACK_ZALLOC (obstack, struct frame_unwind_table_entry);
|
||||
table->list->unwinder = dummy_frame_unwind;
|
||||
table->list->next = OBSTACK_ZALLOC (obstack, struct frame_unwind_table_entry);
|
||||
table->list->next->unwinder = inline_frame_unwind;
|
||||
/* The insertion point for OSABI sniffers. */
|
||||
table->osabi_head = &table->list->next;
|
||||
table->osabi_head = &table->list->next->next;
|
||||
return table;
|
||||
}
|
||||
|
||||
|
|
203
gdb/frame.c
203
gdb/frame.c
|
@ -41,8 +41,11 @@
|
|||
#include "objfiles.h"
|
||||
#include "exceptions.h"
|
||||
#include "gdbthread.h"
|
||||
#include "block.h"
|
||||
#include "inline-frame.h"
|
||||
|
||||
static struct frame_info *get_prev_frame_1 (struct frame_info *this_frame);
|
||||
static struct frame_info *get_prev_frame_raw (struct frame_info *this_frame);
|
||||
|
||||
/* We keep a cache of stack frames, each of which is a "struct
|
||||
frame_info". The innermost one gets allocated (in
|
||||
|
@ -173,6 +176,8 @@ fprint_frame_id (struct ui_file *file, struct frame_id id)
|
|||
fprint_field (file, "code", id.code_addr_p, id.code_addr);
|
||||
fprintf_unfiltered (file, ",");
|
||||
fprint_field (file, "special", id.special_addr_p, id.special_addr);
|
||||
if (id.inline_depth)
|
||||
fprintf_unfiltered (file, ",inlined=%d", id.inline_depth);
|
||||
fprintf_unfiltered (file, "}");
|
||||
}
|
||||
|
||||
|
@ -187,6 +192,12 @@ fprint_frame_type (struct ui_file *file, enum frame_type type)
|
|||
case DUMMY_FRAME:
|
||||
fprintf_unfiltered (file, "DUMMY_FRAME");
|
||||
return;
|
||||
case INLINE_FRAME:
|
||||
fprintf_unfiltered (file, "INLINE_FRAME");
|
||||
return;
|
||||
case SENTINEL_FRAME:
|
||||
fprintf_unfiltered (file, "SENTINEL_FRAME");
|
||||
return;
|
||||
case SIGTRAMP_FRAME:
|
||||
fprintf_unfiltered (file, "SIGTRAMP_FRAME");
|
||||
return;
|
||||
|
@ -239,6 +250,18 @@ fprint_frame (struct ui_file *file, struct frame_info *fi)
|
|||
fprintf_unfiltered (file, "}");
|
||||
}
|
||||
|
||||
/* Given FRAME, return the enclosing normal frame for inlined
|
||||
function frames. Otherwise return the original frame. */
|
||||
|
||||
static struct frame_info *
|
||||
skip_inlined_frames (struct frame_info *frame)
|
||||
{
|
||||
while (get_frame_type (frame) == INLINE_FRAME)
|
||||
frame = get_prev_frame (frame);
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
/* Return a frame uniq ID that can be used to, later, re-find the
|
||||
frame. */
|
||||
|
||||
|
@ -270,14 +293,28 @@ get_frame_id (struct frame_info *fi)
|
|||
return fi->this_id.value;
|
||||
}
|
||||
|
||||
struct frame_id
|
||||
get_stack_frame_id (struct frame_info *next_frame)
|
||||
{
|
||||
return get_frame_id (skip_inlined_frames (next_frame));
|
||||
}
|
||||
|
||||
struct frame_id
|
||||
frame_unwind_caller_id (struct frame_info *next_frame)
|
||||
{
|
||||
/* Use prev_frame, and not get_prev_frame. The latter will truncate
|
||||
struct frame_info *this_frame;
|
||||
|
||||
/* Use get_prev_frame_1, and not get_prev_frame. The latter will truncate
|
||||
the frame chain, leading to this function unintentionally
|
||||
returning a null_frame_id (e.g., when a caller requests the frame
|
||||
ID of "main()"s caller. */
|
||||
return get_frame_id (get_prev_frame_1 (next_frame));
|
||||
|
||||
next_frame = skip_inlined_frames (next_frame);
|
||||
this_frame = get_prev_frame_1 (next_frame);
|
||||
if (this_frame)
|
||||
return get_frame_id (skip_inlined_frames (this_frame));
|
||||
else
|
||||
return null_frame_id;
|
||||
}
|
||||
|
||||
const struct frame_id null_frame_id; /* All zeros. */
|
||||
|
@ -331,6 +368,15 @@ frame_id_p (struct frame_id l)
|
|||
return p;
|
||||
}
|
||||
|
||||
int
|
||||
frame_id_inlined_p (struct frame_id l)
|
||||
{
|
||||
if (!frame_id_p (l))
|
||||
return 0;
|
||||
|
||||
return (l.inline_depth != 0);
|
||||
}
|
||||
|
||||
int
|
||||
frame_id_eq (struct frame_id l, struct frame_id r)
|
||||
{
|
||||
|
@ -342,21 +388,22 @@ frame_id_eq (struct frame_id l, struct frame_id r)
|
|||
else if (l.stack_addr != r.stack_addr)
|
||||
/* If .stack addresses are different, the frames are different. */
|
||||
eq = 0;
|
||||
else if (!l.code_addr_p || !r.code_addr_p)
|
||||
/* An invalid code addr is a wild card, always succeed. */
|
||||
eq = 1;
|
||||
else if (l.code_addr != r.code_addr)
|
||||
/* If .code addresses are different, the frames are different. */
|
||||
else if (l.code_addr_p && r.code_addr_p && l.code_addr != r.code_addr)
|
||||
/* An invalid code addr is a wild card. If .code addresses are
|
||||
different, the frames are different. */
|
||||
eq = 0;
|
||||
else if (!l.special_addr_p || !r.special_addr_p)
|
||||
/* An invalid special addr is a wild card (or unused), always succeed. */
|
||||
eq = 1;
|
||||
else if (l.special_addr == r.special_addr)
|
||||
else if (l.special_addr_p && r.special_addr_p
|
||||
&& l.special_addr != r.special_addr)
|
||||
/* An invalid special addr is a wild card (or unused). Otherwise
|
||||
if special addresses are different, the frames are different. */
|
||||
eq = 0;
|
||||
else if (l.inline_depth != r.inline_depth)
|
||||
/* If inline depths are different, the frames must be different. */
|
||||
eq = 0;
|
||||
else
|
||||
/* Frames are equal. */
|
||||
eq = 1;
|
||||
else
|
||||
/* No luck. */
|
||||
eq = 0;
|
||||
|
||||
if (frame_debug)
|
||||
{
|
||||
fprintf_unfiltered (gdb_stdlog, "{ frame_id_eq (l=");
|
||||
|
@ -407,6 +454,29 @@ frame_id_inner (struct gdbarch *gdbarch, struct frame_id l, struct frame_id r)
|
|||
if (!l.stack_addr_p || !r.stack_addr_p)
|
||||
/* Like NaN, any operation involving an invalid ID always fails. */
|
||||
inner = 0;
|
||||
else if (l.inline_depth > r.inline_depth
|
||||
&& l.stack_addr == r.stack_addr
|
||||
&& l.code_addr_p == r.code_addr_p
|
||||
&& l.special_addr_p == r.special_addr_p
|
||||
&& l.special_addr == r.special_addr)
|
||||
{
|
||||
/* Same function, different inlined functions. */
|
||||
struct block *lb, *rb;
|
||||
|
||||
gdb_assert (l.code_addr_p && r.code_addr_p);
|
||||
|
||||
lb = block_for_pc (l.code_addr);
|
||||
rb = block_for_pc (r.code_addr);
|
||||
|
||||
if (lb == NULL || rb == NULL)
|
||||
/* Something's gone wrong. */
|
||||
inner = 0;
|
||||
else
|
||||
/* This will return true if LB and RB are the same block, or
|
||||
if the block with the smaller depth lexically encloses the
|
||||
block with the greater depth. */
|
||||
inner = contained_in (lb, rb);
|
||||
}
|
||||
else
|
||||
/* Only return non-zero when strictly inner than. Note that, per
|
||||
comment in "frame.h", there is some fuzz here. Frameless
|
||||
|
@ -459,8 +529,8 @@ frame_find_by_id (struct frame_id id)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
CORE_ADDR
|
||||
frame_unwind_caller_pc (struct frame_info *this_frame)
|
||||
static CORE_ADDR
|
||||
frame_unwind_pc (struct frame_info *this_frame)
|
||||
{
|
||||
if (!this_frame->prev_pc.p)
|
||||
{
|
||||
|
@ -498,6 +568,12 @@ frame_unwind_caller_pc (struct frame_info *this_frame)
|
|||
return this_frame->prev_pc.value;
|
||||
}
|
||||
|
||||
CORE_ADDR
|
||||
frame_unwind_caller_pc (struct frame_info *this_frame)
|
||||
{
|
||||
return frame_unwind_pc (skip_inlined_frames (this_frame));
|
||||
}
|
||||
|
||||
CORE_ADDR
|
||||
get_frame_func (struct frame_info *this_frame)
|
||||
{
|
||||
|
@ -1233,7 +1309,6 @@ frame_register_unwind_location (struct frame_info *this_frame, int regnum,
|
|||
static struct frame_info *
|
||||
get_prev_frame_1 (struct frame_info *this_frame)
|
||||
{
|
||||
struct frame_info *prev_frame;
|
||||
struct frame_id this_id;
|
||||
struct gdbarch *gdbarch;
|
||||
|
||||
|
@ -1273,6 +1348,14 @@ get_prev_frame_1 (struct frame_info *this_frame)
|
|||
this_frame->prev_p = 1;
|
||||
this_frame->stop_reason = UNWIND_NO_REASON;
|
||||
|
||||
/* If we are unwinding from an inline frame, all of the below tests
|
||||
were already performed when we unwound from the next non-inline
|
||||
frame. We must skip them, since we can not get THIS_FRAME's ID
|
||||
until we have unwound all the way down to the previous non-inline
|
||||
frame. */
|
||||
if (get_frame_type (this_frame) == INLINE_FRAME)
|
||||
return get_prev_frame_raw (this_frame);
|
||||
|
||||
/* Check that this frame's ID was valid. If it wasn't, don't try to
|
||||
unwind to the prev frame. Be careful to not apply this test to
|
||||
the sentinel frame. */
|
||||
|
@ -1341,7 +1424,8 @@ get_prev_frame_1 (struct frame_info *this_frame)
|
|||
if (this_frame->level > 0
|
||||
&& gdbarch_pc_regnum (gdbarch) >= 0
|
||||
&& get_frame_type (this_frame) == NORMAL_FRAME
|
||||
&& get_frame_type (this_frame->next) == NORMAL_FRAME)
|
||||
&& (get_frame_type (this_frame->next) == NORMAL_FRAME
|
||||
|| get_frame_type (this_frame->next) == INLINE_FRAME))
|
||||
{
|
||||
int optimized, realnum, nrealnum;
|
||||
enum lval_type lval, nlval;
|
||||
|
@ -1370,6 +1454,17 @@ get_prev_frame_1 (struct frame_info *this_frame)
|
|||
}
|
||||
}
|
||||
|
||||
return get_prev_frame_raw (this_frame);
|
||||
}
|
||||
|
||||
/* Construct a new "struct frame_info" and link it previous to
|
||||
this_frame. */
|
||||
|
||||
static struct frame_info *
|
||||
get_prev_frame_raw (struct frame_info *this_frame)
|
||||
{
|
||||
struct frame_info *prev_frame;
|
||||
|
||||
/* Allocate the new frame but do not wire it in to the frame chain.
|
||||
Some (bad) code in INIT_FRAME_EXTRA_INFO tries to look along
|
||||
frame->next to pull some fancy tricks (of course such code is, by
|
||||
|
@ -1492,7 +1587,7 @@ get_prev_frame (struct frame_info *this_frame)
|
|||
the main function when we created the dummy frame, the dummy frame will
|
||||
point inside the main function. */
|
||||
if (this_frame->level >= 0
|
||||
&& get_frame_type (this_frame) != DUMMY_FRAME
|
||||
&& get_frame_type (this_frame) == NORMAL_FRAME
|
||||
&& !backtrace_past_main
|
||||
&& inside_main_func (this_frame))
|
||||
/* Don't unwind past main(). Note, this is done _before_ the
|
||||
|
@ -1537,8 +1632,9 @@ get_prev_frame (struct frame_info *this_frame)
|
|||
from main returns directly to the caller of main. Since we don't
|
||||
stop at main, we should at least stop at the entry point of the
|
||||
application. */
|
||||
if (!backtrace_past_entry
|
||||
&& get_frame_type (this_frame) != DUMMY_FRAME && this_frame->level >= 0
|
||||
if (this_frame->level >= 0
|
||||
&& get_frame_type (this_frame) == NORMAL_FRAME
|
||||
&& !backtrace_past_entry
|
||||
&& inside_entry_func (this_frame))
|
||||
{
|
||||
frame_debug_got_null_frame (this_frame, "inside entry func");
|
||||
|
@ -1549,7 +1645,8 @@ get_prev_frame (struct frame_info *this_frame)
|
|||
like a SIGSEGV or a dummy frame, and hence that NORMAL frames
|
||||
will never unwind a zero PC. */
|
||||
if (this_frame->level > 0
|
||||
&& get_frame_type (this_frame) == NORMAL_FRAME
|
||||
&& (get_frame_type (this_frame) == NORMAL_FRAME
|
||||
|| get_frame_type (this_frame) == INLINE_FRAME)
|
||||
&& get_frame_type (get_next_frame (this_frame)) == NORMAL_FRAME
|
||||
&& get_frame_pc (this_frame) == 0)
|
||||
{
|
||||
|
@ -1564,7 +1661,7 @@ CORE_ADDR
|
|||
get_frame_pc (struct frame_info *frame)
|
||||
{
|
||||
gdb_assert (frame->next != NULL);
|
||||
return frame_unwind_caller_pc (frame->next);
|
||||
return frame_unwind_pc (frame->next);
|
||||
}
|
||||
|
||||
/* Return an address that falls within THIS_FRAME's code block. */
|
||||
|
@ -1609,17 +1706,58 @@ get_frame_address_in_block (struct frame_info *this_frame)
|
|||
We check the type of NEXT_FRAME first, since it is already
|
||||
known; frame type is determined by the unwinder, and since
|
||||
we have THIS_FRAME we've already selected an unwinder for
|
||||
NEXT_FRAME. */
|
||||
NEXT_FRAME.
|
||||
|
||||
If the next frame is inlined, we need to keep going until we find
|
||||
the real function - for instance, if a signal handler is invoked
|
||||
while in an inlined function, then the code address of the
|
||||
"calling" normal function should not be adjusted either. */
|
||||
|
||||
while (get_frame_type (next_frame) == INLINE_FRAME)
|
||||
next_frame = next_frame->next;
|
||||
|
||||
if (get_frame_type (next_frame) == NORMAL_FRAME
|
||||
&& get_frame_type (this_frame) == NORMAL_FRAME)
|
||||
&& (get_frame_type (this_frame) == NORMAL_FRAME
|
||||
|| get_frame_type (this_frame) == INLINE_FRAME))
|
||||
return pc - 1;
|
||||
|
||||
return pc;
|
||||
}
|
||||
|
||||
static int
|
||||
pc_notcurrent (struct frame_info *frame)
|
||||
void
|
||||
find_frame_sal (struct frame_info *frame, struct symtab_and_line *sal)
|
||||
{
|
||||
struct frame_info *next_frame;
|
||||
int notcurrent;
|
||||
|
||||
/* If the next frame represents an inlined function call, this frame's
|
||||
sal is the "call site" of that inlined function, which can not
|
||||
be inferred from get_frame_pc. */
|
||||
next_frame = get_next_frame (frame);
|
||||
if (frame_inlined_callees (frame) > 0)
|
||||
{
|
||||
struct symbol *sym;
|
||||
|
||||
if (next_frame)
|
||||
sym = get_frame_function (next_frame);
|
||||
else
|
||||
sym = inline_skipped_symbol (inferior_ptid);
|
||||
|
||||
init_sal (sal);
|
||||
if (SYMBOL_LINE (sym) != 0)
|
||||
{
|
||||
sal->symtab = SYMBOL_SYMTAB (sym);
|
||||
sal->line = SYMBOL_LINE (sym);
|
||||
}
|
||||
else
|
||||
/* If the symbol does not have a location, we don't know where
|
||||
the call site is. Do not pretend to. This is jarring, but
|
||||
we can't do much better. */
|
||||
sal->pc = get_frame_pc (frame);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* If FRAME is not the innermost frame, that normally means that
|
||||
FRAME->pc points at the return instruction (which is *after* the
|
||||
call instruction), and we want to get the line containing the
|
||||
|
@ -1629,15 +1767,8 @@ pc_notcurrent (struct frame_info *frame)
|
|||
PC and such a PC indicates the current (rather than next)
|
||||
instruction/line, consequently, for such cases, want to get the
|
||||
line containing fi->pc. */
|
||||
struct frame_info *next = get_next_frame (frame);
|
||||
int notcurrent = (next != NULL && get_frame_type (next) == NORMAL_FRAME);
|
||||
return notcurrent;
|
||||
}
|
||||
|
||||
void
|
||||
find_frame_sal (struct frame_info *frame, struct symtab_and_line *sal)
|
||||
{
|
||||
(*sal) = find_pc_line (get_frame_pc (frame), pc_notcurrent (frame));
|
||||
notcurrent = (get_frame_pc (frame) != get_frame_address_in_block (frame));
|
||||
(*sal) = find_pc_line (get_frame_pc (frame), notcurrent);
|
||||
}
|
||||
|
||||
/* Per "frame.h", return the ``address'' of the frame. Code should
|
||||
|
|
19
gdb/frame.h
19
gdb/frame.h
|
@ -39,6 +39,9 @@
|
|||
skipped. Use these to ignore any potentially inlined functions,
|
||||
e.g. inlined into the first instruction of a library trampoline.
|
||||
|
||||
get_stack_frame_WHAT...(): Get WHAT for THIS frame, but if THIS is
|
||||
inlined, skip to the containing stack frame.
|
||||
|
||||
put_frame_WHAT...(): Put a value into this frame (unsafe, need to
|
||||
invalidate the frame / regcache afterwards) (better name more
|
||||
strongly hinting at its unsafeness)
|
||||
|
@ -106,6 +109,10 @@ struct frame_id
|
|||
Typically, it is set to the address of the entry point of the
|
||||
frame's function (as returned by get_frame_func).
|
||||
|
||||
For inlined functions (INLINE_DEPTH != 0), this is the address of
|
||||
the first executed instruction in the block corresponding to the
|
||||
inlined function.
|
||||
|
||||
This field is valid only if code_addr_p is true. Otherwise, this
|
||||
frame is considered to have a wildcard code address, i.e. one that
|
||||
matches every address value in frame comparisons. */
|
||||
|
@ -127,6 +134,10 @@ struct frame_id
|
|||
unsigned int stack_addr_p : 1;
|
||||
unsigned int code_addr_p : 1;
|
||||
unsigned int special_addr_p : 1;
|
||||
|
||||
/* The inline depth of this frame. A frame representing a "called"
|
||||
inlined function will have this set to a nonzero value. */
|
||||
int inline_depth;
|
||||
};
|
||||
|
||||
/* Methods for constructing and comparing Frame IDs. */
|
||||
|
@ -162,6 +173,10 @@ extern struct frame_id frame_id_build_wild (CORE_ADDR stack_addr);
|
|||
non-zero .base). */
|
||||
extern int frame_id_p (struct frame_id l);
|
||||
|
||||
/* Returns non-zero when L is a valid frame representing an inlined
|
||||
function. */
|
||||
extern int frame_id_inlined_p (struct frame_id l);
|
||||
|
||||
/* Returns non-zero when L and R identify the same frame, or, if
|
||||
either L or R have a zero .func, then the same frame base. */
|
||||
extern int frame_id_eq (struct frame_id l, struct frame_id r);
|
||||
|
@ -182,6 +197,9 @@ enum frame_type
|
|||
/* A fake frame, created by GDB when performing an inferior function
|
||||
call. */
|
||||
DUMMY_FRAME,
|
||||
/* A frame representing an inlined function, associated with an
|
||||
upcoming (next, inner, younger) NORMAL_FRAME. */
|
||||
INLINE_FRAME,
|
||||
/* In a signal handler, various OSs handle this in various ways.
|
||||
The main thing is that the frame may be far from normal. */
|
||||
SIGTRAMP_FRAME,
|
||||
|
@ -350,6 +368,7 @@ extern CORE_ADDR get_frame_base (struct frame_info *);
|
|||
|
||||
instead, since that avoids the bug. */
|
||||
extern struct frame_id get_frame_id (struct frame_info *fi);
|
||||
extern struct frame_id get_stack_frame_id (struct frame_info *fi);
|
||||
extern struct frame_id frame_unwind_caller_id (struct frame_info *next_frame);
|
||||
|
||||
/* Assuming that a frame is `normal', return its base-address, or 0 if
|
||||
|
|
|
@ -83,6 +83,11 @@ struct thread_info
|
|||
This is how we know when we step into a subroutine call, and how
|
||||
to set the frame for the breakpoint used to step out. */
|
||||
struct frame_id step_frame_id;
|
||||
|
||||
/* Similarly, the frame ID of the underlying stack frame (skipping
|
||||
any inlined frames). */
|
||||
struct frame_id step_stack_frame_id;
|
||||
|
||||
int current_line;
|
||||
struct symtab *current_symtab;
|
||||
|
||||
|
|
71
gdb/infcmd.c
71
gdb/infcmd.c
|
@ -52,6 +52,7 @@
|
|||
#include "cli/cli-decode.h"
|
||||
#include "gdbthread.h"
|
||||
#include "valprint.h"
|
||||
#include "inline-frame.h"
|
||||
|
||||
/* Functions exported for general use, in inferior.h: */
|
||||
|
||||
|
@ -758,6 +759,17 @@ Can't resume all threads and specify proceed count simultaneously."));
|
|||
continue_1 (all_threads);
|
||||
}
|
||||
|
||||
/* Record the starting point of a "step" or "next" command. */
|
||||
|
||||
static void
|
||||
set_step_frame (void)
|
||||
{
|
||||
struct symtab_and_line sal;
|
||||
|
||||
find_frame_sal (get_current_frame (), &sal);
|
||||
set_step_info (get_current_frame (), sal);
|
||||
}
|
||||
|
||||
/* Step until outside of current statement. */
|
||||
|
||||
static void
|
||||
|
@ -926,7 +938,7 @@ step_1_continuation (void *args)
|
|||
static void
|
||||
step_once (int skip_subroutines, int single_inst, int count, int thread)
|
||||
{
|
||||
struct frame_info *frame;
|
||||
struct frame_info *frame = get_current_frame ();
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
|
@ -936,23 +948,32 @@ step_once (int skip_subroutines, int single_inst, int count, int thread)
|
|||
THREAD is set. */
|
||||
struct thread_info *tp = inferior_thread ();
|
||||
clear_proceed_status ();
|
||||
|
||||
frame = get_current_frame ();
|
||||
tp->step_frame_id = get_frame_id (frame);
|
||||
set_step_frame ();
|
||||
|
||||
if (!single_inst)
|
||||
{
|
||||
CORE_ADDR pc;
|
||||
|
||||
/* Step at an inlined function behaves like "down". */
|
||||
if (!skip_subroutines && !single_inst
|
||||
&& inline_skipped_frames (inferior_ptid))
|
||||
{
|
||||
step_into_inline_frame (inferior_ptid);
|
||||
if (count > 1)
|
||||
step_once (skip_subroutines, single_inst, count - 1, thread);
|
||||
else
|
||||
/* Pretend that we've stopped. */
|
||||
normal_stop ();
|
||||
return;
|
||||
}
|
||||
|
||||
pc = get_frame_pc (frame);
|
||||
find_pc_line_pc_range (pc,
|
||||
&tp->step_range_start, &tp->step_range_end);
|
||||
|
||||
/* If we have no line info, switch to stepi mode. */
|
||||
if (tp->step_range_end == 0 && step_stop_if_no_debug)
|
||||
{
|
||||
tp->step_range_start = tp->step_range_end = 1;
|
||||
}
|
||||
tp->step_range_start = tp->step_range_end = 1;
|
||||
else if (tp->step_range_end == 0)
|
||||
{
|
||||
char *name;
|
||||
|
@ -1188,6 +1209,7 @@ until_next_command (int from_tty)
|
|||
struct thread_info *tp = inferior_thread ();
|
||||
|
||||
clear_proceed_status ();
|
||||
set_step_frame ();
|
||||
|
||||
frame = get_current_frame ();
|
||||
|
||||
|
@ -1217,7 +1239,6 @@ until_next_command (int from_tty)
|
|||
}
|
||||
|
||||
tp->step_over_calls = STEP_OVER_ALL;
|
||||
tp->step_frame_id = get_frame_id (frame);
|
||||
|
||||
tp->step_multi = 0; /* Only one call to proceed */
|
||||
|
||||
|
@ -1443,7 +1464,7 @@ finish_backward (struct symbol *function)
|
|||
/* Set breakpoint and continue. */
|
||||
breakpoint =
|
||||
set_momentary_breakpoint (sal,
|
||||
get_frame_id (get_selected_frame (NULL)),
|
||||
get_stack_frame_id (get_selected_frame (NULL)),
|
||||
bp_breakpoint);
|
||||
/* Tell the breakpoint to keep quiet. We won't be done
|
||||
until we've done another reverse single-step. */
|
||||
|
@ -1481,7 +1502,7 @@ finish_forward (struct symbol *function, struct frame_info *frame)
|
|||
sal = find_pc_line (get_frame_pc (frame), 0);
|
||||
sal.pc = get_frame_pc (frame);
|
||||
|
||||
breakpoint = set_momentary_breakpoint (sal, get_frame_id (frame),
|
||||
breakpoint = set_momentary_breakpoint (sal, get_stack_frame_id (frame),
|
||||
bp_finish);
|
||||
|
||||
old_chain = make_cleanup_delete_breakpoint (breakpoint);
|
||||
|
@ -1544,6 +1565,36 @@ finish_command (char *arg, int from_tty)
|
|||
|
||||
clear_proceed_status ();
|
||||
|
||||
/* 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)
|
||||
{
|
||||
/* 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->step_range_start = tp->step_range_end = get_frame_pc (frame);
|
||||
tp->step_over_calls = STEP_OVER_ALL;
|
||||
|
||||
/* Print info on the selected frame, including level number but not
|
||||
source. */
|
||||
if (from_tty)
|
||||
{
|
||||
printf_filtered (_("Run till exit from "));
|
||||
print_stack_frame (get_selected_frame (NULL), 1, LOCATION);
|
||||
}
|
||||
|
||||
proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Find the function we will return from. */
|
||||
|
||||
function = find_pc_function (get_frame_pc (get_selected_frame (NULL)));
|
||||
|
|
|
@ -242,6 +242,8 @@ extern void error_is_running (void);
|
|||
/* Calls error_is_running if the current thread is running. */
|
||||
extern void ensure_not_running (void);
|
||||
|
||||
void set_step_info (struct frame_info *frame, struct symtab_and_line sal);
|
||||
|
||||
/* From infcmd.c */
|
||||
|
||||
extern void tty_command (char *, int);
|
||||
|
|
147
gdb/infrun.c
147
gdb/infrun.c
|
@ -49,6 +49,7 @@
|
|||
#include "mi/mi-common.h"
|
||||
#include "event-top.h"
|
||||
#include "record.h"
|
||||
#include "inline-frame.h"
|
||||
|
||||
/* Prototypes for local functions */
|
||||
|
||||
|
@ -209,7 +210,7 @@ static unsigned char *signal_program;
|
|||
|
||||
/* Value to pass to target_resume() to cause all threads to resume */
|
||||
|
||||
#define RESUME_ALL (pid_to_ptid (-1))
|
||||
#define RESUME_ALL minus_one_ptid
|
||||
|
||||
/* Command list pointer for the "stop" placeholder. */
|
||||
|
||||
|
@ -1316,6 +1317,7 @@ clear_proceed_status_thread (struct thread_info *tp)
|
|||
tp->step_range_start = 0;
|
||||
tp->step_range_end = 0;
|
||||
tp->step_frame_id = null_frame_id;
|
||||
tp->step_stack_frame_id = null_frame_id;
|
||||
tp->step_over_calls = STEP_OVER_UNDEBUGGABLE;
|
||||
tp->stop_requested = 0;
|
||||
|
||||
|
@ -1689,6 +1691,9 @@ init_wait_for_inferior (void)
|
|||
init_infwait_state ();
|
||||
|
||||
displaced_step_clear ();
|
||||
|
||||
/* Discard any skipped inlined frames. */
|
||||
clear_inline_frame_state (minus_one_ptid);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1744,7 +1749,7 @@ struct execution_control_state
|
|||
int wait_some_more;
|
||||
};
|
||||
|
||||
void init_execution_control_state (struct execution_control_state *ecs);
|
||||
static void init_execution_control_state (struct execution_control_state *ecs);
|
||||
|
||||
void handle_inferior_event (struct execution_control_state *ecs);
|
||||
|
||||
|
@ -2137,10 +2142,23 @@ fetch_inferior_event (void *client_data)
|
|||
display_gdb_prompt (0);
|
||||
}
|
||||
|
||||
/* Record the frame and location we're currently stepping through. */
|
||||
void
|
||||
set_step_info (struct frame_info *frame, struct symtab_and_line sal)
|
||||
{
|
||||
struct thread_info *tp = inferior_thread ();
|
||||
|
||||
tp->step_frame_id = get_frame_id (frame);
|
||||
tp->step_stack_frame_id = get_stack_frame_id (frame);
|
||||
|
||||
tp->current_symtab = sal.symtab;
|
||||
tp->current_line = sal.line;
|
||||
}
|
||||
|
||||
/* Prepare an execution control state for looping through a
|
||||
wait_for_inferior-type loop. */
|
||||
|
||||
void
|
||||
static void
|
||||
init_execution_control_state (struct execution_control_state *ecs)
|
||||
{
|
||||
ecs->random_signal = 0;
|
||||
|
@ -2151,16 +2169,10 @@ init_execution_control_state (struct execution_control_state *ecs)
|
|||
void
|
||||
init_thread_stepping_state (struct thread_info *tss)
|
||||
{
|
||||
struct symtab_and_line sal;
|
||||
|
||||
tss->stepping_over_breakpoint = 0;
|
||||
tss->step_after_step_resume_breakpoint = 0;
|
||||
tss->stepping_through_solib_after_catch = 0;
|
||||
tss->stepping_through_solib_catchpoints = NULL;
|
||||
|
||||
sal = find_pc_line (tss->prev_pc, 0);
|
||||
tss->current_line = sal.line;
|
||||
tss->current_symtab = sal.symtab;
|
||||
}
|
||||
|
||||
/* Return the cached copy of the last pid/waitstatus returned by
|
||||
|
@ -2337,6 +2349,22 @@ ensure_not_running (void)
|
|||
error_is_running ();
|
||||
}
|
||||
|
||||
static int
|
||||
stepped_in_from (struct frame_info *frame, struct frame_id step_frame_id)
|
||||
{
|
||||
for (frame = get_prev_frame (frame);
|
||||
frame != NULL;
|
||||
frame = get_prev_frame (frame))
|
||||
{
|
||||
if (frame_id_eq (get_frame_id (frame), step_frame_id))
|
||||
return 1;
|
||||
if (get_frame_type (frame) != INLINE_FRAME)
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Given an execution control state that has been freshly filled in
|
||||
by an event from the inferior, figure out what it means and take
|
||||
appropriate action. */
|
||||
|
@ -3065,6 +3093,12 @@ targets should add new threads to the thread list themselves in non-stop mode.")
|
|||
ecs->random_signal = 0;
|
||||
stopped_by_random_signal = 0;
|
||||
|
||||
/* 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). */
|
||||
if (ecs->event_thread->step_range_end != 1)
|
||||
skip_inline_frames (ecs->ptid);
|
||||
|
||||
if (ecs->event_thread->stop_signal == TARGET_SIGNAL_TRAP
|
||||
&& ecs->event_thread->trap_expected
|
||||
&& gdbarch_single_step_through_delay_p (gdbarch)
|
||||
|
@ -3302,8 +3336,8 @@ process_event_stop_test:
|
|||
&& ecs->event_thread->stop_signal != TARGET_SIGNAL_0
|
||||
&& (ecs->event_thread->step_range_start <= stop_pc
|
||||
&& stop_pc < ecs->event_thread->step_range_end)
|
||||
&& frame_id_eq (get_frame_id (frame),
|
||||
ecs->event_thread->step_frame_id)
|
||||
&& frame_id_eq (get_stack_frame_id (frame),
|
||||
ecs->event_thread->step_stack_frame_id)
|
||||
&& ecs->event_thread->step_resume_breakpoint == NULL)
|
||||
{
|
||||
/* The inferior is about to take a signal that will take it
|
||||
|
@ -3727,10 +3761,10 @@ infrun: not switching back to stepped thread, it has vanished\n");
|
|||
NOTE: frame_id_eq will never report two invalid frame IDs as
|
||||
being equal, so to get into this block, both the current and
|
||||
previous frame must have valid frame IDs. */
|
||||
if (!frame_id_eq (get_frame_id (frame),
|
||||
ecs->event_thread->step_frame_id)
|
||||
if (!frame_id_eq (get_stack_frame_id (frame),
|
||||
ecs->event_thread->step_stack_frame_id)
|
||||
&& (frame_id_eq (frame_unwind_caller_id (frame),
|
||||
ecs->event_thread->step_frame_id)
|
||||
ecs->event_thread->step_stack_frame_id)
|
||||
|| execution_direction == EXEC_REVERSE))
|
||||
{
|
||||
CORE_ADDR real_stop_pc;
|
||||
|
@ -3974,6 +4008,82 @@ infrun: not switching back to stepped thread, it has vanished\n");
|
|||
return;
|
||||
}
|
||||
|
||||
/* Look for "calls" to inlined functions, part one. If the inline
|
||||
frame machinery detected some skipped call sites, we have entered
|
||||
a new inline function. */
|
||||
|
||||
if (frame_id_eq (get_frame_id (get_current_frame ()),
|
||||
ecs->event_thread->step_frame_id)
|
||||
&& inline_skipped_frames (ecs->ptid))
|
||||
{
|
||||
struct symtab_and_line call_sal;
|
||||
|
||||
if (debug_infrun)
|
||||
fprintf_unfiltered (gdb_stdlog,
|
||||
"infrun: stepped into inlined function\n");
|
||||
|
||||
find_frame_sal (get_current_frame (), &call_sal);
|
||||
|
||||
if (ecs->event_thread->step_over_calls != STEP_OVER_ALL)
|
||||
{
|
||||
/* For "step", we're going to stop. But if the call site
|
||||
for this inlined function is on the same source line as
|
||||
we were previously stepping, go down into the function
|
||||
first. Otherwise stop at the call site. */
|
||||
|
||||
if (call_sal.line == ecs->event_thread->current_line
|
||||
&& call_sal.symtab == ecs->event_thread->current_symtab)
|
||||
step_into_inline_frame (ecs->ptid);
|
||||
|
||||
ecs->event_thread->stop_step = 1;
|
||||
print_stop_reason (END_STEPPING_RANGE, 0);
|
||||
stop_stepping (ecs);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* For "next", we should stop at the call site if it is on a
|
||||
different source line. Otherwise continue through the
|
||||
inlined function. */
|
||||
if (call_sal.line == ecs->event_thread->current_line
|
||||
&& call_sal.symtab == ecs->event_thread->current_symtab)
|
||||
keep_going (ecs);
|
||||
else
|
||||
{
|
||||
ecs->event_thread->stop_step = 1;
|
||||
print_stop_reason (END_STEPPING_RANGE, 0);
|
||||
stop_stepping (ecs);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Look for "calls" to inlined functions, part two. If we are still
|
||||
in the same real function we were stepping through, but we have
|
||||
to go further up to find the exact frame ID, we are stepping
|
||||
through a more inlined call beyond its call site. */
|
||||
|
||||
if (get_frame_type (get_current_frame ()) == INLINE_FRAME
|
||||
&& !frame_id_eq (get_frame_id (get_current_frame ()),
|
||||
ecs->event_thread->step_frame_id)
|
||||
&& stepped_in_from (get_current_frame (),
|
||||
ecs->event_thread->step_frame_id))
|
||||
{
|
||||
if (debug_infrun)
|
||||
fprintf_unfiltered (gdb_stdlog,
|
||||
"infrun: stepping through inlined function\n");
|
||||
|
||||
if (ecs->event_thread->step_over_calls == STEP_OVER_ALL)
|
||||
keep_going (ecs);
|
||||
else
|
||||
{
|
||||
ecs->event_thread->stop_step = 1;
|
||||
print_stop_reason (END_STEPPING_RANGE, 0);
|
||||
stop_stepping (ecs);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ((stop_pc == stop_pc_sal.pc)
|
||||
&& (ecs->event_thread->current_line != stop_pc_sal.line
|
||||
|| ecs->event_thread->current_symtab != stop_pc_sal.symtab))
|
||||
|
@ -3999,9 +4109,7 @@ infrun: not switching back to stepped thread, it has vanished\n");
|
|||
|
||||
ecs->event_thread->step_range_start = stop_pc_sal.pc;
|
||||
ecs->event_thread->step_range_end = stop_pc_sal.end;
|
||||
ecs->event_thread->step_frame_id = get_frame_id (frame);
|
||||
ecs->event_thread->current_line = stop_pc_sal.line;
|
||||
ecs->event_thread->current_symtab = stop_pc_sal.symtab;
|
||||
set_step_info (frame, stop_pc_sal);
|
||||
|
||||
if (debug_infrun)
|
||||
fprintf_unfiltered (gdb_stdlog, "infrun: keep going\n");
|
||||
|
@ -4188,7 +4296,7 @@ insert_step_resume_breakpoint_at_frame (struct frame_info *return_frame)
|
|||
sr_sal.pc = gdbarch_addr_bits_remove (gdbarch, get_frame_pc (return_frame));
|
||||
sr_sal.section = find_pc_overlay (sr_sal.pc);
|
||||
|
||||
insert_step_resume_breakpoint_at_sal (sr_sal, get_frame_id (return_frame));
|
||||
insert_step_resume_breakpoint_at_sal (sr_sal, get_stack_frame_id (return_frame));
|
||||
}
|
||||
|
||||
/* Similar to insert_step_resume_breakpoint_at_frame, except
|
||||
|
@ -5266,6 +5374,7 @@ struct inferior_status
|
|||
CORE_ADDR step_range_start;
|
||||
CORE_ADDR step_range_end;
|
||||
struct frame_id step_frame_id;
|
||||
struct frame_id step_stack_frame_id;
|
||||
enum step_over_calls_kind step_over_calls;
|
||||
CORE_ADDR step_resume_break_address;
|
||||
int stop_after_trap;
|
||||
|
@ -5295,6 +5404,7 @@ save_inferior_status (void)
|
|||
inf_status->step_range_start = tp->step_range_start;
|
||||
inf_status->step_range_end = tp->step_range_end;
|
||||
inf_status->step_frame_id = tp->step_frame_id;
|
||||
inf_status->step_stack_frame_id = tp->step_stack_frame_id;
|
||||
inf_status->step_over_calls = tp->step_over_calls;
|
||||
inf_status->stop_after_trap = stop_after_trap;
|
||||
inf_status->stop_soon = inf->stop_soon;
|
||||
|
@ -5348,6 +5458,7 @@ restore_inferior_status (struct inferior_status *inf_status)
|
|||
tp->step_range_start = inf_status->step_range_start;
|
||||
tp->step_range_end = inf_status->step_range_end;
|
||||
tp->step_frame_id = inf_status->step_frame_id;
|
||||
tp->step_stack_frame_id = inf_status->step_stack_frame_id;
|
||||
tp->step_over_calls = inf_status->step_over_calls;
|
||||
stop_after_trap = inf_status->stop_after_trap;
|
||||
inf->stop_soon = inf_status->stop_soon;
|
||||
|
|
|
@ -0,0 +1,394 @@
|
|||
/* Inline frame unwinder for GDB.
|
||||
|
||||
Copyright (C) 2008 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/>. */
|
||||
|
||||
#include "defs.h"
|
||||
#include "addrmap.h"
|
||||
#include "block.h"
|
||||
#include "frame-unwind.h"
|
||||
#include "inferior.h"
|
||||
#include "symtab.h"
|
||||
#include "vec.h"
|
||||
|
||||
#include "gdb_assert.h"
|
||||
|
||||
/* We need to save a few variables for every thread stopped at the
|
||||
virtual call site of an inlined function. If there was always a
|
||||
"struct thread_info", we could hang it off that; in the mean time,
|
||||
keep our own list. */
|
||||
struct inline_state
|
||||
{
|
||||
/* The thread this data relates to. It should be a currently
|
||||
stopped thread; we assume thread IDs never change while the
|
||||
thread is stopped. */
|
||||
ptid_t ptid;
|
||||
|
||||
/* The number of inlined functions we are skipping. Each of these
|
||||
functions can be stepped in to. */
|
||||
int skipped_frames;
|
||||
|
||||
/* Only valid if SKIPPED_FRAMES is non-zero. This is the PC used
|
||||
when calculating SKIPPED_FRAMES; used to check whether we have
|
||||
moved to a new location by user request. If so, we invalidate
|
||||
any skipped frames. */
|
||||
CORE_ADDR saved_pc;
|
||||
|
||||
/* Only valid if SKIPPED_FRAMES is non-zero. This is the symbol
|
||||
of the outermost skipped inline function. It's used to find the
|
||||
call site of the current frame. */
|
||||
struct symbol *skipped_symbol;
|
||||
};
|
||||
|
||||
typedef struct inline_state inline_state_s;
|
||||
DEF_VEC_O(inline_state_s);
|
||||
|
||||
static VEC(inline_state_s) *inline_states;
|
||||
|
||||
/* Locate saved inlined frame state for PTID, if it exists. */
|
||||
|
||||
static struct inline_state *
|
||||
find_inline_frame_state (ptid_t ptid)
|
||||
{
|
||||
struct inline_state *state;
|
||||
int ix;
|
||||
|
||||
for (ix = 0; VEC_iterate (inline_state_s, inline_states, ix, state); ix++)
|
||||
{
|
||||
if (ptid_equal (state->ptid, ptid))
|
||||
return state;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Allocate saved inlined frame state for PTID. */
|
||||
|
||||
static struct inline_state *
|
||||
allocate_inline_frame_state (ptid_t ptid)
|
||||
{
|
||||
struct inline_state *state;
|
||||
|
||||
state = VEC_safe_push (inline_state_s, inline_states, NULL);
|
||||
memset (state, 0, sizeof (*state));
|
||||
state->ptid = ptid;
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
/* Forget about any hidden inlined functions in PTID, which is new or
|
||||
about to be resumed. PTID may be minus_one_ptid (all processes)
|
||||
or a PID (all threads in this process). */
|
||||
|
||||
void
|
||||
clear_inline_frame_state (ptid_t ptid)
|
||||
{
|
||||
struct inline_state *state;
|
||||
int ix;
|
||||
|
||||
if (ptid_equal (ptid, minus_one_ptid))
|
||||
{
|
||||
VEC_free (inline_state_s, inline_states);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ptid_is_pid (ptid))
|
||||
{
|
||||
VEC (inline_state_s) *new_states = NULL;
|
||||
int pid = ptid_get_pid (ptid);
|
||||
for (ix = 0; VEC_iterate (inline_state_s, inline_states, ix, state); ix++)
|
||||
if (pid != ptid_get_pid (state->ptid))
|
||||
VEC_safe_push (inline_state_s, new_states, state);
|
||||
VEC_free (inline_state_s, inline_states);
|
||||
inline_states = new_states;
|
||||
return;
|
||||
}
|
||||
|
||||
for (ix = 0; VEC_iterate (inline_state_s, inline_states, ix, state); ix++)
|
||||
if (ptid_equal (state->ptid, ptid))
|
||||
{
|
||||
VEC_unordered_remove (inline_state_s, inline_states, ix);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
inline_frame_this_id (struct frame_info *this_frame,
|
||||
void **this_cache,
|
||||
struct frame_id *this_id)
|
||||
{
|
||||
struct symbol *func;
|
||||
|
||||
/* In order to have a stable frame ID for a given inline function,
|
||||
we must get the stack / special addresses from the underlying
|
||||
real frame's this_id method. So we must call get_prev_frame.
|
||||
Because we are inlined into some function, there must be previous
|
||||
frames, so this is safe - as long as we're careful not to
|
||||
create any cycles. */
|
||||
*this_id = get_frame_id (get_prev_frame (this_frame));
|
||||
|
||||
/* We need a valid frame ID, so we need to be based on a valid
|
||||
frame. FSF submission NOTE: this would be a good assertion to
|
||||
apply to all frames, all the time. That would fix the ambiguity
|
||||
of null_frame_id (between "no/any frame" and "the outermost
|
||||
frame"). This will take work. */
|
||||
gdb_assert (frame_id_p (*this_id));
|
||||
|
||||
/* Future work NOTE: Alexandre Oliva applied a patch to GCC 4.3
|
||||
which generates DW_AT_entry_pc for inlined functions when
|
||||
possible. If this attribute is available, we should use it
|
||||
in the frame ID (and eventually, to set breakpoints). */
|
||||
func = get_frame_function (this_frame);
|
||||
gdb_assert (func != NULL);
|
||||
(*this_id).code_addr = BLOCK_START (SYMBOL_BLOCK_VALUE (func));
|
||||
(*this_id).inline_depth++;
|
||||
}
|
||||
|
||||
static struct value *
|
||||
inline_frame_prev_register (struct frame_info *this_frame, void **this_cache,
|
||||
int regnum)
|
||||
{
|
||||
/* Use get_frame_register_value instead of
|
||||
frame_unwind_got_register, to avoid requiring this frame's ID.
|
||||
This frame's ID depends on the previous frame's ID (unusual), and
|
||||
the previous frame's ID depends on this frame's unwound
|
||||
registers. If unwinding registers from this frame called
|
||||
get_frame_id, there would be a loop.
|
||||
|
||||
Do not copy this code into any other unwinder! Inlined functions
|
||||
are special; other unwinders must not have a dependency on the
|
||||
previous frame's ID, and therefore can and should use
|
||||
frame_unwind_got_register instead. */
|
||||
return get_frame_register_value (this_frame, regnum);
|
||||
}
|
||||
|
||||
/* Check whether we are at an inlining site that does not already
|
||||
have an associated frame. */
|
||||
|
||||
static int
|
||||
inline_frame_sniffer (const struct frame_unwind *self,
|
||||
struct frame_info *this_frame,
|
||||
void **this_cache)
|
||||
{
|
||||
CORE_ADDR this_pc;
|
||||
struct block *frame_block, *cur_block;
|
||||
int depth;
|
||||
struct frame_info *next_frame;
|
||||
struct inline_state *state = find_inline_frame_state (inferior_ptid);
|
||||
|
||||
this_pc = get_frame_address_in_block (this_frame);
|
||||
frame_block = block_for_pc (this_pc);
|
||||
if (frame_block == NULL)
|
||||
return 0;
|
||||
|
||||
/* Calculate DEPTH, the number of inlined functions at this
|
||||
location. */
|
||||
depth = 0;
|
||||
cur_block = frame_block;
|
||||
while (BLOCK_SUPERBLOCK (cur_block))
|
||||
{
|
||||
if (block_inlined_p (cur_block))
|
||||
depth++;
|
||||
|
||||
cur_block = BLOCK_SUPERBLOCK (cur_block);
|
||||
}
|
||||
|
||||
/* Check how many inlined functions already have frames. */
|
||||
for (next_frame = get_next_frame (this_frame);
|
||||
next_frame && get_frame_type (next_frame) == INLINE_FRAME;
|
||||
next_frame = get_next_frame (next_frame))
|
||||
{
|
||||
gdb_assert (depth > 0);
|
||||
depth--;
|
||||
}
|
||||
|
||||
/* If this is the topmost frame, or all frames above us are inlined,
|
||||
then check whether we were requested to skip some frames (so they
|
||||
can be stepped into later). */
|
||||
if (state != NULL && state->skipped_frames > 0 && next_frame == NULL)
|
||||
{
|
||||
if (this_pc != state->saved_pc)
|
||||
state->skipped_frames = 0;
|
||||
else
|
||||
{
|
||||
gdb_assert (depth >= state->skipped_frames);
|
||||
depth -= state->skipped_frames;
|
||||
}
|
||||
}
|
||||
|
||||
/* If all the inlined functions here already have frames, then pass
|
||||
to the normal unwinder for this PC. */
|
||||
if (depth == 0)
|
||||
return 0;
|
||||
|
||||
/* If the next frame is an inlined function, but not the outermost, then
|
||||
we are the next outer. If it is not an inlined function, then we
|
||||
are the innermost inlined function of a different real frame. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
const struct frame_unwind inline_frame_unwinder = {
|
||||
INLINE_FRAME,
|
||||
inline_frame_this_id,
|
||||
inline_frame_prev_register,
|
||||
NULL,
|
||||
inline_frame_sniffer
|
||||
};
|
||||
|
||||
const struct frame_unwind *const inline_frame_unwind = &inline_frame_unwinder;
|
||||
|
||||
/* Return non-zero if BLOCK, an inlined function block containing PC,
|
||||
has a group of contiguous instructions starting at PC (but not
|
||||
before it). */
|
||||
|
||||
static int
|
||||
block_starting_point_at (CORE_ADDR pc, struct block *block)
|
||||
{
|
||||
struct blockvector *bv;
|
||||
struct block *new_block;
|
||||
|
||||
bv = blockvector_for_pc (pc, NULL);
|
||||
if (BLOCKVECTOR_MAP (bv) == NULL)
|
||||
return 0;
|
||||
|
||||
new_block = addrmap_find (BLOCKVECTOR_MAP (bv), pc - 1);
|
||||
if (new_block == NULL)
|
||||
return 1;
|
||||
|
||||
if (new_block == block || contained_in (new_block, block))
|
||||
return 0;
|
||||
|
||||
/* The immediately preceeding address belongs to a different block,
|
||||
which is not a child of this one. Treat this as an entrance into
|
||||
BLOCK. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Skip all inlined functions whose call sites are at the current PC.
|
||||
Frames for the hidden functions will not appear in the backtrace until the
|
||||
user steps into them. */
|
||||
|
||||
void
|
||||
skip_inline_frames (ptid_t ptid)
|
||||
{
|
||||
CORE_ADDR this_pc;
|
||||
struct block *frame_block, *cur_block;
|
||||
struct symbol *last_sym = NULL;
|
||||
int skip_count = 0;
|
||||
struct inline_state *state;
|
||||
|
||||
/* This function is called right after reinitializing the frame
|
||||
cache. We try not to do more unwinding than absolutely
|
||||
necessary, for performance. */
|
||||
this_pc = get_frame_pc (get_current_frame ());
|
||||
frame_block = block_for_pc (this_pc);
|
||||
|
||||
if (frame_block != NULL)
|
||||
{
|
||||
cur_block = frame_block;
|
||||
while (BLOCK_SUPERBLOCK (cur_block))
|
||||
{
|
||||
if (block_inlined_p (cur_block))
|
||||
{
|
||||
/* See comments in inline_frame_this_id about this use
|
||||
of BLOCK_START. */
|
||||
if (BLOCK_START (cur_block) == this_pc
|
||||
|| block_starting_point_at (this_pc, cur_block))
|
||||
{
|
||||
skip_count++;
|
||||
last_sym = BLOCK_FUNCTION (cur_block);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
cur_block = BLOCK_SUPERBLOCK (cur_block);
|
||||
}
|
||||
}
|
||||
|
||||
gdb_assert (find_inline_frame_state (ptid) == NULL);
|
||||
state = allocate_inline_frame_state (ptid);
|
||||
state->skipped_frames = skip_count;
|
||||
state->saved_pc = this_pc;
|
||||
state->skipped_symbol = last_sym;
|
||||
|
||||
if (skip_count != 0)
|
||||
reinit_frame_cache ();
|
||||
}
|
||||
|
||||
/* Step into an inlined function by unhiding it. */
|
||||
|
||||
void
|
||||
step_into_inline_frame (ptid_t ptid)
|
||||
{
|
||||
struct inline_state *state = find_inline_frame_state (ptid);
|
||||
|
||||
gdb_assert (state != NULL && state->skipped_frames > 0);
|
||||
state->skipped_frames--;
|
||||
reinit_frame_cache ();
|
||||
}
|
||||
|
||||
/* Return the number of hidden functions inlined into the current
|
||||
frame. */
|
||||
|
||||
int
|
||||
inline_skipped_frames (ptid_t ptid)
|
||||
{
|
||||
struct inline_state *state = find_inline_frame_state (ptid);
|
||||
|
||||
if (state == NULL)
|
||||
return 0;
|
||||
else
|
||||
return state->skipped_frames;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
struct inline_state *state = find_inline_frame_state (ptid);
|
||||
|
||||
gdb_assert (state != NULL);
|
||||
return state->skipped_symbol;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
struct frame_info *next_frame;
|
||||
int inline_count = 0;
|
||||
|
||||
/* First count how many inlined functions at this PC have frames
|
||||
above FRAME (are inlined into FRAME). */
|
||||
for (next_frame = get_next_frame (this_frame);
|
||||
next_frame && get_frame_type (next_frame) == INLINE_FRAME;
|
||||
next_frame = get_next_frame (next_frame))
|
||||
inline_count++;
|
||||
|
||||
/* Simulate some most-inner inlined frames which were suppressed, so
|
||||
they can be stepped into later. If we are unwinding already
|
||||
outer frames from some non-inlined frame this does not apply. */
|
||||
if (next_frame == NULL)
|
||||
inline_count += inline_skipped_frames (inferior_ptid);
|
||||
|
||||
return inline_count;
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/* Definitions for inline frame support.
|
||||
|
||||
Copyright (C) 2008 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;
|
||||
|
||||
/* The inline frame unwinder. */
|
||||
|
||||
extern const struct frame_unwind *const inline_frame_unwind;
|
||||
|
||||
/* Skip all inlined functions whose call sites are at the current PC.
|
||||
Frames for the hidden functions will not appear in the backtrace until the
|
||||
user steps into them. */
|
||||
|
||||
void skip_inline_frames (ptid_t ptid);
|
||||
|
||||
/* 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) */
|
|
@ -790,7 +790,7 @@ prim_record_minimal_symbol_and_info (const char *name, CORE_ADDR address,
|
|||
|
||||
if (msym_bunch_index == BUNCH_SIZE)
|
||||
{
|
||||
new = (struct msym_bunch *) xmalloc (sizeof (struct msym_bunch));
|
||||
new = XCALLOC (1, struct msym_bunch);
|
||||
msym_bunch_index = 0;
|
||||
new->next = msym_bunch;
|
||||
msym_bunch = new;
|
||||
|
|
|
@ -854,6 +854,10 @@ regcache_write_pc (struct regcache *regcache, CORE_ADDR pc)
|
|||
else
|
||||
internal_error (__FILE__, __LINE__,
|
||||
_("regcache_write_pc: Unable to update PC"));
|
||||
|
||||
/* Writing the PC (for instance, from "load") invalidates the
|
||||
current frame. */
|
||||
reinit_frame_cache ();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1182,6 +1182,7 @@ s390_prologue_frame_unwind_cache (struct frame_info *this_frame,
|
|||
CORE_ADDR prev_sp;
|
||||
int frame_pointer;
|
||||
int size;
|
||||
struct frame_info *next_frame;
|
||||
|
||||
/* Try to find the function start address. If we can't find it, we don't
|
||||
bother searching for it -- with modern compilers this would be mostly
|
||||
|
@ -1215,7 +1216,10 @@ s390_prologue_frame_unwind_cache (struct frame_info *this_frame,
|
|||
/* FIXME: cagney/2004-05-01: This sanity check shouldn't be
|
||||
needed, instead the code should simpliy rely on its
|
||||
analysis. */
|
||||
if (get_next_frame (this_frame)
|
||||
next_frame = get_next_frame (this_frame);
|
||||
while (next_frame && get_frame_type (next_frame) == INLINE_FRAME)
|
||||
next_frame = get_next_frame (next_frame);
|
||||
if (next_frame
|
||||
&& get_frame_type (get_next_frame (this_frame)) == NORMAL_FRAME)
|
||||
return 0;
|
||||
|
||||
|
@ -1261,8 +1265,11 @@ s390_prologue_frame_unwind_cache (struct frame_info *this_frame,
|
|||
This can only happen in an innermost frame. */
|
||||
/* FIXME: cagney/2004-05-01: This sanity check shouldn't be needed,
|
||||
instead the code should simpliy rely on its analysis. */
|
||||
next_frame = get_next_frame (this_frame);
|
||||
while (next_frame && get_frame_type (next_frame) == INLINE_FRAME)
|
||||
next_frame = get_next_frame (next_frame);
|
||||
if (size > 0
|
||||
&& (!get_next_frame (this_frame)
|
||||
&& (next_frame == NULL
|
||||
|| get_frame_type (get_next_frame (this_frame)) != NORMAL_FRAME))
|
||||
{
|
||||
/* See the comment in s390_in_function_epilogue_p on why this is
|
||||
|
|
57
gdb/stack.c
57
gdb/stack.c
|
@ -46,6 +46,7 @@
|
|||
#include "gdbthread.h"
|
||||
#include "cp-support.h"
|
||||
#include "disasm.h"
|
||||
#include "inline-frame.h"
|
||||
|
||||
#include "gdb_assert.h"
|
||||
#include <ctype.h>
|
||||
|
@ -99,6 +100,30 @@ print_stack_frame_stub (void *args)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Return 1 if we should display the address in addition to the location,
|
||||
because we are in the middle of a statement. */
|
||||
|
||||
static int
|
||||
frame_show_address (struct frame_info *frame,
|
||||
struct symtab_and_line sal)
|
||||
{
|
||||
/* If there is a line number, but no PC, then there is no location
|
||||
information associated with this sal. The only way that should
|
||||
happen is for the call sites of inlined functions (SAL comes from
|
||||
find_frame_sal). Otherwise, we would have some PC range if the
|
||||
SAL came from a line table. */
|
||||
if (sal.line != 0 && sal.pc == 0 && sal.end == 0)
|
||||
{
|
||||
if (get_next_frame (frame) == NULL)
|
||||
gdb_assert (inline_skipped_frames (inferior_ptid) > 0);
|
||||
else
|
||||
gdb_assert (get_frame_type (get_next_frame (frame)) == INLINE_FRAME);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return get_frame_pc (frame) != sal.pc;
|
||||
}
|
||||
|
||||
/* Show or print a stack frame FRAME briefly. The output is format
|
||||
according to PRINT_LEVEL and PRINT_WHAT printing the frame's
|
||||
relative level, function name, argument list, and file name and
|
||||
|
@ -565,7 +590,7 @@ print_frame_info (struct frame_info *frame, int print_level,
|
|||
{
|
||||
int done = 0;
|
||||
int mid_statement = ((print_what == SRC_LINE)
|
||||
&& (get_frame_pc (frame) != sal.pc));
|
||||
&& frame_show_address (frame, sal));
|
||||
|
||||
if (annotation_level)
|
||||
done = identify_source_line (sal.symtab, sal.line, mid_statement,
|
||||
|
@ -623,7 +648,7 @@ find_frame_funname (struct frame_info *frame, char **funname,
|
|||
*funname = NULL;
|
||||
*funlang = language_unknown;
|
||||
|
||||
func = find_pc_function (get_frame_address_in_block (frame));
|
||||
func = get_frame_function (frame);
|
||||
if (func)
|
||||
{
|
||||
/* In certain pathological cases, the symtabs give the wrong
|
||||
|
@ -644,8 +669,13 @@ find_frame_funname (struct frame_info *frame, char **funname,
|
|||
changed (and we'll create a find_pc_minimal_function or some
|
||||
such). */
|
||||
|
||||
struct minimal_symbol *msymbol =
|
||||
lookup_minimal_symbol_by_pc (get_frame_address_in_block (frame));
|
||||
struct minimal_symbol *msymbol = NULL;
|
||||
|
||||
/* Don't attempt to do this for inlined functions, which do not
|
||||
have a corresponding minimal symbol. */
|
||||
if (!block_inlined_p (SYMBOL_BLOCK_VALUE (func)))
|
||||
msymbol
|
||||
= lookup_minimal_symbol_by_pc (get_frame_address_in_block (frame));
|
||||
|
||||
if (msymbol != NULL
|
||||
&& (SYMBOL_VALUE_ADDRESS (msymbol)
|
||||
|
@ -719,7 +749,7 @@ print_frame (struct frame_info *frame, int print_level,
|
|||
}
|
||||
get_user_print_options (&opts);
|
||||
if (opts.addressprint)
|
||||
if (get_frame_pc (frame) != sal.pc || !sal.symtab
|
||||
if (frame_show_address (frame, sal) || !sal.symtab
|
||||
|| print_what == LOC_AND_ADDRESS)
|
||||
{
|
||||
annotate_frame_address ();
|
||||
|
@ -1042,8 +1072,10 @@ frame_info (char *addr_exp, int from_tty)
|
|||
printf_filtered (_(" Outermost frame: %s\n"),
|
||||
frame_stop_reason_string (reason));
|
||||
}
|
||||
|
||||
if (calling_frame_info)
|
||||
else if (get_frame_type (fi) == INLINE_FRAME)
|
||||
printf_filtered (" inlined into frame %d",
|
||||
frame_relative_level (get_prev_frame (fi)));
|
||||
else
|
||||
{
|
||||
printf_filtered (" called by frame at ");
|
||||
fputs_filtered (paddress (get_frame_base (calling_frame_info)),
|
||||
|
@ -1503,7 +1535,9 @@ print_frame_local_vars (struct frame_info *frame, int num_tabs,
|
|||
if (print_block_frame_locals (block, frame, num_tabs, stream))
|
||||
values_printed = 1;
|
||||
/* After handling the function's top-level block, stop. Don't
|
||||
continue to its superblock, the block of per-file symbols. */
|
||||
continue to its superblock, the block of per-file symbols.
|
||||
Also do not continue to the containing function of an inlined
|
||||
function. */
|
||||
if (BLOCK_FUNCTION (block))
|
||||
break;
|
||||
block = BLOCK_SUPERBLOCK (block);
|
||||
|
@ -1574,7 +1608,9 @@ print_frame_label_vars (struct frame_info *frame, int this_level_only,
|
|||
return;
|
||||
|
||||
/* After handling the function's top-level block, stop. Don't
|
||||
continue to its superblock, the block of per-file symbols. */
|
||||
continue to its superblock, the block of per-file symbols.
|
||||
Also do not continue to the containing function of an inlined
|
||||
function. */
|
||||
if (BLOCK_FUNCTION (block))
|
||||
break;
|
||||
block = BLOCK_SUPERBLOCK (block);
|
||||
|
@ -1840,6 +1876,9 @@ return_command (char *retval_exp, int from_tty)
|
|||
thisfun = get_frame_function (thisframe);
|
||||
gdbarch = get_frame_arch (thisframe);
|
||||
|
||||
if (get_frame_type (get_current_frame ()) == INLINE_FRAME)
|
||||
error (_("Can not force return from an inlined function."));
|
||||
|
||||
/* Compute the return value. If the computation triggers an error,
|
||||
let it bail. If the return type can't be handled, set
|
||||
RETURN_VALUE to NULL, and QUERY_PREFIX to an informational
|
||||
|
|
133
gdb/symtab.c
133
gdb/symtab.c
|
@ -1384,10 +1384,13 @@ lookup_symbol_aux_local (const char *name, const char *linkage_name,
|
|||
sym = lookup_symbol_aux_block (name, linkage_name, block, domain);
|
||||
if (sym != NULL)
|
||||
return sym;
|
||||
|
||||
if (BLOCK_FUNCTION (block) != NULL && block_inlined_p (block))
|
||||
break;
|
||||
block = BLOCK_SUPERBLOCK (block);
|
||||
}
|
||||
|
||||
/* We've reached the static block without finding a result. */
|
||||
/* We've reached the edge of the function without finding a result. */
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
@ -2654,6 +2657,7 @@ find_function_start_sal (struct symbol *sym, int funfirstline)
|
|||
|
||||
CORE_ADDR pc;
|
||||
struct symtab_and_line sal;
|
||||
struct block *b, *function_block;
|
||||
|
||||
pc = BLOCK_START (block);
|
||||
fixup_symbol_section (sym, objfile);
|
||||
|
@ -2707,6 +2711,25 @@ find_function_start_sal (struct symbol *sym, int funfirstline)
|
|||
|
||||
sal.pc = pc;
|
||||
|
||||
/* Check if we are now inside an inlined function. If we can,
|
||||
use the call site of the function instead. */
|
||||
b = block_for_pc_sect (sal.pc, SYMBOL_OBJ_SECTION (sym));
|
||||
function_block = NULL;
|
||||
while (b != NULL)
|
||||
{
|
||||
if (BLOCK_FUNCTION (b) != NULL && block_inlined_p (b))
|
||||
function_block = b;
|
||||
else if (BLOCK_FUNCTION (b) != NULL)
|
||||
break;
|
||||
b = BLOCK_SUPERBLOCK (b);
|
||||
}
|
||||
if (function_block != NULL
|
||||
&& SYMBOL_LINE (BLOCK_FUNCTION (function_block)) != 0)
|
||||
{
|
||||
sal.line = SYMBOL_LINE (BLOCK_FUNCTION (function_block));
|
||||
sal.symtab = SYMBOL_SYMTAB (BLOCK_FUNCTION (function_block));
|
||||
}
|
||||
|
||||
return sal;
|
||||
}
|
||||
|
||||
|
@ -3707,6 +3730,24 @@ language_search_unquoted_string (char *text, char *p)
|
|||
return p;
|
||||
}
|
||||
|
||||
static void
|
||||
completion_list_add_fields (struct symbol *sym, char *sym_text,
|
||||
int sym_text_len, char *text, char *word)
|
||||
{
|
||||
if (SYMBOL_CLASS (sym) == LOC_TYPEDEF)
|
||||
{
|
||||
struct type *t = SYMBOL_TYPE (sym);
|
||||
enum type_code c = TYPE_CODE (t);
|
||||
int j;
|
||||
|
||||
if (c == TYPE_CODE_UNION || c == TYPE_CODE_STRUCT)
|
||||
for (j = TYPE_N_BASECLASSES (t); j < TYPE_NFIELDS (t); j++)
|
||||
if (TYPE_FIELD_NAME (t, j))
|
||||
completion_list_add_name (TYPE_FIELD_NAME (t, j),
|
||||
sym_text, sym_text_len, text, word);
|
||||
}
|
||||
}
|
||||
|
||||
/* Type of the user_data argument passed to add_macro_name. The
|
||||
contents are simply whatever is needed by
|
||||
completion_list_add_name. */
|
||||
|
@ -3742,9 +3783,9 @@ default_make_symbol_completion_list (char *text, char *word)
|
|||
struct partial_symtab *ps;
|
||||
struct minimal_symbol *msymbol;
|
||||
struct objfile *objfile;
|
||||
struct block *b, *surrounding_static_block = 0;
|
||||
struct block *b;
|
||||
const struct block *surrounding_static_block, *surrounding_global_block;
|
||||
struct dict_iterator iter;
|
||||
int j;
|
||||
struct partial_symbol **psym;
|
||||
/* The symbol we are completing on. Points in same buffer as text. */
|
||||
char *sym_text;
|
||||
|
@ -3854,41 +3895,43 @@ default_make_symbol_completion_list (char *text, char *word)
|
|||
}
|
||||
|
||||
/* Search upwards from currently selected frame (so that we can
|
||||
complete on local vars. */
|
||||
complete on local vars). Also catch fields of types defined in
|
||||
this places which match our text string. Only complete on types
|
||||
visible from current context. */
|
||||
|
||||
for (b = get_selected_block (0); b != NULL; b = BLOCK_SUPERBLOCK (b))
|
||||
{
|
||||
if (!BLOCK_SUPERBLOCK (b))
|
||||
{
|
||||
surrounding_static_block = b; /* For elmin of dups */
|
||||
}
|
||||
b = get_selected_block (0);
|
||||
surrounding_static_block = block_static_block (b);
|
||||
surrounding_global_block = block_global_block (b);
|
||||
if (surrounding_static_block != NULL)
|
||||
while (b != surrounding_static_block)
|
||||
{
|
||||
QUIT;
|
||||
|
||||
/* Also catch fields of types defined in this places which match our
|
||||
text string. Only complete on types visible from current context. */
|
||||
ALL_BLOCK_SYMBOLS (b, iter, sym)
|
||||
{
|
||||
COMPLETION_LIST_ADD_SYMBOL (sym, sym_text, sym_text_len, text,
|
||||
word);
|
||||
completion_list_add_fields (sym, sym_text, sym_text_len, text,
|
||||
word);
|
||||
}
|
||||
|
||||
ALL_BLOCK_SYMBOLS (b, iter, sym)
|
||||
{
|
||||
QUIT;
|
||||
COMPLETION_LIST_ADD_SYMBOL (sym, sym_text, sym_text_len, text, word);
|
||||
if (SYMBOL_CLASS (sym) == LOC_TYPEDEF)
|
||||
{
|
||||
struct type *t = SYMBOL_TYPE (sym);
|
||||
enum type_code c = TYPE_CODE (t);
|
||||
/* Stop when we encounter an enclosing function. Do not stop for
|
||||
non-inlined functions - the locals of the enclosing function
|
||||
are in scope for a nested function. */
|
||||
if (BLOCK_FUNCTION (b) != NULL && block_inlined_p (b))
|
||||
break;
|
||||
b = BLOCK_SUPERBLOCK (b);
|
||||
}
|
||||
|
||||
if (c == TYPE_CODE_UNION || c == TYPE_CODE_STRUCT)
|
||||
{
|
||||
for (j = TYPE_N_BASECLASSES (t); j < TYPE_NFIELDS (t); j++)
|
||||
{
|
||||
if (TYPE_FIELD_NAME (t, j))
|
||||
{
|
||||
completion_list_add_name (TYPE_FIELD_NAME (t, j),
|
||||
sym_text, sym_text_len, text, word);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Add fields from the file's types; symbols will be added below. */
|
||||
|
||||
if (surrounding_static_block != NULL)
|
||||
ALL_BLOCK_SYMBOLS (surrounding_static_block, iter, sym)
|
||||
completion_list_add_fields (sym, sym_text, sym_text_len, text, word);
|
||||
|
||||
if (surrounding_global_block != NULL)
|
||||
ALL_BLOCK_SYMBOLS (surrounding_global_block, iter, sym)
|
||||
completion_list_add_fields (sym, sym_text, sym_text_len, text, word);
|
||||
|
||||
/* Go through the symtabs and check the externs and statics for
|
||||
symbols which match. */
|
||||
|
@ -3907,9 +3950,6 @@ default_make_symbol_completion_list (char *text, char *word)
|
|||
{
|
||||
QUIT;
|
||||
b = BLOCKVECTOR_BLOCK (BLOCKVECTOR (s), STATIC_BLOCK);
|
||||
/* Don't do this block twice. */
|
||||
if (b == surrounding_static_block)
|
||||
continue;
|
||||
ALL_BLOCK_SYMBOLS (b, iter, sym)
|
||||
{
|
||||
COMPLETION_LIST_ADD_SYMBOL (sym, sym_text, sym_text_len, text, word);
|
||||
|
@ -4375,6 +4415,25 @@ skip_prologue_using_sal (struct gdbarch *gdbarch, CORE_ADDR func_addr)
|
|||
line mark the prologue -> body transition. */
|
||||
if (sal.line >= prologue_sal.line)
|
||||
break;
|
||||
|
||||
/* The line number is smaller. Check that it's from the
|
||||
same function, not something inlined. If it's inlined,
|
||||
then there is no point comparing the line numbers. */
|
||||
bl = block_for_pc (prologue_sal.end);
|
||||
while (bl)
|
||||
{
|
||||
if (block_inlined_p (bl))
|
||||
break;
|
||||
if (BLOCK_FUNCTION (bl))
|
||||
{
|
||||
bl = NULL;
|
||||
break;
|
||||
}
|
||||
bl = BLOCK_SUPERBLOCK (bl);
|
||||
}
|
||||
if (bl != NULL)
|
||||
break;
|
||||
|
||||
/* The case in which compiler's optimizer/scheduler has
|
||||
moved instructions into the prologue. We look ahead in
|
||||
the function looking for address ranges whose
|
||||
|
|
16
gdb/symtab.h
16
gdb/symtab.h
|
@ -573,9 +573,18 @@ struct symbol
|
|||
|
||||
unsigned is_argument : 1;
|
||||
|
||||
/* Line number of definition. FIXME: Should we really make the assumption
|
||||
that nobody will try to debug files longer than 64K lines? What about
|
||||
machine generated programs? */
|
||||
/* Whether this is an inlined function (class LOC_BLOCK only). */
|
||||
unsigned is_inlined : 1;
|
||||
|
||||
/* Line number of this symbol's definition, except for inlined
|
||||
functions. For an inlined function (class LOC_BLOCK and
|
||||
SYMBOL_INLINED set) this is the line number of the function's call
|
||||
site. Inlined function symbols are not definitions, and they are
|
||||
never found by symbol table lookup.
|
||||
|
||||
FIXME: Should we really make the assumption that nobody will try
|
||||
to debug files longer than 64K lines? What about machine
|
||||
generated programs? */
|
||||
|
||||
unsigned short line;
|
||||
|
||||
|
@ -613,6 +622,7 @@ struct symbol
|
|||
#define SYMBOL_DOMAIN(symbol) (symbol)->domain
|
||||
#define SYMBOL_CLASS(symbol) (symbol)->aclass
|
||||
#define SYMBOL_IS_ARGUMENT(symbol) (symbol)->is_argument
|
||||
#define SYMBOL_INLINED(symbol) (symbol)->is_inlined
|
||||
#define SYMBOL_TYPE(symbol) (symbol)->type
|
||||
#define SYMBOL_LINE(symbol) (symbol)->line
|
||||
#define SYMBOL_SYMTAB(symbol) (symbol)->symtab
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include "gdbthread.h"
|
||||
#include "solib.h"
|
||||
#include "exec.h"
|
||||
#include "inline-frame.h"
|
||||
|
||||
static void target_info (char *, int);
|
||||
|
||||
|
@ -2037,6 +2038,7 @@ target_resume (ptid_t ptid, int step, enum target_signal signal)
|
|||
|
||||
set_executing (ptid, 1);
|
||||
set_running (ptid, 1);
|
||||
clear_inline_frame_state (ptid);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,16 @@
|
|||
2009-06-27 Daniel Jacobowitz <dan@codesourcery.com>
|
||||
|
||||
* gdb.base/break.exp: Add an XFAIL for gcc/36748.
|
||||
* gdb.cp/annota2.exp: Accept frames-invalid in more places.
|
||||
* gdb.opt/Makefile.in (EXECUTABLES): Update.
|
||||
* gdb.opt/clobbered-registers-O2.exp: Update to GPL v3.
|
||||
* gdb.opt/inline-bt.c, gdb.opt/inline-bt.exp,
|
||||
gdb.opt/inline-cmds.c, gdb.opt/inline-cmds.exp,
|
||||
gdb.opt/inline-locals.c, gdb.opt/inline-locals.exp,
|
||||
gdb.opt/inline-markers.c: New files.
|
||||
* lib/gdb.exp (skip_inline_frame_tests): New function.
|
||||
(skip_inline_var_tests): New function.
|
||||
|
||||
2009-06-27 Andreas Schwab <schwab@linux-m68k.org>
|
||||
|
||||
* gdb.cp/exception.exp: Don't require $hex before inner frame in
|
||||
|
|
|
@ -880,6 +880,13 @@ gdb_expect {
|
|||
# marker4() is defined at line 46 when compiled with -DPROTOTYPES
|
||||
pass "run until breakpoint set at small function, optimized file (line bp_location14)"
|
||||
}
|
||||
-re "Breakpoint $decimal, factorial \\(.*\\) .*\{\r\n$gdb_prompt" {
|
||||
# GCC 4.3 emits bad line number information - see gcc/36748.
|
||||
if { [test_compiler_info "gcc-4-3-*"] } {
|
||||
setup_xfail *-*-*
|
||||
}
|
||||
fail "run until breakpoint set at small function, optimized file"
|
||||
}
|
||||
-re ".*$gdb_prompt " {
|
||||
fail "run until breakpoint set at small function, optimized file"
|
||||
}
|
||||
|
|
|
@ -119,10 +119,11 @@ gdb_expect {
|
|||
# continue until exit
|
||||
# this will test:
|
||||
# annotate-exited
|
||||
# `a.x is 1' is asynchronous regarding to `frames-invalid'.
|
||||
#
|
||||
send_gdb "continue\n"
|
||||
gdb_expect {
|
||||
-re "\r\n\032\032post-prompt\r\nContinuing.\r\n\r\n\032\032starting\(\r\n\r\n\032\032frames-invalid\)+\r\na.x is 1\r\n\r\n\032\032exited 0\r\n\r\nProgram exited normally.\r\n\r\n\032\032stopped\r\n$gdb_prompt$" \
|
||||
-re "\r\n\032\032post-prompt\r\nContinuing.\r\n\r\n\032\032starting\(\r\n\r\n\032\032frames-invalid\)*\r\na.x is 1\r\n\(\r\n\032\032frames-invalid\r\n\)*\r\n\032\032exited 0\r\n\r\nProgram exited normally.\r\n\r\n\032\032stopped\r\n$gdb_prompt$" \
|
||||
{ pass "continue until exit" }
|
||||
-re ".*$gdb_prompt$" { fail "continue to exit" }
|
||||
timeout { fail "continue to exit (timeout)" }
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
VPATH = @srcdir@
|
||||
srcdir = @srcdir@
|
||||
|
||||
EXECUTABLES = hello/hello
|
||||
EXECUTABLES = clobbered-registers-O2 inline-bt inline-cmds inline-locals
|
||||
|
||||
MISCELLANEOUS =
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#
|
||||
# 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 2 of the License, or
|
||||
# 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,
|
||||
|
@ -11,8 +11,7 @@
|
|||
# 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, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# This file is part of the gdb testsuite.
|
||||
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/* Copyright (C) 2008 Free Software Foundation, Inc.
|
||||
|
||||
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/>. */
|
||||
|
||||
int x, y;
|
||||
volatile int result;
|
||||
|
||||
void bar(void);
|
||||
|
||||
inline int func1(void)
|
||||
{
|
||||
bar ();
|
||||
return x * y;
|
||||
}
|
||||
|
||||
inline int func2(void)
|
||||
{
|
||||
return x * func1 ();
|
||||
}
|
||||
|
||||
int main (void)
|
||||
{
|
||||
int val;
|
||||
|
||||
x = 7;
|
||||
y = 8;
|
||||
bar ();
|
||||
|
||||
val = func1 ();
|
||||
result = val;
|
||||
|
||||
val = func2 ();
|
||||
result = val;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
# Copyright 2008 Free Software Foundation, Inc.
|
||||
|
||||
# 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/>.
|
||||
|
||||
set testfile "inline-bt"
|
||||
set srcfile ${testfile}.c
|
||||
set srcfile2 "inline-markers.c"
|
||||
set fullsrcfile "${srcdir}/${subdir}/${srcfile}"
|
||||
set fullsrcfile2 "${srcdir}/${subdir}/${srcfile2}"
|
||||
set sources [list ${fullsrcfile} ${fullsrcfile2}]
|
||||
set binfile ${objdir}/${subdir}/${testfile}
|
||||
|
||||
if { [gdb_compile ${sources} ${binfile} \
|
||||
executable {debug optimize=-O2}] != "" } {
|
||||
untested inline-bt.exp
|
||||
return -1
|
||||
}
|
||||
|
||||
gdb_exit
|
||||
gdb_start
|
||||
gdb_reinitialize_dir $srcdir/$subdir
|
||||
gdb_load ${binfile}
|
||||
|
||||
runto_main
|
||||
|
||||
get_compiler_info $binfile
|
||||
get_debug_format
|
||||
if { [skip_inline_frame_tests] } {
|
||||
untested inline-bt.exp
|
||||
return
|
||||
}
|
||||
|
||||
set line1 [gdb_get_line_number "set breakpoint 1 here" ${srcfile2}]
|
||||
gdb_breakpoint $srcfile2:$line1
|
||||
|
||||
gdb_test "continue" ".*set breakpoint 1 here.*" "continue to bar (1)"
|
||||
gdb_test "backtrace" "#0 bar.*#1 .*main.*" "backtrace from bar (1)"
|
||||
gdb_test "info frame" ".*called by frame.*" "bar not inlined"
|
||||
|
||||
gdb_test "continue" ".*set breakpoint 1 here.*" "continue to bar (2)"
|
||||
gdb_test "backtrace" "#0 bar.*#1 .*func1.*#2 .*main.*" \
|
||||
"backtrace from bar (2)"
|
||||
gdb_test "up" "#1 .*func1.*" "up from bar (2)"
|
||||
gdb_test "info frame" ".*inlined into frame.*" "func1 inlined (2)"
|
||||
|
||||
gdb_test "continue" ".*set breakpoint 1 here.*" "continue to bar (3)"
|
||||
gdb_test "backtrace" "#0 bar.*#1 .*func1.*#2 .*func2.*#3 .*main.*" \
|
||||
"backtrace from bar (3)"
|
||||
gdb_test "up" "#1 .*func1.*" "up from bar (3)"
|
||||
gdb_test "info frame" ".*inlined into frame.*" "func1 inlined (3)"
|
||||
gdb_test "up" "#2 .*func2.*" "up from func1 (3)"
|
||||
gdb_test "info frame" ".*inlined into frame.*" "func2 inlined (3)"
|
|
@ -0,0 +1,85 @@
|
|||
/* Copyright (C) 2008 Free Software Foundation, Inc.
|
||||
|
||||
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/>. */
|
||||
|
||||
int x, y;
|
||||
volatile int result;
|
||||
|
||||
void bar(void);
|
||||
void marker(void);
|
||||
void noinline(void);
|
||||
|
||||
inline int func1(void)
|
||||
{
|
||||
bar ();
|
||||
return x * y;
|
||||
}
|
||||
|
||||
inline int func2(void)
|
||||
{
|
||||
return x * func1 ();
|
||||
}
|
||||
|
||||
inline void func3(void)
|
||||
{
|
||||
bar ();
|
||||
}
|
||||
|
||||
inline void outer_inline1(void)
|
||||
{
|
||||
noinline ();
|
||||
}
|
||||
|
||||
inline void outer_inline2(void)
|
||||
{
|
||||
outer_inline1 ();
|
||||
}
|
||||
|
||||
int main (void)
|
||||
{ /* start of main */
|
||||
int val;
|
||||
|
||||
x = 7;
|
||||
y = 8;
|
||||
|
||||
result = func1 ();
|
||||
result = func2 ();
|
||||
marker ();
|
||||
|
||||
result = 0;
|
||||
result = 0; /* set breakpoint 3 here */
|
||||
|
||||
func1 (); /* first call */
|
||||
func1 (); /* second call */
|
||||
marker ();
|
||||
|
||||
result = 0;
|
||||
result = 0; /* set breakpoint 4 here */
|
||||
|
||||
func1 ();
|
||||
func3 ();
|
||||
marker ();
|
||||
|
||||
result = 0;
|
||||
result = 0; /* set breakpoint 5 here */
|
||||
|
||||
marker ();
|
||||
func1 ();
|
||||
func3 ();
|
||||
marker (); /* set breakpoint 6 here */
|
||||
|
||||
outer_inline2 ();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,308 @@
|
|||
# Copyright 2008 Free Software Foundation, Inc.
|
||||
|
||||
# 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/>.
|
||||
|
||||
set testfile "inline-cmds"
|
||||
set srcfile "${testfile}.c"
|
||||
set srcfile2 "inline-markers.c"
|
||||
set fullsrcfile "${srcdir}/${subdir}/${srcfile}"
|
||||
set fullsrcfile2 "${srcdir}/${subdir}/${srcfile2}"
|
||||
set sources [list ${fullsrcfile} ${fullsrcfile2}]
|
||||
set binfile ${objdir}/${subdir}/${testfile}
|
||||
|
||||
if { [gdb_compile $sources ${binfile} \
|
||||
executable {debug optimize=-O2}] != "" } {
|
||||
untested inline-cmds.exp
|
||||
return -1
|
||||
}
|
||||
|
||||
gdb_exit
|
||||
gdb_start
|
||||
gdb_reinitialize_dir $srcdir/$subdir
|
||||
gdb_load ${binfile}
|
||||
|
||||
gdb_test "set listsize 1" ""
|
||||
|
||||
runto_main
|
||||
|
||||
get_compiler_info $binfile
|
||||
get_debug_format
|
||||
if { [skip_inline_frame_tests] } {
|
||||
untested inline-cmds.exp
|
||||
return
|
||||
}
|
||||
|
||||
# First, check that the things we expected to be inlined really were,
|
||||
# and those that shouldn't be weren't.
|
||||
set line1 [gdb_get_line_number "set breakpoint 1 here" ${srcfile2}]
|
||||
gdb_breakpoint $srcfile2:$line1
|
||||
set line2 [gdb_get_line_number "set breakpoint 2 here" ${srcfile2}]
|
||||
gdb_breakpoint $srcfile2:$line2
|
||||
|
||||
gdb_test "continue" ".*set breakpoint 1 here.*" "continue to bar (1)"
|
||||
gdb_test "backtrace" "#0 bar.*#1 .*func1.*#2 .*main.*" \
|
||||
"backtrace from bar (1)"
|
||||
gdb_test "up" "#1 .*func1.*" "up from bar (1)"
|
||||
gdb_test "info frame" ".*inlined into frame.*" "func1 inlined (1)"
|
||||
|
||||
gdb_test "continue" ".*set breakpoint 1 here.*" "continue to bar (2)"
|
||||
gdb_test "backtrace" "#0 bar.*#1 .*func1.*#2 .*func2.*#3 .*main.*" \
|
||||
"backtrace from bar (2)"
|
||||
gdb_test "up" "#1 .*func1.*" "up from bar (2)"
|
||||
gdb_test "info frame" ".*inlined into frame.*" "func1 inlined (2)"
|
||||
gdb_test "up" "#2 .*func2.*" "up from func1 (2)"
|
||||
gdb_test "info frame" ".*inlined into frame.*" "func2 inlined (2)"
|
||||
|
||||
gdb_test "continue" ".*set breakpoint 2 here.*" "continue to marker"
|
||||
gdb_test "backtrace" "#0 marker.*#1 .*main.*" "backtrace from marker"
|
||||
gdb_test "info frame" ".*called by frame.*" "marker not inlined"
|
||||
|
||||
# Next, check that we can next over inlined functions. We should not end up
|
||||
# inside any of them.
|
||||
delete_breakpoints
|
||||
runto_main
|
||||
|
||||
# The lines before the first inlined call.
|
||||
set first "x = 7|y = 8"
|
||||
|
||||
# Some extra lines that end up in our stepping due to code motion.
|
||||
set opt "start of main|result = 0"
|
||||
|
||||
# We start this test with a "list" instead of a "next", in case the
|
||||
# first non-prologue instruction in main comes from the inlined function.
|
||||
set msg "next over inlined functions"
|
||||
gdb_test_multiple "list" $msg {
|
||||
-re "($first|result = func1|result = func2|$opt).*$gdb_prompt $" {
|
||||
send_gdb "next\r"
|
||||
exp_continue
|
||||
}
|
||||
-re "marker \\\(\\\);\r\n$gdb_prompt $" {
|
||||
pass $msg
|
||||
}
|
||||
}
|
||||
|
||||
# Check that when next shows the call of func1, it has not happened yet.
|
||||
runto_main
|
||||
|
||||
# Like the return value of gdb_test: -1 something is wrong, 0 passed, 1 failed.
|
||||
set bt_test -1
|
||||
set x_test -1
|
||||
set func1_step -1
|
||||
|
||||
set last_was_func1_call 0
|
||||
set msg "next past inlined func1"
|
||||
gdb_test_multiple "list" $msg {
|
||||
-re "($first|$opt).*$gdb_prompt $" {
|
||||
set last_was_func1_call 0
|
||||
send_gdb "next\r"
|
||||
exp_continue
|
||||
}
|
||||
-re "result = func1 \\\(\\\);\r\n$gdb_prompt $" {
|
||||
# Check whether x has been set. If 0, we may be doing something
|
||||
# else associated with this line besides the inlined call - e.g.
|
||||
# loading the address of result. If 7, we may be at the call site.
|
||||
# If 15, though, we might be past the call and back at the store to
|
||||
# result - that's OK, as long as we weren't just here (see
|
||||
# func1_step above).
|
||||
set x_val -1
|
||||
gdb_test_multiple "print x" "" {
|
||||
-re "\\\$$decimal = (\[0-9\]*)\r\n$gdb_prompt $" {
|
||||
set x_val $expect_out(1,string)
|
||||
}
|
||||
-re "$gdb_prompt $" { }
|
||||
}
|
||||
if { $x_val == 0 || $x_val == 7 } {
|
||||
if { $x_test != 1 } {
|
||||
set x_test 0
|
||||
}
|
||||
} elseif { $x_val == 15 } {
|
||||
if { $func1_step == -1 } {
|
||||
# We passed func1 without stopping at the call site.
|
||||
set x_test 1
|
||||
}
|
||||
} else {
|
||||
set x_test 1
|
||||
}
|
||||
|
||||
# func1 should not show up on backtraces if we are at its call
|
||||
# site.
|
||||
if { $bt_test != 1 } {
|
||||
set bt_test [gdb_test "backtrace" "#0 \[^#]*main.*" ""]
|
||||
}
|
||||
|
||||
# When we next over func1, we should not return to the same
|
||||
# line. But we might go past the line, according to source
|
||||
# code order, and then come back. A valid but odd layout is
|
||||
# body of func1, load result's address into a register using
|
||||
# the source location of "result = 0" several lines down, and
|
||||
# then return to this line for the store. GCC 4.3 does that
|
||||
# on ARM.
|
||||
if { $last_was_func1_call } {
|
||||
set func1_step 1
|
||||
} elseif { $func1_step == -1 } {
|
||||
set func1_step 0
|
||||
}
|
||||
set last_was_func1_call 1
|
||||
|
||||
send_gdb "next\r"
|
||||
exp_continue
|
||||
}
|
||||
|
||||
-re "result = func2 \\\(\\\);\r\n$gdb_prompt $" {
|
||||
pass $msg
|
||||
}
|
||||
}
|
||||
|
||||
if { $x_test == 0 } {
|
||||
pass "print x before func1"
|
||||
} else {
|
||||
fail "print x before func1"
|
||||
}
|
||||
|
||||
if { $bt_test == 0 } {
|
||||
pass "backtrace does not include func1"
|
||||
} else {
|
||||
fail "backtrace does not include func1"
|
||||
}
|
||||
|
||||
if { $bt_test == 0 } {
|
||||
pass "stepped over call to func1"
|
||||
} else {
|
||||
fail "stepped over call to func1"
|
||||
}
|
||||
|
||||
# Next, check that we can single step into inlined functions. We should always
|
||||
# "stop" at the call sites before entering them.
|
||||
runto_main
|
||||
|
||||
set msg "step into func1"
|
||||
set saw_call_site 0
|
||||
gdb_test_multiple "list" $msg {
|
||||
-re "($first|$opt).*$gdb_prompt $" {
|
||||
send_gdb "step\r"
|
||||
exp_continue
|
||||
}
|
||||
-re "result = func1.*$gdb_prompt $" {
|
||||
set saw_call_site 1
|
||||
send_gdb "step\r"
|
||||
exp_continue
|
||||
}
|
||||
-re "func1 \\\(\\\) at .*\r\n$decimal.*bar \\\(\\\);\r\n$gdb_prompt $" {
|
||||
if { $saw_call_site } {
|
||||
pass $msg
|
||||
} else {
|
||||
fail $msg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Check finish out of an inlined function.
|
||||
set msg "finish from func1"
|
||||
gdb_test_multiple "finish" $msg {
|
||||
-re "result = func1 \\\(\\\);\r\n$gdb_prompt $" {
|
||||
pass $msg
|
||||
}
|
||||
-re "($first|$opt).*$gdb_prompt $" {
|
||||
# Whoops. We finished, but ended up back at an earlier line. Keep
|
||||
# trying.
|
||||
send_gdb "step\r"
|
||||
exp_continue
|
||||
}
|
||||
-re "func1 \\\(\\\) at .*\r\n$decimal.*bar \\\(\\\);\r\n$gdb_prompt $" {
|
||||
send_gdb "finish\r"
|
||||
exp_continue
|
||||
}
|
||||
}
|
||||
|
||||
# Test some corner cases involving consecutive inlined functions.
|
||||
set line3 [gdb_get_line_number "set breakpoint 3 here"]
|
||||
gdb_breakpoint $line3
|
||||
gdb_continue_to_breakpoint "consecutive func1"
|
||||
|
||||
gdb_test "next" ".*func1 .*first call.*" "next to first func1"
|
||||
set msg "next to second func1"
|
||||
gdb_test_multiple "next" $msg {
|
||||
-re ".*func1 .*second call.*$gdb_prompt $" {
|
||||
pass $msg
|
||||
}
|
||||
-re ".*marker .*$gdb_prompt $" {
|
||||
# This assembles to two consecutive call instructions.
|
||||
# Both appear to be at the same line, because they're
|
||||
# in the body of the same inlined function. This is
|
||||
# reasonable for the line table. GDB should take the
|
||||
# containing block and/or function into account when
|
||||
# deciding how far to step. The single line table entry
|
||||
# is actually two consecutive instances of the same line.
|
||||
kfail gdb/NNNN $msg
|
||||
}
|
||||
}
|
||||
|
||||
# It is easier when the two inlined functions are not on the same line.
|
||||
set line4 [gdb_get_line_number "set breakpoint 4 here"]
|
||||
gdb_breakpoint $line4
|
||||
gdb_continue_to_breakpoint "func1 then func3"
|
||||
|
||||
gdb_test "next" ".*func1 \\\(\\\);" "next to func1 before func3"
|
||||
gdb_test "next" ".*func3 \\\(\\\);" "next to func3"
|
||||
|
||||
# Test finishing out of one thing and into another.
|
||||
set line5 [gdb_get_line_number "set breakpoint 5 here"]
|
||||
gdb_breakpoint $line5
|
||||
gdb_continue_to_breakpoint "finish into func1"
|
||||
|
||||
gdb_test "next" ".*marker \\\(\\\);" "next to finish marker"
|
||||
gdb_test "step" ".*set breakpoint 2 here.*" "step into finish marker"
|
||||
gdb_test "finish" "func1 \\\(\\\);" "finish from marker to func1"
|
||||
|
||||
gdb_test "step" "bar \\\(\\\);" "step into func1 for finish"
|
||||
gdb_test "finish" "func3 \\\(\\\);" "finish from func1 to func3"
|
||||
|
||||
# Test a deeper call stack.
|
||||
set line6 [gdb_get_line_number "set breakpoint 6 here"]
|
||||
gdb_breakpoint $line6
|
||||
gdb_continue_to_breakpoint "before the outer_inline call"
|
||||
gdb_test "step" "marker \\\(\\\) at .*" "reach 1 the outer_inline call"
|
||||
gdb_test "finish" "main \\\(\\\) at .*outer_inline2 \\\(\\\);" "reach outer_inline2"
|
||||
gdb_test "bt" "#0 main.*" "backtrace at main of outer_inline"
|
||||
gdb_test "step" "outer_inline2 \\\(\\\) at .*" "enter outer_inline2"
|
||||
gdb_test "bt" "#0 outer_inline2.*#1 main.*" "backtrace at outer_inline2"
|
||||
gdb_test "step" "outer_inline1 \\\(\\\) at .*" "enter outer_inline1 from outer_inline2"
|
||||
|
||||
set msg "backtrace at outer_inline1"
|
||||
gdb_test_multiple "bt" $msg {
|
||||
-re "#0 outer_inline1.*#1 outer_inline2.*#2 main.*$gdb_prompt $" {
|
||||
pass $msg
|
||||
}
|
||||
-re "#0 $hex in outer_inline1.*#1 outer_inline2.*#2 main.*$gdb_prompt $" {
|
||||
# Binutils PR gas/6717. Gas moves .loc past .p2align and the
|
||||
# leading nop of the inlined call appears to be on the same line
|
||||
# as main's call to marker.
|
||||
xfail $msg
|
||||
gdb_test "step" "noinline \\\(\\\);" "step to call of noinline"
|
||||
}
|
||||
}
|
||||
|
||||
gdb_test "step" "noinline \\\(\\\) at .*" "enter noinline from outer_inline1"
|
||||
gdb_test "bt" "#0 noinline.*#1 .*outer_inline1.*#2 .*outer_inline2.*#3 main.*" "backtrace at noinline from outer_inline1"
|
||||
gdb_test "step" "inlined_fn \\\(\\\) at .*" "enter inlined_fn from noinline"
|
||||
gdb_test "bt" "#0 inlined_fn.*#1 noinline.*#2 .*outer_inline1.*#3 .*outer_inline2.*#4 main.*" "backtrace at inlined_fn from noinline"
|
||||
gdb_test "info frame" ".*inlined into frame.*" "inlined_fn from noinline inlined"
|
||||
gdb_test "up" "#1 noinline.*" "up to noinline"
|
||||
gdb_test "info frame" ".*\n called by frame.*" "noinline from outer_inline1 not inlined"
|
||||
gdb_test "up" "#2 .*outer_inline1.*" "up to outer_inline1"
|
||||
gdb_test "info frame" ".*inlined into frame.*" "outer_inline1 inlined"
|
||||
gdb_test "up" "#3 .*outer_inline2.*" "up to outer_inline2"
|
||||
gdb_test "info frame" ".*inlined into frame.*" "outer_inline2 inlined"
|
||||
gdb_test "up" "#4 main.*" "up from outer_inline2"
|
||||
gdb_test "info frame" ".*\n caller of frame.*" "main not inlined"
|
|
@ -0,0 +1,52 @@
|
|||
/* Copyright (C) 2008 Free Software Foundation, Inc.
|
||||
|
||||
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/>. */
|
||||
|
||||
int x, y;
|
||||
volatile int result;
|
||||
volatile int *array_p;
|
||||
|
||||
void bar(void);
|
||||
|
||||
inline int func1(int arg1)
|
||||
{
|
||||
int array[64];
|
||||
array_p = array;
|
||||
array[0] = result;
|
||||
array[1] = arg1;
|
||||
bar ();
|
||||
return x * y + array_p[0] * arg1;
|
||||
}
|
||||
|
||||
inline int func2(int arg2)
|
||||
{
|
||||
return x * func1 (arg2);
|
||||
}
|
||||
|
||||
int main (void)
|
||||
{
|
||||
int val;
|
||||
|
||||
x = 7;
|
||||
y = 8;
|
||||
bar ();
|
||||
|
||||
val = func1 (result);
|
||||
result = val;
|
||||
|
||||
val = func2 (result);
|
||||
result = val;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
# Copyright 2008 Free Software Foundation, Inc.
|
||||
|
||||
# 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/>.
|
||||
|
||||
set testfile "inline-locals"
|
||||
set srcfile ${testfile}.c
|
||||
set srcfile2 "inline-markers.c"
|
||||
set fullsrcfile "${srcdir}/${subdir}/${srcfile}"
|
||||
set fullsrcfile2 "${srcdir}/${subdir}/${srcfile2}"
|
||||
set sources [list ${fullsrcfile} ${fullsrcfile2}]
|
||||
set binfile ${objdir}/${subdir}/${testfile}
|
||||
|
||||
if { [gdb_compile ${sources} ${binfile} \
|
||||
executable {debug optimize=-O2}] != "" } {
|
||||
untested inline-locals.exp
|
||||
return -1
|
||||
}
|
||||
|
||||
gdb_exit
|
||||
gdb_start
|
||||
gdb_reinitialize_dir $srcdir/$subdir
|
||||
gdb_load ${binfile}
|
||||
|
||||
runto_main
|
||||
|
||||
get_compiler_info $binfile
|
||||
get_debug_format
|
||||
if { [skip_inline_var_tests] } {
|
||||
untested inline-bt.exp
|
||||
return
|
||||
}
|
||||
|
||||
set no_frames [skip_inline_frame_tests]
|
||||
|
||||
set line1 [gdb_get_line_number "set breakpoint 1 here" ${srcfile2}]
|
||||
gdb_breakpoint $srcfile2:$line1
|
||||
|
||||
gdb_test "continue" ".*set breakpoint 1 here.*" "continue to bar (1)"
|
||||
|
||||
gdb_test "continue" ".*set breakpoint 1 here.*" "continue to bar (2)"
|
||||
|
||||
if { ! $no_frames } {
|
||||
gdb_test "backtrace" "#0 bar.*#1 .*func1.*#2 .*main.*" \
|
||||
"backtrace from bar (2)"
|
||||
gdb_test "up" "#1 .*func1 .* at .*" "up from bar (2)"
|
||||
gdb_test "info frame" ".*inlined into frame.*" "func1 inlined (2)"
|
||||
gdb_test "info locals" "array = {.*}" "info locals above bar (2)"
|
||||
|
||||
set msg "info args above bar (2)"
|
||||
gdb_test_multiple "info args" $msg {
|
||||
-re "arg1 = $decimal\r\n$gdb_prompt $" {
|
||||
pass $msg
|
||||
}
|
||||
-re "arg1 = <value optimized out>\r\narg1 = <value optimized out>\r\n$gdb_prompt $" {
|
||||
# GCC 4.4 loses location information for arg1 (like GCC
|
||||
# 4.3) and also generates a strange DIE tree that causes
|
||||
# us to display the argument twice: inlined func1 has the
|
||||
# abstract func1 for DW_AT_abstract_origin but its arg1
|
||||
# child has the out of line func1's arg1 for
|
||||
# DW_AT_abstract_origin, with a location list unrelated to
|
||||
# the inlined instance.
|
||||
if { [test_compiler_info "gcc-4-4-*"] || [test_compiler_info "gcc-4-5-*"] } {
|
||||
setup_xfail *-*-* gcc/40573
|
||||
}
|
||||
fail $msg
|
||||
}
|
||||
-re "arg1 = <value optimized out>\r\n$gdb_prompt $" {
|
||||
# GCC 4.3 loses location information for arg1. GCC 4.2 is OK.
|
||||
if { [test_compiler_info "gcc-4-3-*"] } {
|
||||
setup_xfail *-*-*
|
||||
}
|
||||
fail $msg
|
||||
}
|
||||
}
|
||||
} else {
|
||||
gdb_test "up" "#1 .*main .* at .*" "up from bar (2)"
|
||||
gdb_test "info locals" ".*arg1 = 0.*" "info locals above bar (2)"
|
||||
}
|
||||
|
||||
# Make sure that locals on the stack are found. This is an array to
|
||||
# prevent it from living in a register.
|
||||
gdb_test "print array\[0\]" "\\\$$decimal = 0" "print local (2)"
|
||||
|
||||
if { ! $no_frames } {
|
||||
# Verify that we do not print out variables from the inlined
|
||||
# function's caller.
|
||||
gdb_test "print val" "No symbol \"val\" in current context\\." \
|
||||
"print out of scope local"
|
||||
}
|
||||
|
||||
# Repeat the tests from a depth of two inlined functions, and with a
|
||||
# more interesting value in the local array.
|
||||
gdb_test "continue" ".*set breakpoint 1 here.*" "continue to bar (3)"
|
||||
if { ! $no_frames } {
|
||||
gdb_test "backtrace" "#0 bar.*#1 .*func1.*#2 .*func2.*#3 .*main.*" \
|
||||
"backtrace from bar (3)"
|
||||
gdb_test "up" "#1 .*func1 .* at .*" "up from bar (3)"
|
||||
gdb_test "info frame" ".*inlined into frame.*" "func1 inlined (3)"
|
||||
gdb_test "info locals" "array = {.*}" "info locals above bar (3)"
|
||||
|
||||
set msg "info args above bar (3)"
|
||||
gdb_test_multiple "info args" $msg {
|
||||
-re "arg1 = $decimal\r\n$gdb_prompt $" {
|
||||
pass $msg
|
||||
}
|
||||
-re "arg1 = <value optimized out>\r\narg1 = <value optimized out>\r\n$gdb_prompt $" {
|
||||
# See the similar GCC 4.4 XFAIL above for an explanation.
|
||||
if { [test_compiler_info "gcc-4-4-*"] || [test_compiler_info "gcc-4-5-*"] } {
|
||||
setup_xfail *-*-* gcc/40573
|
||||
}
|
||||
fail $msg
|
||||
}
|
||||
-re "arg1 = <value optimized out>\r\n$gdb_prompt $" {
|
||||
# GCC 4.3 loses location information for arg1. GCC 4.2 is OK.
|
||||
if { [test_compiler_info "gcc-4-3-*"] } {
|
||||
setup_xfail *-*-*
|
||||
}
|
||||
fail $msg
|
||||
}
|
||||
}
|
||||
} else {
|
||||
gdb_test "up" "#1 .*main .* at .*" "up from bar (3)"
|
||||
gdb_test "info locals" ".*arg1 = 1.*" "info locals above bar (3a)"
|
||||
gdb_test "info locals" ".*arg2 = 184.*" "info locals above bar (3b)"
|
||||
}
|
||||
|
||||
gdb_test "print array\[0\]" "\\\$$decimal = 184" "print local (3)"
|
|
@ -0,0 +1,36 @@
|
|||
/* Copyright (C) 2008 Free Software Foundation, Inc.
|
||||
|
||||
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/>. */
|
||||
|
||||
extern int x, y;
|
||||
|
||||
void bar(void)
|
||||
{
|
||||
x += y; /* set breakpoint 1 here */
|
||||
}
|
||||
|
||||
void marker(void)
|
||||
{
|
||||
x += y; /* set breakpoint 2 here */
|
||||
}
|
||||
|
||||
inline void inlined_fn(void)
|
||||
{
|
||||
x += y;
|
||||
}
|
||||
|
||||
void noinline(void)
|
||||
{
|
||||
inlined_fn (); /* inlined */
|
||||
}
|
|
@ -1459,6 +1459,37 @@ proc skip_hp_tests {} {
|
|||
return $skip_hp
|
||||
}
|
||||
|
||||
# Return whether we should skip tests for showing inlined functions in
|
||||
# backtraces. Requires get_compiler_info and get_debug_format.
|
||||
|
||||
proc skip_inline_frame_tests {} {
|
||||
# GDB only recognizes inlining information in DWARF 2 (DWARF 3).
|
||||
if { ! [test_debug_format "DWARF 2"] } {
|
||||
return 1
|
||||
}
|
||||
|
||||
# GCC before 4.1 does not emit DW_AT_call_file / DW_AT_call_line.
|
||||
if { ([test_compiler_info "gcc-2-*"]
|
||||
|| [test_compiler_info "gcc-3-*"]
|
||||
|| [test_compiler_info "gcc-4-0-*"]) } {
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Return whether we should skip tests for showing variables from
|
||||
# inlined functions. Requires get_compiler_info and get_debug_format.
|
||||
|
||||
proc skip_inline_var_tests {} {
|
||||
# GDB only recognizes inlining information in DWARF 2 (DWARF 3).
|
||||
if { ! [test_debug_format "DWARF 2"] } {
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
set compiler_info "unknown"
|
||||
set gcc_compiled 0
|
||||
set hp_cc_compiler 0
|
||||
|
|
|
@ -1003,7 +1003,7 @@ value_of_variable (struct symbol *var, struct block *b)
|
|||
frame = block_innermost_frame (b);
|
||||
if (!frame)
|
||||
{
|
||||
if (BLOCK_FUNCTION (b)
|
||||
if (BLOCK_FUNCTION (b) && !block_inlined_p (b)
|
||||
&& SYMBOL_PRINT_NAME (BLOCK_FUNCTION (b)))
|
||||
error (_("No frame is currently executing in block %s."),
|
||||
SYMBOL_PRINT_NAME (BLOCK_FUNCTION (b)));
|
||||
|
|
Loading…
Reference in New Issue