3442 lines
120 KiB
Diff
3442 lines
120 KiB
Diff
http://sourceware.org/ml/gdb-patches/2008-07/msg00442.html
|
||
with pre-applied and post-unapplied (the patch is currently 2008-12-06 not
|
||
applied upstream)
|
||
http://sourceware.org/ml/gdb-patches/2008-07/msg00317.html
|
||
|
||
Removed dwarf_expr_frame_base NULL check duplicity with *-vla.patch.
|
||
|
||
Index: gdb-6.8.50.20081209/gdb/NEWS
|
||
===================================================================
|
||
--- gdb-6.8.50.20081209.orig/gdb/NEWS 2008-12-02 08:57:36.000000000 +0100
|
||
+++ gdb-6.8.50.20081209/gdb/NEWS 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -26,6 +26,9 @@ completions will be "f1" and "f2".
|
||
operators when expanding macros. It also supports variable-arity
|
||
macros.
|
||
|
||
+* Inlined functions are now supported. They show up in backtraces, and
|
||
+the "step", "next", and "finish" commands handle them automatically.
|
||
+
|
||
* New remote packets
|
||
|
||
qSearch:memory:
|
||
Index: gdb-6.8.50.20081209/gdb/block.c
|
||
===================================================================
|
||
--- gdb-6.8.50.20081209.orig/gdb/block.c 2008-09-05 13:37:16.000000000 +0200
|
||
+++ gdb-6.8.50.20081209/gdb/block.c 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -47,8 +47,16 @@ contained_in (const struct block *a, con
|
||
{
|
||
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, con
|
||
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
|
||
Index: gdb-6.8.50.20081209/gdb/block.h
|
||
===================================================================
|
||
--- gdb-6.8.50.20081209.orig/gdb/block.h 2008-09-05 13:37:16.000000000 +0200
|
||
+++ gdb-6.8.50.20081209/gdb/block.h 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -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 =
|
||
|
||
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 **);
|
||
Index: gdb-6.8.50.20081209/gdb/blockframe.c
|
||
===================================================================
|
||
--- gdb-6.8.50.20081209.orig/gdb/blockframe.c 2008-09-05 13:37:16.000000000 +0200
|
||
+++ gdb-6.8.50.20081209/gdb/blockframe.c 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -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 *blo
|
||
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);
|
||
Index: gdb-6.8.50.20081209/gdb/breakpoint.c
|
||
===================================================================
|
||
--- gdb-6.8.50.20081209.orig/gdb/breakpoint.c 2008-12-10 01:28:28.000000000 +0100
|
||
+++ gdb-6.8.50.20081209/gdb/breakpoint.c 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -2572,19 +2572,21 @@ 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 ();
|
||
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.
|
||
@@ -2596,10 +2598,9 @@ watchpoint_check (void *p)
|
||
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 ())
|
||
- && gdbarch_in_function_epilogue_p (current_gdbarch, read_pc ()))
|
||
+ if (gdbarch_in_function_epilogue_p (current_gdbarch, read_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. */
|
||
@@ -2833,7 +2834,7 @@ bpstat_check_breakpoint_conditions (bpst
|
||
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)
|
||
{
|
||
@@ -2848,8 +2849,12 @@ bpstat_check_breakpoint_conditions (bpst
|
||
|
||
if (bl->cond && bl->owner->disposition != disp_del_at_next_stop)
|
||
{
|
||
- /* 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),
|
||
@@ -4820,6 +4825,11 @@ set_momentary_breakpoint (struct symtab_
|
||
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;
|
||
@@ -5832,7 +5842,6 @@ watch_command_1 (char *arg, int accessfl
|
||
struct block *exp_valid_block;
|
||
struct value *val, *mark, *val_chain;
|
||
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;
|
||
@@ -5987,34 +5996,34 @@ watch_command_1 (char *arg, int accessfl
|
||
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_id (frame)))
|
||
+ {
|
||
+ scope_breakpoint
|
||
+ = create_internal_breakpoint (frame_pc_unwind (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_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_pc_unwind (frame);
|
||
+ scope_breakpoint->loc->address
|
||
+ = adjust_breakpoint_address (scope_breakpoint->loc->requested_address,
|
||
+ scope_breakpoint->type);
|
||
+ }
|
||
}
|
||
|
||
/* Now set up the breakpoint. */
|
||
@@ -6195,7 +6204,6 @@ until_break_command (char *arg, int from
|
||
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;
|
||
@@ -6228,20 +6236,22 @@ until_break_command (char *arg, int from
|
||
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_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_pc_unwind (frame), 0);
|
||
+ sal.pc = frame_pc_unwind (frame);
|
||
+ breakpoint2 = set_momentary_breakpoint (sal,
|
||
+ frame_unwind_id (frame),
|
||
bp_until);
|
||
make_cleanup_delete_breakpoint (breakpoint2);
|
||
}
|
||
Index: gdb-6.8.50.20081209/gdb/buildsym.c
|
||
===================================================================
|
||
--- gdb-6.8.50.20081209.orig/gdb/buildsym.c 2008-08-21 20:40:34.000000000 +0200
|
||
+++ gdb-6.8.50.20081209/gdb/buildsym.c 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -1159,6 +1159,12 @@ end_symtab (CORE_ADDR end_addr, struct o
|
||
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))
|
||
Index: gdb-6.8.50.20081209/gdb/doc/gdb.texinfo
|
||
===================================================================
|
||
--- gdb-6.8.50.20081209.orig/gdb/doc/gdb.texinfo 2008-12-10 01:28:28.000000000 +0100
|
||
+++ gdb-6.8.50.20081209/gdb/doc/gdb.texinfo 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -147,6 +147,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
|
||
@@ -1813,7 +1814,7 @@ To request debugging information, specif
|
||
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.
|
||
@@ -1822,22 +1823,7 @@ executables containing debugging informa
|
||
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
|
||
@@ -8271,6 +8257,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
|
||
|
||
Index: gdb-6.8.50.20081209/gdb/dwarf2loc.c
|
||
===================================================================
|
||
--- gdb-6.8.50.20081209.orig/gdb/dwarf2loc.c 2008-12-10 01:22:11.000000000 +0100
|
||
+++ gdb-6.8.50.20081209/gdb/dwarf2loc.c 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -31,6 +31,7 @@
|
||
#include "regcache.h"
|
||
#include "objfiles.h"
|
||
#include "exceptions.h"
|
||
+#include "block.h"
|
||
|
||
#include "elf/dwarf2.h"
|
||
#include "dwarf2expr.h"
|
||
@@ -150,7 +151,10 @@ dwarf_expr_frame_base (void *baton, gdb_
|
||
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,
|
||
Index: gdb-6.8.50.20081209/gdb/dwarf2read.c
|
||
===================================================================
|
||
--- gdb-6.8.50.20081209.orig/gdb/dwarf2read.c 2008-12-10 01:28:28.000000000 +0100
|
||
+++ gdb-6.8.50.20081209/gdb/dwarf2read.c 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -45,6 +45,7 @@
|
||
#include "hashtab.h"
|
||
#include "command.h"
|
||
#include "gdbcmd.h"
|
||
+#include "block.h"
|
||
#include "addrmap.h"
|
||
#include "top.h"
|
||
|
||
@@ -2787,12 +2788,8 @@ process_die (struct die_info *die, struc
|
||
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:
|
||
@@ -3055,10 +3052,25 @@ read_func_scope (struct die_info *die, s
|
||
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));
|
||
|
||
@@ -6942,6 +6954,9 @@ die_specification (struct die_info *die,
|
||
*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);
|
||
@@ -7625,6 +7640,7 @@ new_symbol (struct die_info *die, struct
|
||
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));
|
||
|
||
@@ -7654,13 +7670,17 @@ new_symbol (struct die_info *die, struct
|
||
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);
|
||
@@ -7707,6 +7727,14 @@ new_symbol (struct die_info *die, struct
|
||
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
|
||
@@ -7754,7 +7782,14 @@ new_symbol (struct die_info *die, struct
|
||
}
|
||
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)
|
||
{
|
||
Index: gdb-6.8.50.20081209/gdb/frame-unwind.c
|
||
===================================================================
|
||
--- gdb-6.8.50.20081209.orig/gdb/frame-unwind.c 2008-05-06 20:37:46.000000000 +0200
|
||
+++ gdb-6.8.50.20081209/gdb/frame-unwind.c 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -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 *obsta
|
||
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;
|
||
}
|
||
|
||
Index: gdb-6.8.50.20081209/gdb/frame.c
|
||
===================================================================
|
||
--- gdb-6.8.50.20081209.orig/gdb/frame.c 2008-12-10 01:27:33.000000000 +0100
|
||
+++ gdb-6.8.50.20081209/gdb/frame.c 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -41,8 +41,14 @@
|
||
#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);
|
||
+
|
||
+static void deprecated_update_frame_pc_hack (struct frame_info *frame,
|
||
+ CORE_ADDR pc);
|
||
|
||
/* We keep a cache of stack frames, each of which is a "struct
|
||
frame_info". The innermost one gets allocated (in
|
||
@@ -183,6 +189,8 @@ fprint_frame_id (struct ui_file *file, s
|
||
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, "}");
|
||
}
|
||
|
||
@@ -197,6 +205,12 @@ fprint_frame_type (struct ui_file *file,
|
||
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;
|
||
@@ -249,6 +263,18 @@ fprint_frame (struct ui_file *file, stru
|
||
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. */
|
||
|
||
@@ -281,13 +307,21 @@ 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_id (struct frame_info *next_frame)
|
||
{
|
||
/* Use prev_frame, 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);
|
||
+ return get_frame_id (skip_inlined_frames (get_prev_frame_1 (next_frame)));
|
||
}
|
||
|
||
const struct frame_id null_frame_id; /* All zeros. */
|
||
@@ -342,6 +376,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;
|
||
@@ -352,21 +395,22 @@ frame_id_eq (struct frame_id l, struct f
|
||
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=");
|
||
@@ -411,6 +455,29 @@ frame_id_inner (struct gdbarch *gdbarch,
|
||
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
|
||
@@ -463,8 +530,8 @@ frame_find_by_id (struct frame_id id)
|
||
return NULL;
|
||
}
|
||
|
||
-CORE_ADDR
|
||
-frame_pc_unwind (struct frame_info *this_frame)
|
||
+static CORE_ADDR
|
||
+frame_unwind_pc (struct frame_info *this_frame)
|
||
{
|
||
if (!this_frame->prev_pc.p)
|
||
{
|
||
@@ -503,6 +570,12 @@ frame_pc_unwind (struct frame_info *this
|
||
}
|
||
|
||
CORE_ADDR
|
||
+frame_pc_unwind (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;
|
||
@@ -1217,7 +1290,6 @@ frame_register_unwind_location (struct f
|
||
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;
|
||
|
||
@@ -1257,6 +1329,14 @@ get_prev_frame_1 (struct frame_info *thi
|
||
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. */
|
||
@@ -1324,7 +1404,8 @@ get_prev_frame_1 (struct frame_info *thi
|
||
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;
|
||
@@ -1353,6 +1434,17 @@ get_prev_frame_1 (struct frame_info *thi
|
||
}
|
||
}
|
||
|
||
+ 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
|
||
@@ -1511,7 +1603,7 @@ get_prev_frame (struct frame_info *this_
|
||
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
|
||
@@ -1556,8 +1648,9 @@ get_prev_frame (struct frame_info *this_
|
||
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");
|
||
@@ -1568,7 +1661,8 @@ get_prev_frame (struct frame_info *this_
|
||
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)
|
||
{
|
||
@@ -1593,7 +1687,7 @@ CORE_ADDR
|
||
get_frame_pc (struct frame_info *frame)
|
||
{
|
||
gdb_assert (frame->next != NULL);
|
||
- return frame_pc_unwind (frame->next);
|
||
+ return frame_unwind_pc (frame->next);
|
||
}
|
||
|
||
/* Return an address that falls within THIS_FRAME's code block. */
|
||
@@ -1638,17 +1732,58 @@ get_frame_address_in_block (struct 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
|
||
@@ -1658,15 +1793,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
|
||
@@ -1747,7 +1875,7 @@ get_frame_type (struct frame_info *frame
|
||
return frame->unwind->type;
|
||
}
|
||
|
||
-void
|
||
+static void
|
||
deprecated_update_frame_pc_hack (struct frame_info *frame, CORE_ADDR pc)
|
||
{
|
||
if (frame_debug)
|
||
Index: gdb-6.8.50.20081209/gdb/frame.h
|
||
===================================================================
|
||
--- gdb-6.8.50.20081209.orig/gdb/frame.h 2008-08-26 19:40:24.000000000 +0200
|
||
+++ gdb-6.8.50.20081209/gdb/frame.h 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -34,6 +34,9 @@
|
||
frame_unwind_WHAT...(): Unwind THIS frame's WHAT from the NEXT
|
||
frame.
|
||
|
||
+ 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)
|
||
@@ -101,6 +104,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. */
|
||
@@ -122,6 +129,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. */
|
||
@@ -157,6 +168,10 @@ extern struct frame_id frame_id_build_wi
|
||
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);
|
||
@@ -177,6 +192,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,
|
||
@@ -340,6 +358,7 @@ extern CORE_ADDR get_frame_base (struct
|
||
|
||
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_id (struct frame_info *next_frame);
|
||
|
||
/* Assuming that a frame is `normal', return its base-address, or 0 if
|
||
@@ -660,14 +679,6 @@ extern struct frame_info *deprecated_saf
|
||
|
||
extern struct frame_info *create_new_frame (CORE_ADDR base, CORE_ADDR pc);
|
||
|
||
-/* FIXME: cagney/2002-12-06: Has the PC in the current frame changed?
|
||
- "infrun.c", Thanks to gdbarch_decr_pc_after_break, can change the PC after
|
||
- the initial frame create. This puts things back in sync.
|
||
-
|
||
- This replaced: frame->pc = ....; */
|
||
-extern void deprecated_update_frame_pc_hack (struct frame_info *frame,
|
||
- CORE_ADDR pc);
|
||
-
|
||
/* FIXME: cagney/2002-12-18: Has the frame's base changed? Or to be
|
||
more exact, was that initial guess at the frame's base as returned
|
||
by the deleted read_fp() wrong? If it was, fix it. This shouldn't
|
||
Index: gdb-6.8.50.20081209/gdb/gdbthread.h
|
||
===================================================================
|
||
--- gdb-6.8.50.20081209.orig/gdb/gdbthread.h 2008-11-17 13:13:49.000000000 +0100
|
||
+++ gdb-6.8.50.20081209/gdb/gdbthread.h 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -83,6 +83,13 @@ 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;
|
||
+
|
||
+ /* The source file and line at the beginning of the current step
|
||
+ operation. Only valid when step_frame_id is set. */
|
||
int current_line;
|
||
struct symtab *current_symtab;
|
||
|
||
Index: gdb-6.8.50.20081209/gdb/infcall.c
|
||
===================================================================
|
||
--- gdb-6.8.50.20081209.orig/gdb/infcall.c 2008-12-04 22:41:00.000000000 +0100
|
||
+++ gdb-6.8.50.20081209/gdb/infcall.c 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -734,11 +734,8 @@ The program being debugged exited while
|
||
|
||
if (unwind_on_signal_p)
|
||
{
|
||
- /* The user wants the context restored. */
|
||
-
|
||
- /* We must get back to the frame we were before the
|
||
- dummy call. */
|
||
- frame_pop (get_current_frame ());
|
||
+ /* The user wants the context restored. Calling error will
|
||
+ run inf_status_cleanup, which does all the work. */
|
||
|
||
/* FIXME: Insert a bunch of wrap_here; name can be very
|
||
long if it's a C++ name with arguments and stuff. */
|
||
Index: gdb-6.8.50.20081209/gdb/infcmd.c
|
||
===================================================================
|
||
--- gdb-6.8.50.20081209.orig/gdb/infcmd.c 2008-12-05 23:38:10.000000000 +0100
|
||
+++ gdb-6.8.50.20081209/gdb/infcmd.c 2008-12-10 01:34:41.000000000 +0100
|
||
@@ -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: */
|
||
|
||
@@ -722,6 +723,17 @@ Can't resume all threads and specify pro
|
||
continue_1 (all_threads);
|
||
}
|
||
|
||
+/* Record the starting point of a "step" or "next" command. */
|
||
+
|
||
+static void
|
||
+set_step_frame (struct thread_info *tp)
|
||
+{
|
||
+ struct symtab_and_line sal;
|
||
+
|
||
+ find_frame_sal (get_current_frame (), &sal);
|
||
+ set_step_info (tp, get_current_frame (), sal);
|
||
+}
|
||
+
|
||
/* Step until outside of current statement. */
|
||
|
||
static void
|
||
@@ -799,73 +811,39 @@ step_1 (int skip_subroutines, int single
|
||
make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
|
||
}
|
||
|
||
- /* In synchronous case, all is well, just use the regular for loop. */
|
||
+ /* In synchronous case, all is well; each step_once call will step once. */
|
||
if (!target_can_async_p ())
|
||
{
|
||
for (; count > 0; count--)
|
||
{
|
||
struct thread_info *tp = inferior_thread ();
|
||
- clear_proceed_status ();
|
||
-
|
||
- frame = get_current_frame ();
|
||
- tp->step_frame_id = get_frame_id (frame);
|
||
|
||
- if (!single_inst)
|
||
- {
|
||
- CORE_ADDR pc;
|
||
-
|
||
- pc = get_frame_pc (frame);
|
||
- find_pc_line_pc_range (pc,
|
||
- &tp->step_range_start, &tp->step_range_end);
|
||
- if (tp->step_range_end == 0)
|
||
- {
|
||
- char *name;
|
||
- if (find_pc_partial_function (pc, &name,
|
||
- &tp->step_range_start,
|
||
- &tp->step_range_end) == 0)
|
||
- error (_("Cannot find bounds of current function"));
|
||
+ step_once (skip_subroutines, single_inst, count, thread);
|
||
|
||
- target_terminal_ours ();
|
||
- printf_filtered (_("\
|
||
-Single stepping until exit from function %s, \n\
|
||
-which has no line number information.\n"), name);
|
||
- }
|
||
- }
|
||
- else
|
||
+ if (!tp->step_multi || (!target_has_execution
|
||
+ || !inferior_thread ()->stop_step))
|
||
{
|
||
- /* Say we are stepping, but stop after one insn whatever it does. */
|
||
- tp->step_range_start = tp->step_range_end = 1;
|
||
- if (!skip_subroutines)
|
||
- /* It is stepi.
|
||
- Don't step over function calls, not even to functions lacking
|
||
- line numbers. */
|
||
- tp->step_over_calls = STEP_OVER_NONE;
|
||
+ /* If we stopped for some reason that is not stepping
|
||
+ there are no further steps to make. */
|
||
+ tp->step_multi = 0;
|
||
+ break;
|
||
}
|
||
-
|
||
- if (skip_subroutines)
|
||
- tp->step_over_calls = STEP_OVER_ALL;
|
||
-
|
||
- tp->step_multi = (count > 1);
|
||
- proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 1);
|
||
-
|
||
- if (!target_has_execution
|
||
- || !inferior_thread ()->stop_step)
|
||
- break;
|
||
}
|
||
|
||
do_cleanups (cleanups);
|
||
- return;
|
||
}
|
||
- /* In case of asynchronous target things get complicated, do only
|
||
- one step for now, before returning control to the event loop. Let
|
||
- the continuation figure out how many other steps we need to do,
|
||
- and handle them one at the time, through step_once(). */
|
||
else
|
||
{
|
||
+ /* In the case of an asynchronous target things get complicated;
|
||
+ do only one step for now, before returning control to the
|
||
+ event loop. Let the continuation figure out how many other
|
||
+ steps we need to do, and handle them one at the time, through
|
||
+ step_once. */
|
||
step_once (skip_subroutines, single_inst, count, thread);
|
||
+
|
||
/* We are running, and the continuation is installed. It will
|
||
disable the longjmp breakpoint as appropriate. */
|
||
- discard_cleanups (cleanups);
|
||
+ discard_cleanups (cleanups);
|
||
}
|
||
}
|
||
|
||
@@ -919,7 +897,6 @@ static void
|
||
step_once (int skip_subroutines, int single_inst, int count, int thread)
|
||
{
|
||
struct frame_info *frame;
|
||
- struct step_1_continuation_args *args;
|
||
|
||
if (count > 0)
|
||
{
|
||
@@ -929,6 +906,20 @@ step_once (int skip_subroutines, int sin
|
||
THREAD is set. */
|
||
struct thread_info *tp = inferior_thread ();
|
||
clear_proceed_status ();
|
||
+ set_step_frame (tp);
|
||
+
|
||
+ /* 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;
|
||
+ }
|
||
|
||
frame = get_current_frame ();
|
||
tp->step_frame_id = get_frame_id (frame);
|
||
@@ -977,12 +968,21 @@ which has no line number information.\n"
|
||
tp->step_multi = (count > 1);
|
||
proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 1);
|
||
|
||
- args = xmalloc (sizeof (*args));
|
||
- args->skip_subroutines = skip_subroutines;
|
||
- args->single_inst = single_inst;
|
||
- args->count = count;
|
||
- args->thread = thread;
|
||
- add_intermediate_continuation (tp, step_1_continuation, args, xfree);
|
||
+ /* For async targets, register a continuation to do any
|
||
+ additional steps. For sync targets, the caller will handle
|
||
+ further stepping. */
|
||
+ if (target_can_async_p ())
|
||
+ {
|
||
+ struct step_1_continuation_args *args;
|
||
+
|
||
+ args = xmalloc (sizeof (*args));
|
||
+ args->skip_subroutines = skip_subroutines;
|
||
+ args->single_inst = single_inst;
|
||
+ args->count = count;
|
||
+ args->thread = thread;
|
||
+
|
||
+ add_intermediate_continuation (tp, step_1_continuation, args, xfree);
|
||
+ }
|
||
}
|
||
}
|
||
|
||
@@ -1167,15 +1167,13 @@ signal_command (char *signum_exp, int fr
|
||
static void
|
||
until_next_command (int from_tty)
|
||
{
|
||
- struct frame_info *frame;
|
||
CORE_ADDR pc;
|
||
struct symbol *func;
|
||
struct symtab_and_line sal;
|
||
struct thread_info *tp = inferior_thread ();
|
||
|
||
clear_proceed_status ();
|
||
-
|
||
- frame = get_current_frame ();
|
||
+ set_step_frame (tp);
|
||
|
||
/* Step until either exited from this function or greater
|
||
than the current line (if in symbolic section) or pc (if
|
||
@@ -1203,7 +1201,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 */
|
||
|
||
@@ -1534,6 +1531,37 @@ 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)
|
||
+ {
|
||
+ struct thread_info *tp = inferior_thread ();
|
||
+
|
||
+ /* 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 symtab_and_line empty_sal;
|
||
+ init_sal (&empty_sal);
|
||
+ set_step_info (tp, 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)));
|
||
Index: gdb-6.8.50.20081209/gdb/inferior.h
|
||
===================================================================
|
||
--- gdb-6.8.50.20081209.orig/gdb/inferior.h 2008-12-10 01:22:23.000000000 +0100
|
||
+++ gdb-6.8.50.20081209/gdb/inferior.h 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -249,6 +249,9 @@ 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 thread_info *tp, struct frame_info *frame,
|
||
+ struct symtab_and_line sal);
|
||
+
|
||
/* From infcmd.c */
|
||
|
||
extern void tty_command (char *, int);
|
||
Index: gdb-6.8.50.20081209/gdb/infrun.c
|
||
===================================================================
|
||
--- gdb-6.8.50.20081209.orig/gdb/infrun.c 2008-12-10 01:27:34.000000000 +0100
|
||
+++ gdb-6.8.50.20081209/gdb/infrun.c 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -45,6 +45,7 @@
|
||
#include "language.h"
|
||
#include "solib.h"
|
||
#include "main.h"
|
||
+#include "inline-frame.h"
|
||
|
||
#include "gdb_assert.h"
|
||
#include "mi/mi-common.h"
|
||
@@ -206,7 +207,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. */
|
||
|
||
@@ -1147,6 +1148,8 @@ a command like `return' or `jump' to con
|
||
step = 0;
|
||
}
|
||
|
||
+ clear_inline_frame_state (resume_ptid);
|
||
+
|
||
if (debug_displaced
|
||
&& use_displaced_stepping (gdbarch)
|
||
&& tp->trap_expected)
|
||
@@ -1188,6 +1191,7 @@ clear_proceed_status_thread (struct thre
|
||
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;
|
||
|
||
@@ -1532,6 +1536,9 @@ init_wait_for_inferior (void)
|
||
init_infwait_state ();
|
||
|
||
displaced_step_clear ();
|
||
+
|
||
+ /* Discard any skipped inlined frames. */
|
||
+ clear_inline_frame_state (minus_one_ptid);
|
||
}
|
||
|
||
|
||
@@ -1587,7 +1594,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);
|
||
|
||
@@ -1903,10 +1910,21 @@ 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 thread_info *tp, struct frame_info *frame,
|
||
+ struct symtab_and_line sal)
|
||
+{
|
||
+ 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;
|
||
@@ -1917,16 +1935,10 @@ init_execution_control_state (struct exe
|
||
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
|
||
@@ -2096,6 +2108,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. */
|
||
@@ -2781,6 +2809,12 @@ targets should add new threads to the th
|
||
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 (current_gdbarch)
|
||
@@ -3013,8 +3047,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 (get_current_frame ()),
|
||
- ecs->event_thread->step_frame_id)
|
||
+ && frame_id_eq (get_stack_frame_id (get_current_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
|
||
@@ -3400,10 +3434,10 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (
|
||
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 (get_current_frame ()),
|
||
- ecs->event_thread->step_frame_id)
|
||
+ if (!frame_id_eq (get_stack_frame_id (get_current_frame ()),
|
||
+ ecs->event_thread->step_stack_frame_id)
|
||
&& (frame_id_eq (frame_unwind_id (get_current_frame ()),
|
||
- ecs->event_thread->step_frame_id)
|
||
+ ecs->event_thread->step_stack_frame_id)
|
||
|| execution_direction == EXEC_REVERSE))
|
||
{
|
||
CORE_ADDR real_stop_pc;
|
||
@@ -3646,6 +3680,82 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (
|
||
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))
|
||
@@ -3671,9 +3781,7 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (
|
||
|
||
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 (get_current_frame ());
|
||
- ecs->event_thread->current_line = stop_pc_sal.line;
|
||
- ecs->event_thread->current_symtab = stop_pc_sal.symtab;
|
||
+ set_step_info (ecs->event_thread, get_current_frame (), stop_pc_sal);
|
||
|
||
if (debug_infrun)
|
||
fprintf_unfiltered (gdb_stdlog, "infrun: keep going\n");
|
||
@@ -4197,17 +4305,6 @@ normal_stop (void)
|
||
previous_inferior_ptid = inferior_ptid;
|
||
}
|
||
|
||
- /* NOTE drow/2004-01-17: Is this still necessary? */
|
||
- /* Make sure that the current_frame's pc is correct. This
|
||
- is a correction for setting up the frame info before doing
|
||
- gdbarch_decr_pc_after_break */
|
||
- if (target_has_execution)
|
||
- /* FIXME: cagney/2002-12-06: Has the PC changed? Thanks to
|
||
- gdbarch_decr_pc_after_break, the program counter can change. Ask the
|
||
- frame code to check for this and sort out any resultant mess.
|
||
- gdbarch_decr_pc_after_break needs to just go away. */
|
||
- deprecated_update_frame_pc_hack (get_current_frame (), read_pc ());
|
||
-
|
||
if (!breakpoints_always_inserted_mode () && target_has_execution)
|
||
{
|
||
if (remove_breakpoints ())
|
||
@@ -4776,6 +4873,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;
|
||
@@ -4814,6 +4912,7 @@ save_inferior_status (int restore_stack_
|
||
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;
|
||
@@ -4869,6 +4968,7 @@ restore_inferior_status (struct inferior
|
||
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;
|
||
Index: gdb-6.8.50.20081209/gdb/inline-frame.c
|
||
===================================================================
|
||
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
||
+++ gdb-6.8.50.20081209/gdb/inline-frame.c 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -0,0 +1,382 @@
|
||
+/* 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. If PTID is minus_one_ptid, forget about all
|
||
+ hidden inlined functions. */
|
||
+
|
||
+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;
|
||
+ }
|
||
+
|
||
+ 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;
|
||
+}
|
||
Index: gdb-6.8.50.20081209/gdb/inline-frame.h
|
||
===================================================================
|
||
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
||
+++ gdb-6.8.50.20081209/gdb/inline-frame.h 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -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) */
|
||
Index: gdb-6.8.50.20081209/gdb/minsyms.c
|
||
===================================================================
|
||
--- gdb-6.8.50.20081209.orig/gdb/minsyms.c 2008-12-10 01:27:33.000000000 +0100
|
||
+++ gdb-6.8.50.20081209/gdb/minsyms.c 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -767,7 +767,7 @@ prim_record_minimal_symbol_and_info (con
|
||
|
||
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;
|
||
Index: gdb-6.8.50.20081209/gdb/s390-tdep.c
|
||
===================================================================
|
||
--- gdb-6.8.50.20081209.orig/gdb/s390-tdep.c 2008-09-11 16:23:15.000000000 +0200
|
||
+++ gdb-6.8.50.20081209/gdb/s390-tdep.c 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -1182,6 +1182,7 @@ s390_prologue_frame_unwind_cache (struct
|
||
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
|
||
/* 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
|
||
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
|
||
Index: gdb-6.8.50.20081209/gdb/stack.c
|
||
===================================================================
|
||
--- gdb-6.8.50.20081209.orig/gdb/stack.c 2008-12-10 01:22:11.000000000 +0100
|
||
+++ gdb-6.8.50.20081209/gdb/stack.c 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -45,6 +45,7 @@
|
||
#include "valprint.h"
|
||
#include "gdbthread.h"
|
||
#include "cp-support.h"
|
||
+#include "inline-frame.h"
|
||
|
||
#include "gdb_assert.h"
|
||
#include <ctype.h>
|
||
@@ -98,6 +99,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
|
||
@@ -537,7 +562,7 @@ print_frame_info (struct frame_info *fra
|
||
{
|
||
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,
|
||
@@ -594,7 +619,7 @@ print_frame (struct frame_info *frame, i
|
||
stb = ui_out_stream_new (uiout);
|
||
old_chain = make_cleanup_ui_out_stream_delete (stb);
|
||
|
||
- 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
|
||
@@ -615,8 +640,13 @@ print_frame (struct frame_info *frame, i
|
||
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)
|
||
@@ -673,7 +703,7 @@ print_frame (struct frame_info *frame, i
|
||
}
|
||
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 ();
|
||
@@ -853,8 +883,16 @@ parse_frame_specification_1 (const char
|
||
{
|
||
if (frame_id_eq (id, get_frame_id (fid)))
|
||
{
|
||
- while (frame_id_eq (id, frame_unwind_id (fid)))
|
||
- fid = get_prev_frame (fid);
|
||
+ struct frame_info *prev_frame;
|
||
+
|
||
+ while (1)
|
||
+ {
|
||
+ prev_frame = get_prev_frame (fid);
|
||
+ if (!prev_frame
|
||
+ || !frame_id_eq (id, get_frame_id (prev_frame)))
|
||
+ break;
|
||
+ fid = prev_frame;
|
||
+ }
|
||
return fid;
|
||
}
|
||
}
|
||
@@ -988,8 +1026,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)),
|
||
@@ -1453,7 +1493,9 @@ print_frame_local_vars (struct frame_inf
|
||
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);
|
||
@@ -1524,7 +1566,9 @@ print_frame_label_vars (struct frame_inf
|
||
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);
|
||
@@ -1795,6 +1839,9 @@ return_command (char *retval_exp, int fr
|
||
thisframe = get_selected_frame ("No selected frame.");
|
||
thisfun = get_frame_function (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
|
||
Index: gdb-6.8.50.20081209/gdb/symtab.c
|
||
===================================================================
|
||
--- gdb-6.8.50.20081209.orig/gdb/symtab.c 2008-12-10 01:27:33.000000000 +0100
|
||
+++ gdb-6.8.50.20081209/gdb/symtab.c 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -1373,10 +1373,13 @@ lookup_symbol_aux_local (const char *nam
|
||
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;
|
||
}
|
||
@@ -2609,6 +2612,7 @@ find_function_start_sal (struct symbol *
|
||
|
||
CORE_ADDR pc;
|
||
struct symtab_and_line sal;
|
||
+ struct block *b, *function_block;
|
||
|
||
pc = BLOCK_START (block);
|
||
fixup_symbol_section (sym, objfile);
|
||
@@ -2647,6 +2651,25 @@ find_function_start_sal (struct symbol *
|
||
|
||
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;
|
||
}
|
||
|
||
@@ -3669,6 +3692,24 @@ add_macro_name (const char *name, const
|
||
datum->text, datum->word);
|
||
}
|
||
|
||
+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);
|
||
+ }
|
||
+}
|
||
+
|
||
char **
|
||
default_make_symbol_completion_list (char *text, char *word)
|
||
{
|
||
@@ -3681,9 +3722,9 @@ default_make_symbol_completion_list (cha
|
||
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;
|
||
@@ -3793,41 +3834,43 @@ default_make_symbol_completion_list (cha
|
||
}
|
||
|
||
/* 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. */
|
||
@@ -3846,9 +3889,6 @@ default_make_symbol_completion_list (cha
|
||
{
|
||
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);
|
||
@@ -4305,6 +4345,25 @@ skip_prologue_using_sal (CORE_ADDR func_
|
||
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
|
||
Index: gdb-6.8.50.20081209/gdb/symtab.h
|
||
===================================================================
|
||
--- gdb-6.8.50.20081209.orig/gdb/symtab.h 2008-10-01 19:25:22.000000000 +0200
|
||
+++ gdb-6.8.50.20081209/gdb/symtab.h 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -556,9 +556,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;
|
||
|
||
@@ -589,6 +598,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
|
||
Index: gdb-6.8.50.20081209/gdb/testsuite/gdb.base/break.exp
|
||
===================================================================
|
||
--- gdb-6.8.50.20081209.orig/gdb/testsuite/gdb.base/break.exp 2008-08-06 14:52:07.000000000 +0200
|
||
+++ gdb-6.8.50.20081209/gdb/testsuite/gdb.base/break.exp 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -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"
|
||
}
|
||
Index: gdb-6.8.50.20081209/gdb/testsuite/gdb.cp/annota2.exp
|
||
===================================================================
|
||
--- gdb-6.8.50.20081209.orig/gdb/testsuite/gdb.cp/annota2.exp 2008-05-04 21:39:00.000000000 +0200
|
||
+++ gdb-6.8.50.20081209/gdb/testsuite/gdb.cp/annota2.exp 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -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\n)*a.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)" }
|
||
Index: gdb-6.8.50.20081209/gdb/testsuite/gdb.opt/inline-bt.c
|
||
===================================================================
|
||
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
||
+++ gdb-6.8.50.20081209/gdb/testsuite/gdb.opt/inline-bt.c 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -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;
|
||
+}
|
||
Index: gdb-6.8.50.20081209/gdb/testsuite/gdb.opt/inline-bt.exp
|
||
===================================================================
|
||
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
||
+++ gdb-6.8.50.20081209/gdb/testsuite/gdb.opt/inline-bt.exp 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -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" ${fullsrcfile2}]
|
||
+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)"
|
||
Index: gdb-6.8.50.20081209/gdb/testsuite/gdb.opt/inline-cmds.c
|
||
===================================================================
|
||
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
||
+++ gdb-6.8.50.20081209/gdb/testsuite/gdb.opt/inline-cmds.c 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -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;
|
||
+}
|
||
Index: gdb-6.8.50.20081209/gdb/testsuite/gdb.opt/inline-cmds.exp
|
||
===================================================================
|
||
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
||
+++ gdb-6.8.50.20081209/gdb/testsuite/gdb.opt/inline-cmds.exp 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -0,0 +1,279 @@
|
||
+# 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" ${fullsrcfile2}]
|
||
+gdb_breakpoint $srcfile2:$line1
|
||
+set line2 [gdb_get_line_number "set breakpoint 2 here" ${fullsrcfile2}]
|
||
+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 msg "next past inlined func1"
|
||
+gdb_test_multiple "list" $msg {
|
||
+ -re "($first|$opt).*$gdb_prompt $" {
|
||
+ 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 are past the call and back at the store to
|
||
+ # result - that's bad, we should have stepped out of func1 and
|
||
+ # kept stepping until the line changed.
|
||
+ 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
|
||
+ }
|
||
+ } else {
|
||
+ set x_test 1
|
||
+ }
|
||
+
|
||
+ # func1 should not show up on backtraces yet.
|
||
+ if { $bt_test != 1 } {
|
||
+ set bt_test [gdb_test "backtrace" "#0 \[^#]*main.*" ""]
|
||
+ }
|
||
+
|
||
+ 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"
|
||
+}
|
||
+
|
||
+# 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"
|
||
Index: gdb-6.8.50.20081209/gdb/testsuite/gdb.opt/inline-locals.c
|
||
===================================================================
|
||
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
||
+++ gdb-6.8.50.20081209/gdb/testsuite/gdb.opt/inline-locals.c 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -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;
|
||
+}
|
||
Index: gdb-6.8.50.20081209/gdb/testsuite/gdb.opt/inline-locals.exp
|
||
===================================================================
|
||
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
||
+++ gdb-6.8.50.20081209/gdb/testsuite/gdb.opt/inline-locals.exp 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -0,0 +1,118 @@
|
||
+# 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" ${fullsrcfile2}]
|
||
+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\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\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)"
|
||
Index: gdb-6.8.50.20081209/gdb/testsuite/gdb.opt/inline-markers.c
|
||
===================================================================
|
||
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
||
+++ gdb-6.8.50.20081209/gdb/testsuite/gdb.opt/inline-markers.c 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -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 */
|
||
+}
|
||
Index: gdb-6.8.50.20081209/gdb/testsuite/lib/gdb.exp
|
||
===================================================================
|
||
--- gdb-6.8.50.20081209.orig/gdb/testsuite/lib/gdb.exp 2008-12-10 01:28:27.000000000 +0100
|
||
+++ gdb-6.8.50.20081209/gdb/testsuite/lib/gdb.exp 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -1471,6 +1471,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
|
||
Index: gdb-6.8.50.20081209/gdb/valops.c
|
||
===================================================================
|
||
--- gdb-6.8.50.20081209.orig/gdb/valops.c 2008-12-10 01:25:43.000000000 +0100
|
||
+++ gdb-6.8.50.20081209/gdb/valops.c 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -1051,7 +1051,7 @@ value_of_variable (struct symbol *var, s
|
||
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)));
|
||
Index: gdb-6.8.50.20081209/gdb/Makefile.in
|
||
===================================================================
|
||
--- gdb-6.8.50.20081209.orig/gdb/Makefile.in 2008-12-10 01:28:27.000000000 +0100
|
||
+++ gdb-6.8.50.20081209/gdb/Makefile.in 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -630,6 +630,7 @@ SFILES = ada-exp.y ada-lang.c ada-typepr
|
||
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 \
|
||
@@ -800,6 +801,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $
|
||
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 \
|
||
Index: gdb-6.8.50.20081209/gdb/testsuite/gdb.opt/Makefile.in
|
||
===================================================================
|
||
--- gdb-6.8.50.20081209.orig/gdb/testsuite/gdb.opt/Makefile.in 2008-04-18 01:06:54.000000000 +0200
|
||
+++ gdb-6.8.50.20081209/gdb/testsuite/gdb.opt/Makefile.in 2008-12-10 01:28:37.000000000 +0100
|
||
@@ -1,7 +1,7 @@
|
||
VPATH = @srcdir@
|
||
srcdir = @srcdir@
|
||
|
||
-EXECUTABLES = hello/hello
|
||
+EXECUTABLES = clobbered-registers-O2 inline-bt inline-cmds inline-locals
|
||
|
||
MISCELLANEOUS =
|
||
|