diff options
Diffstat (limited to 'gdb')
42 files changed, 2195 insertions, 216 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index ad81a37..d274e2f 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,4 +1,102 @@ 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 ... (frame_unwind_caller_id): ... this. All callers updated. diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 6879bbb..e5d9c0b 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -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 \ @@ -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. diff --git a/gdb/block.c b/gdb/block.c index 9ebe975..1889ecd 100644 --- a/gdb/block.c +++ b/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 diff --git a/gdb/block.h b/gdb/block.h index 9b43144..53e7371 100644 --- a/gdb/block.h +++ b/gdb/block.h @@ -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 **); diff --git a/gdb/blockframe.c b/gdb/blockframe.c index f6565e5..41a3502 100644 --- a/gdb/blockframe.c +++ b/gdb/blockframe.c @@ -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); diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index 1998acf..710a792 100644 --- a/gdb/breakpoint.c +++ b/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); } diff --git a/gdb/buildsym.c b/gdb/buildsym.c index 8d7fed3..71bd8f4 100644 --- a/gdb/buildsym.c +++ b/gdb/buildsym.c @@ -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)) diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index 972e458..26b2aae 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -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 diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 94852ec..17fc7d6 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -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 diff --git a/gdb/dwarf2loc.c b/gdb/dwarf2loc.c index 16d8cee..f0f59e8 100644 --- a/gdb/dwarf2loc.c +++ b/gdb/dwarf2loc.c @@ -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; diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c index f5901c7..fe1f384 100644 --- a/gdb/dwarf2read.c +++ b/gdb/dwarf2read.c @@ -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) @@ -7025,6 +7043,9 @@ die_specification (struct die_info *die, struct dwarf2_cu **spec_cu) *spec_cu); if (spec_attr == NULL) + spec_attr = dwarf2_attr (die, DW_AT_abstract_origin, *spec_cu); + + if (spec_attr == NULL) return NULL; else return follow_die_ref (die, spec_attr, spec_cu); @@ -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: diff --git a/gdb/frame-unwind.c b/gdb/frame-unwind.c index 98d6b43..d3e102a 100644 --- a/gdb/frame-unwind.c +++ b/gdb/frame-unwind.c @@ -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; } diff --git a/gdb/frame.c b/gdb/frame.c index 74671a4..68c4146 100644 --- a/gdb/frame.c +++ b/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. */ @@ -271,13 +294,27 @@ get_frame_id (struct frame_info *fi) } 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. */ @@ -332,6 +369,15 @@ frame_id_p (struct frame_id l) } 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) { int eq; @@ -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) { @@ -499,6 +569,12 @@ frame_unwind_caller_pc (struct frame_info *this_frame) } 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) { struct frame_info *next_frame = this_frame->next; @@ -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 diff --git a/gdb/frame.h b/gdb/frame.h index 255e392..261c329 100644 --- a/gdb/frame.h +++ b/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 diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h index d8cb4b3..956ef9a 100644 --- a/gdb/gdbthread.h +++ b/gdb/gdbthread.h @@ -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; diff --git a/gdb/infcmd.c b/gdb/infcmd.c index 47064e5..b1ccf9f 100644 --- a/gdb/infcmd.c +++ b/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))); diff --git a/gdb/inferior.h b/gdb/inferior.h index ce255ee..7312e51 100644 --- a/gdb/inferior.h +++ b/gdb/inferior.h @@ -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); diff --git a/gdb/infrun.c b/gdb/infrun.c index 5f1b16b..a2ab386 100644 --- a/gdb/infrun.c +++ b/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; diff --git a/gdb/inline-frame.c b/gdb/inline-frame.c new file mode 100644 index 0000000..731d3ad --- /dev/null +++ b/gdb/inline-frame.c @@ -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; +} diff --git a/gdb/inline-frame.h b/gdb/inline-frame.h new file mode 100644 index 0000000..210d6c3 --- /dev/null +++ b/gdb/inline-frame.h @@ -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) */ diff --git a/gdb/minsyms.c b/gdb/minsyms.c index e4b0f31..1a1a37f 100644 --- a/gdb/minsyms.c +++ b/gdb/minsyms.c @@ -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; diff --git a/gdb/regcache.c b/gdb/regcache.c index 13b9db4..bbf5812 100644 --- a/gdb/regcache.c +++ b/gdb/regcache.c @@ -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 (); } diff --git a/gdb/s390-tdep.c b/gdb/s390-tdep.c index b309524..4670d50 100644 --- a/gdb/s390-tdep.c +++ b/gdb/s390-tdep.c @@ -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 diff --git a/gdb/stack.c b/gdb/stack.c index 7d942d6..0f5b426 100644 --- a/gdb/stack.c +++ b/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 diff --git a/gdb/symtab.c b/gdb/symtab.c index ee138ff..f677ed0 100644 --- a/gdb/symtab.c +++ b/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. */ + + 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; - for (b = get_selected_block (0); b != NULL; b = BLOCK_SUPERBLOCK (b)) - { - if (!BLOCK_SUPERBLOCK (b)) - { - surrounding_static_block = b; /* For elmin of dups */ - } + 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); + } - /* Also catch fields of types defined in this places which match our - text string. Only complete on types visible from current context. */ + /* 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); + } - 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); + /* Add fields from the file's types; symbols will be added below. */ - 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); - } - } - } - } - } - } + 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 diff --git a/gdb/symtab.h b/gdb/symtab.h index ab06b1e..740d4e0 100644 --- a/gdb/symtab.h +++ b/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 diff --git a/gdb/target.c b/gdb/target.c index 7a5b768..ca94300 100644 --- a/gdb/target.c +++ b/gdb/target.c @@ -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; } } diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 1cb19c7..78678fe 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -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 diff --git a/gdb/testsuite/gdb.base/break.exp b/gdb/testsuite/gdb.base/break.exp index 7067dc3..20acd7e 100644 --- a/gdb/testsuite/gdb.base/break.exp +++ b/gdb/testsuite/gdb.base/break.exp @@ -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" } diff --git a/gdb/testsuite/gdb.cp/annota2.exp b/gdb/testsuite/gdb.cp/annota2.exp index 26c8abf..1f24abd 100644 --- a/gdb/testsuite/gdb.cp/annota2.exp +++ b/gdb/testsuite/gdb.cp/annota2.exp @@ -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)" } diff --git a/gdb/testsuite/gdb.opt/Makefile.in b/gdb/testsuite/gdb.opt/Makefile.in index e8bea9c..a35e59a 100644 --- a/gdb/testsuite/gdb.opt/Makefile.in +++ b/gdb/testsuite/gdb.opt/Makefile.in @@ -1,7 +1,7 @@ VPATH = @srcdir@ srcdir = @srcdir@ -EXECUTABLES = hello/hello +EXECUTABLES = clobbered-registers-O2 inline-bt inline-cmds inline-locals MISCELLANEOUS = diff --git a/gdb/testsuite/gdb.opt/clobbered-registers-O2.exp b/gdb/testsuite/gdb.opt/clobbered-registers-O2.exp index bccca91..a192fa5 100644 --- a/gdb/testsuite/gdb.opt/clobbered-registers-O2.exp +++ b/gdb/testsuite/gdb.opt/clobbered-registers-O2.exp @@ -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. diff --git a/gdb/testsuite/gdb.opt/inline-bt.c b/gdb/testsuite/gdb.opt/inline-bt.c new file mode 100644 index 0000000..402e283 --- /dev/null +++ b/gdb/testsuite/gdb.opt/inline-bt.c @@ -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; +} diff --git a/gdb/testsuite/gdb.opt/inline-bt.exp b/gdb/testsuite/gdb.opt/inline-bt.exp new file mode 100644 index 0000000..10325ab --- /dev/null +++ b/gdb/testsuite/gdb.opt/inline-bt.exp @@ -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)" diff --git a/gdb/testsuite/gdb.opt/inline-cmds.c b/gdb/testsuite/gdb.opt/inline-cmds.c new file mode 100644 index 0000000..b58fe92 --- /dev/null +++ b/gdb/testsuite/gdb.opt/inline-cmds.c @@ -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; +} diff --git a/gdb/testsuite/gdb.opt/inline-cmds.exp b/gdb/testsuite/gdb.opt/inline-cmds.exp new file mode 100644 index 0000000..43c87e5 --- /dev/null +++ b/gdb/testsuite/gdb.opt/inline-cmds.exp @@ -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" diff --git a/gdb/testsuite/gdb.opt/inline-locals.c b/gdb/testsuite/gdb.opt/inline-locals.c new file mode 100644 index 0000000..0b0ec62 --- /dev/null +++ b/gdb/testsuite/gdb.opt/inline-locals.c @@ -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; +} diff --git a/gdb/testsuite/gdb.opt/inline-locals.exp b/gdb/testsuite/gdb.opt/inline-locals.exp new file mode 100644 index 0000000..cdc180a --- /dev/null +++ b/gdb/testsuite/gdb.opt/inline-locals.exp @@ -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)" diff --git a/gdb/testsuite/gdb.opt/inline-markers.c b/gdb/testsuite/gdb.opt/inline-markers.c new file mode 100644 index 0000000..330e242 --- /dev/null +++ b/gdb/testsuite/gdb.opt/inline-markers.c @@ -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 */ +} diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp index b20d035..200ab35 100644 --- a/gdb/testsuite/lib/gdb.exp +++ b/gdb/testsuite/lib/gdb.exp @@ -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 diff --git a/gdb/valops.c b/gdb/valops.c index 1d19393..c0bc2a5 100644 --- a/gdb/valops.c +++ b/gdb/valops.c @@ -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))); |