* [FYI] Inlining support, rough patch
@ 2008-06-13 19:39 Daniel Jacobowitz
2008-06-13 19:43 ` Daniel Jacobowitz
` (2 more replies)
0 siblings, 3 replies; 38+ messages in thread
From: Daniel Jacobowitz @ 2008-06-13 19:39 UTC (permalink / raw)
To: gdb-patches
I've been talking about this patch for a long time. Jan needed
something similar, and I had recently merged HEAD into our internal
sources. So rather make him port the Apple patch from their 6.3
GDB, I promised to separate this out for him.
This is only lightly tested. The fully patched tree is very
thoroughly tested, of course, but I had to break this out of the much
larger diff.
There is a chapter in the patched gdb.texinfo discussing optimized
code debugging, including inlined functions. Basically, they work
in the most obvious ways I could think of.
When you set a breakpoint at a PC or interrupt the program with
Control-C, the current line of code is shown. Backtrace will list
any functions inlined at this point. But when you step onto
an inlined function call, the call site is shown; you have to "step"
again to enter it. You can "next" over it, or "finish" out of it.
Finish will not show the return value. There's no way to find it,
and on top of that problem it's hard to be sure you've finished the
inlined function, not just returned to another scheduled line of code
from the caller.
Breakpoints by name on inlined functions are not supported, but
breakpoints by file and line work. Breakpoints at the call site of an
inlined function may or may not work - sometimes they are moved to
after the call. That's quite a challenging problem in its own right
so I declined to address it at the same time.
The patch is not quite ready for inclusion. It has some bits,
like the get_current_frame / get_current_user_frame distinction, that
I am unhappy with. But if someone else wants to work on them... I'm
not sure when I'll have time to finish the merge.
Oh, you need GCC 4.x. This improves support for inlined functions in
GCC 3.4.x slightly, but not very much; you can see their args/locals,
but they don't appear on backtraces without DW_AT_call_line support
from the compiler.
Enjoy.
--
Daniel Jacobowitz
CodeSourcery
Index: Makefile.in
===================================================================
RCS file: /scratch/gcc/repos/src/src/gdb/Makefile.in,v
retrieving revision 1.1027
diff -u -p -r1.1027 Makefile.in
--- Makefile.in 10 Jun 2008 10:23:53 -0000 1.1027
+++ Makefile.in 13 Jun 2008 14:57:59 -0000
@@ -615,6 +615,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 \
@@ -1083,6 +1084,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 \
@@ -2096,13 +2098,13 @@ dwarf2-frame.o: dwarf2-frame.c $(defs_h)
dwarf2loc.o: dwarf2loc.c $(defs_h) $(ui_out_h) $(value_h) $(frame_h) \
$(gdbcore_h) $(target_h) $(inferior_h) $(ax_h) $(ax_gdb_h) \
$(regcache_h) $(objfiles_h) $(exceptions_h) $(elf_dwarf2_h) \
- $(dwarf2expr_h) $(dwarf2loc_h) $(gdb_string_h) $(gdb_assert_h)
+ $(dwarf2expr_h) $(dwarf2loc_h) $(gdb_string_h) $(gdb_assert_h) $(block_h)
dwarf2read.o: dwarf2read.c $(defs_h) $(bfd_h) $(symtab_h) $(gdbtypes_h) \
$(objfiles_h) $(elf_dwarf2_h) $(buildsym_h) $(demangle_h) \
$(expression_h) $(filenames_h) $(macrotab_h) $(language_h) \
$(complaints_h) $(bcache_h) $(dwarf2expr_h) $(dwarf2loc_h) \
$(cp_support_h) $(hashtab_h) $(command_h) $(gdbcmd_h) \
- $(addrmap_h) $(gdb_string_h) $(gdb_assert_h)
+ $(block_h) $(addrmap_h) $(gdb_string_h) $(gdb_assert_h)
elfread.o: elfread.c $(defs_h) $(bfd_h) $(gdb_string_h) $(elf_bfd_h) \
$(elf_mips_h) $(symtab_h) $(symfile_h) $(objfiles_h) $(buildsym_h) \
$(stabsread_h) $(gdb_stabs_h) $(complaints_h) $(demangle_h) \
@@ -2153,7 +2155,8 @@ frame.o: frame.c $(defs_h) $(frame_h) $(
$(regcache_h) $(gdb_assert_h) $(gdb_string_h) $(user_regs_h) \
$(gdb_obstack_h) $(dummy_frame_h) $(sentinel_frame_h) $(gdbcore_h) \
$(annotate_h) $(language_h) $(frame_unwind_h) $(frame_base_h) \
- $(command_h) $(gdbcmd_h) $(observer_h) $(objfiles_h) $(exceptions_h)
+ $(command_h) $(gdbcmd_h) $(observer_h) $(objfiles_h) $(exceptions_h) \
+ $(block_h)
frame-unwind.o: frame-unwind.c $(defs_h) $(frame_h) $(frame_unwind_h) \
$(gdb_assert_h) $(dummy_frame_h) $(gdb_obstack_h) $(value_h) \
$(regcache_h)
@@ -2350,6 +2353,8 @@ infrun.o: infrun.c $(defs_h) $(gdb_strin
inf-ttrace.o: inf-ttrace.c $(defs_h) $(command_h) $(gdbcore_h) \
$(gdbthread_h) $(inferior_h) $(target_h) \
$(gdb_assert_h) $(gdb_string_h) $(inf_child_h) $(inf_ttrace_h)
+inline-frame.o: inline-frame.c $(defs_h) $(frame_unwind_h) $(block_h) \
+ $(symtab_h) $(addrmap_h) $(gdb_assert_h)
interps.o: interps.c $(defs_h) $(gdbcmd_h) $(ui_out_h) $(event_loop_h) \
$(event_top_h) $(interps_h) $(completer_h) $(gdb_string_h) \
$(gdb_events_h) $(gdb_assert_h) $(top_h) $(exceptions_h)
Index: ada-lang.c
===================================================================
RCS file: /scratch/gcc/repos/src/src/gdb/ada-lang.c,v
retrieving revision 1.149
diff -u -p -r1.149 ada-lang.c
--- ada-lang.c 6 Jun 2008 06:48:36 -0000 1.149
+++ ada-lang.c 13 Jun 2008 14:57:59 -0000
@@ -4625,7 +4625,7 @@ remove_irrelevant_renamings (struct ada_
if (current_block == NULL)
return nsyms;
- current_function = block_function (current_block);
+ current_function = block_linkage_function (current_block);
if (current_function == NULL)
return nsyms;
@@ -6721,7 +6721,7 @@ ada_find_renaming_symbol (const char *na
static struct symbol *
find_old_style_renaming_symbol (const char *name, struct block *block)
{
- const struct symbol *function_sym = block_function (block);
+ const struct symbol *function_sym = block_linkage_function (block);
char *rename;
if (function_sym != NULL)
@@ -9895,7 +9895,7 @@ ada_unhandled_exception_name_addr_from_r
the frame corresponding to RAISE_SYM_NAME. This frame is
at least 3 levels up, so we simply skip the first 3 frames
without checking the name of their associated function. */
- fi = get_current_frame ();
+ fi = get_current_user_frame ();
for (frame_level = 0; frame_level < 3; frame_level += 1)
if (fi != NULL)
fi = get_prev_frame (fi);
@@ -9990,7 +9990,7 @@ print_it_exception (enum exception_catch
exception_name [sizeof (exception_name) - 1] = '\0';
}
- ada_find_printable_frame (get_current_frame ());
+ ada_find_printable_frame (get_current_user_frame ());
annotate_catchpoint (b->number);
switch (ex)
Index: block.c
===================================================================
RCS file: /scratch/gcc/repos/src/src/gdb/block.c,v
retrieving revision 1.15
diff -u -p -r1.15 block.c
--- block.c 1 Jan 2008 22:53:09 -0000 1.15
+++ block.c 13 Jun 2008 14:57:59 -0000
@@ -47,23 +47,40 @@ 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;
+}
/* Return the symbol for the function which contains a specified
- lexical block, described by a struct block BL. */
+ lexical block, described by a struct block BL. Inlined functions
+ are never returned. */
struct symbol *
-block_function (const struct block *bl)
+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 BLOCK represents an inlined function. */
+
+int
+block_inlined_p (const struct block *block)
+{
+ return BLOCK_INLINED (block);
+}
+
/* 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
@@ -304,6 +321,7 @@ allocate_block (struct obstack *obstack)
BLOCK_SUPERBLOCK (bl) = NULL;
BLOCK_DICT (bl) = NULL;
BLOCK_NAMESPACE (bl) = NULL;
+ BLOCK_INLINED (bl) = 0;
return bl;
}
Index: block.h
===================================================================
RCS file: /scratch/gcc/repos/src/src/gdb/block.h,v
retrieving revision 1.16
diff -u -p -r1.16 block.h
--- block.h 1 Jan 2008 22:53:09 -0000 1.16
+++ block.h 13 Jun 2008 14:57:59 -0000
@@ -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;
@@ -96,6 +96,9 @@ struct block
cplus_specific;
}
language_specific;
+
+ /* Set if this block corresponds to an inlined function. */
+ unsigned char inlined;
};
#define BLOCK_START(bl) (bl)->startaddr
@@ -104,6 +107,7 @@ struct block
#define BLOCK_SUPERBLOCK(bl) (bl)->superblock
#define BLOCK_DICT(bl) (bl)->dict
#define BLOCK_NAMESPACE(bl) (bl)->language_specific.cplus_specific.namespace
+#define BLOCK_INLINED(bl) (bl)->inlined
/* Macro to loop through all symbols in a block BL, in no particular
order. ITER helps keep track of the iteration, and should be a
@@ -132,7 +136,9 @@ struct blockvector
enum { GLOBAL_BLOCK = 0, STATIC_BLOCK = 1, FIRST_LOCAL_BLOCK = 2 };
-extern struct symbol *block_function (const struct 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 *);
Index: blockframe.c
===================================================================
RCS file: /scratch/gcc/repos/src/src/gdb/blockframe.c,v
retrieving revision 1.115
diff -u -p -r1.115 blockframe.c
--- blockframe.c 1 Jan 2008 22:53:09 -0000 1.115
+++ blockframe.c 13 Jun 2008 14:57:59 -0000
@@ -61,11 +61,40 @@ 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, *cur_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;
+
+ next_frame = get_next_frame (frame);
+ if (next_frame == NULL)
+ inline_count = inline_skipped_frames ();
+ else
+ {
+ inline_count = 0;
+ while (next_frame != NULL && get_frame_type (next_frame) == INLINE_FRAME)
+ {
+ inline_count++;
+ next_frame = get_next_frame (next_frame);
+ }
+ }
+
+ while (inline_count > 0)
+ {
+ if (block_inlined_p (bl))
+ inline_count--;
+
+ bl = BLOCK_SUPERBLOCK (bl);
+ gdb_assert (bl != NULL);
+ }
+
+ return bl;
}
CORE_ADDR
@@ -77,7 +106,7 @@ get_pc_function_start (CORE_ADDR pc)
bl = block_for_pc (pc);
if (bl)
{
- struct symbol *symbol = block_function (bl);
+ struct symbol *symbol = block_linkage_function (bl);
if (symbol)
{
@@ -104,9 +133,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_function (bl);
+
+ if (bl == NULL)
+ return NULL;
+
+ while (BLOCK_FUNCTION (bl) == NULL && BLOCK_SUPERBLOCK (bl) != NULL)
+ bl = BLOCK_SUPERBLOCK (bl);
+
+ return BLOCK_FUNCTION (bl);
}
\f
@@ -119,7 +153,7 @@ find_pc_sect_function (CORE_ADDR pc, str
struct block *b = block_for_pc_sect (pc, section);
if (b == 0)
return 0;
- return block_function (b);
+ return block_linkage_function (b);
}
/* Return the function containing pc value PC.
@@ -356,11 +390,11 @@ block_innermost_frame (struct block *blo
start = BLOCK_START (block);
end = BLOCK_END (block);
- frame = get_current_frame ();
+ frame = get_current_user_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: breakpoint.c
===================================================================
RCS file: /scratch/gcc/repos/src/src/gdb/breakpoint.c,v
retrieving revision 1.325
diff -u -p -r1.325 breakpoint.c
--- breakpoint.c 10 Jun 2008 09:29:15 -0000 1.325
+++ breakpoint.c 13 Jun 2008 14:57:59 -0000
@@ -2641,18 +2641,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_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.
@@ -2664,10 +2667,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. */
@@ -2937,7 +2939,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_stack_eq (b->frame_id, get_frame_id (get_current_frame ())))
bs->stop = 0;
else if (bs->stop)
{
@@ -2952,9 +2954,13 @@ 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. */
- select_frame (get_current_frame ());
+ /* 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_user_frame ());
value_is_zero
= catch_errors (breakpoint_cond_eval, (bl->cond),
"Error in testing breakpoint condition:\n",
@@ -5681,7 +5687,7 @@ resolve_sal_pc (struct symtab_and_line *
bv = blockvector_for_pc_sect (sal->pc, 0, &b, sal->symtab);
if (bv != NULL)
{
- sym = block_function (b);
+ sym = block_linkage_function (b);
if (sym != NULL)
{
fixup_symbol_section (sym, sal->symtab->objfile);
@@ -5813,7 +5819,6 @@ watch_command_1 (char *arg, int accessfl
struct block *exp_valid_block;
struct value *val, *mark;
struct frame_info *frame;
- struct frame_info *prev_frame = NULL;
char *exp_start = NULL;
char *exp_end = NULL;
char *tok, *id_tok_start, *end_tok;
@@ -5947,34 +5952,36 @@ 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);
+ while (get_frame_type (frame) == INLINE_FRAME)
+ frame = get_prev_frame (frame);
- scope_breakpoint->enable_state = bp_enabled;
+ 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;
- /* 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. */
@@ -6147,7 +6154,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;
@@ -6192,11 +6198,15 @@ until_break_command (char *arg, int from
/* Keep within the current frame, or in frames called by the current
one. */
- if (prev_frame)
+
+ while (get_frame_type (frame) == INLINE_FRAME)
+ frame = get_prev_frame (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: buildsym.c
===================================================================
RCS file: /scratch/gcc/repos/src/src/gdb/buildsym.c,v
retrieving revision 1.64
diff -u -p -r1.64 buildsym.c
--- buildsym.c 27 May 2008 19:29:51 -0000 1.64
+++ buildsym.c 13 Jun 2008 14:57:59 -0000
@@ -1171,6 +1171,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: dwarf2loc.c
===================================================================
RCS file: /scratch/gcc/repos/src/src/gdb/dwarf2loc.c,v
retrieving revision 1.51
diff -u -p -r1.51 dwarf2loc.c
--- dwarf2loc.c 4 May 2008 12:44:16 -0000 1.51
+++ dwarf2loc.c 13 Jun 2008 14:57:59 -0000
@@ -31,6 +31,7 @@
#include "regcache.h"
#include "objfiles.h"
#include "exceptions.h"
+#include "block.h"
#include "elf/dwarf2.h"
#include "dwarf2expr.h"
@@ -146,14 +147,19 @@ 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,
something has gone wrong. */
gdb_assert (framefunc != NULL);
- if (SYMBOL_OPS (framefunc) == &dwarf2_loclist_funcs)
+ if (SYMBOL_LOCATION_BATON (framefunc) == NULL)
+ *start = NULL;
+ else if (SYMBOL_OPS (framefunc) == &dwarf2_loclist_funcs)
{
struct dwarf2_loclist_baton *symbaton;
struct frame_info *frame = debaton->frame;
Index: dwarf2read.c
===================================================================
RCS file: /scratch/gcc/repos/src/src/gdb/dwarf2read.c,v
retrieving revision 1.265
diff -u -p -r1.265 dwarf2read.c
--- dwarf2read.c 27 May 2008 19:29:51 -0000 1.265
+++ dwarf2read.c 13 Jun 2008 14:57:59 -0000
@@ -45,6 +45,7 @@
#include "hashtab.h"
#include "command.h"
#include "gdbcmd.h"
+#include "block.h"
#include "addrmap.h"
#include <fcntl.h>
@@ -2713,12 +2714,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:
@@ -2941,12 +2938,27 @@ 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;
const char *previous_prefix = processing_current_prefix;
struct cleanup *back_to = NULL;
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));
@@ -3033,6 +3045,9 @@ read_func_scope (struct die_info *die, s
block = finish_block (new->name, &local_symbols, new->old_blocks,
lowpc, highpc, objfile);
+ if (inlined_func)
+ BLOCK_INLINED (block) = 1;
+
/* If we have address ranges, record them. */
dwarf2_record_block_ranges (die, block, baseaddr, cu);
@@ -6672,8 +6687,9 @@ die_is_declaration (struct die_info *die
&& dwarf2_attr (die, DW_AT_specification, cu) == NULL);
}
-/* Return the die giving the specification for DIE, if there is
- one. */
+/* Return the die giving the specification for DIE, if there is one.
+ If there is no specification, but there is an abstract origin, that
+ is returned. */
static struct die_info *
die_specification (struct die_info *die, struct dwarf2_cu *cu)
@@ -6681,6 +6697,9 @@ die_specification (struct die_info *die,
struct attribute *spec_attr = dwarf2_attr (die, DW_AT_specification, cu);
if (spec_attr == NULL)
+ spec_attr = dwarf2_attr (die, DW_AT_abstract_origin, cu);
+
+ if (spec_attr == NULL)
return NULL;
else
return follow_die_ref (die, spec_attr, cu);
@@ -7357,6 +7376,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));
@@ -7384,13 +7404,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);
@@ -7437,6 +7461,13 @@ 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;
+ /* 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
@@ -7484,7 +7515,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: findvar.c
===================================================================
RCS file: /scratch/gcc/repos/src/src/gdb/findvar.c,v
retrieving revision 1.114
diff -u -p -r1.114 findvar.c
--- findvar.c 27 May 2008 19:29:51 -0000 1.114
+++ findvar.c 13 Jun 2008 14:57:59 -0000
@@ -494,13 +494,11 @@ read_var_value (struct symbol *var, stru
case LOC_REGISTER:
case LOC_REGPARM_ADDR:
{
- struct block *b;
int regno = SYMBOL_VALUE (var);
struct value *regval;
if (frame == NULL)
return 0;
- b = get_frame_block (frame, 0);
if (SYMBOL_CLASS (var) == LOC_REGPARM_ADDR)
{
Index: frame-unwind.c
===================================================================
RCS file: /scratch/gcc/repos/src/src/gdb/frame-unwind.c,v
retrieving revision 1.22
diff -u -p -r1.22 frame-unwind.c
--- frame-unwind.c 6 May 2008 18:37:46 -0000 1.22
+++ frame-unwind.c 13 Jun 2008 14:57:59 -0000
@@ -51,8 +51,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: frame-unwind.h
===================================================================
RCS file: /scratch/gcc/repos/src/src/gdb/frame-unwind.h,v
retrieving revision 1.22
diff -u -p -r1.22 frame-unwind.h
--- frame-unwind.h 6 May 2008 18:37:46 -0000 1.22
+++ frame-unwind.h 13 Jun 2008 14:57:59 -0000
@@ -135,6 +135,8 @@ struct frame_unwind
frame_dealloc_cache_ftype *dealloc_cache;
};
+extern const struct frame_unwind *const inline_frame_unwind;
+
/* Register a frame unwinder, _prepending_ it to the front of the
search list (so it is sniffed before previously registered
unwinders). By using a prepend, later calls can install unwinders
Index: frame.c
===================================================================
RCS file: /scratch/gcc/repos/src/src/gdb/frame.c,v
retrieving revision 1.242
diff -u -p -r1.242 frame.c
--- frame.c 20 May 2008 22:21:19 -0000 1.242
+++ frame.c 13 Jun 2008 14:57:59 -0000
@@ -40,8 +40,13 @@
#include "observer.h"
#include "objfiles.h"
#include "exceptions.h"
+#include "block.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
@@ -172,6 +177,11 @@ 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, ",block=0x%s", paddr_nz (id.block_addr));
+ }
fprintf_unfiltered (file, "}");
}
@@ -186,6 +196,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;
@@ -330,8 +346,8 @@ frame_id_p (struct frame_id l)
return p;
}
-int
-frame_id_eq (struct frame_id l, struct frame_id r)
+static int
+frame_id_eq_1 (struct frame_id l, struct frame_id r)
{
int eq;
if (!l.stack_addr_p || !r.stack_addr_p)
@@ -341,21 +357,52 @@ 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
/* Frames are equal. */
eq = 1;
- else
- /* No luck. */
+
+ return eq;
+}
+
+int
+frame_id_stack_eq (struct frame_id l, struct frame_id r)
+{
+ int eq = frame_id_eq_1 (l, r);
+
+ if (frame_debug)
+ {
+ fprintf_unfiltered (gdb_stdlog, "{ frame_id_stack_eq (l=");
+ fprint_frame_id (gdb_stdlog, l);
+ fprintf_unfiltered (gdb_stdlog, ",r=");
+ fprint_frame_id (gdb_stdlog, r);
+ fprintf_unfiltered (gdb_stdlog, ") -> %d }\n", eq);
+ }
+
+ return eq;
+}
+
+int
+frame_id_eq (struct frame_id l, struct frame_id r)
+{
+ int eq = frame_id_eq_1 (l, r);
+
+ if (l.inline_depth != r.inline_depth)
+ /* If inline depths are different, the frames must be different. */
eq = 0;
+ else if (l.block_addr != r.block_addr)
+ /* If the inlined block has a different start address, the frames
+ must be different. */
+ eq = 0;
+
if (frame_debug)
{
fprintf_unfiltered (gdb_stdlog, "{ frame_id_eq (l=");
@@ -364,6 +411,7 @@ frame_id_eq (struct frame_id l, struct f
fprint_frame_id (gdb_stdlog, r);
fprintf_unfiltered (gdb_stdlog, ") -> %d }\n", eq);
}
+
return eq;
}
@@ -374,6 +422,28 @@ 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.code_addr == r.code_addr
+ && l.special_addr_p == r.special_addr_p
+ && l.special_addr == r.special_addr)
+ {
+ /* Same function, different inlined functions. */
+ struct block *lb, *rb;
+
+ lb = block_for_pc (l.block_addr);
+ rb = block_for_pc (r.block_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
@@ -401,7 +471,7 @@ frame_find_by_id (struct frame_id id)
if (!frame_id_p (id))
return NULL;
- for (frame = get_current_frame ();
+ for (frame = get_current_user_frame ();
frame != NULL;
frame = get_prev_frame (frame))
{
@@ -917,7 +987,7 @@ unwind_to_current_frame (struct ui_out *
}
struct frame_info *
-get_current_frame (void)
+get_current_user_frame (void)
{
/* First check, and report, the lack of registers. Having GDB
report "No stack!" or "No memory" when the target doesn't even
@@ -945,6 +1015,24 @@ get_current_frame (void)
return current_frame;
}
+/* Given FRAME, return the enclosing normal frame for inlined
+ function frames. Otherwise return the original frame. */
+
+static struct frame_info *
+get_real_frame (struct frame_info *frame)
+{
+ while (get_frame_type (frame) == INLINE_FRAME)
+ frame = get_prev_frame (frame);
+
+ return frame;
+}
+
+struct frame_info *
+get_current_frame (void)
+{
+ return get_real_frame (get_current_user_frame ());
+}
+
/* The "selected" stack frame is used by default for local and arg
access. May be zero, for no selected frame. */
@@ -966,7 +1054,7 @@ get_selected_frame (const char *message)
/* Hey! Don't trust this. It should really be re-finding the
last selected frame of the currently selected thread. This,
though, is better than nothing. */
- select_frame (get_current_frame ());
+ select_frame (get_current_user_frame ());
}
/* There is always a frame. */
gdb_assert (selected_frame != NULL);
@@ -1151,7 +1239,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;
@@ -1187,6 +1274,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. */
@@ -1254,7 +1349,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;
@@ -1283,6 +1379,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
@@ -1442,7 +1549,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
@@ -1488,8 +1595,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 (gdb_stdlog, this_frame, "inside entry func");
@@ -1500,7 +1608,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)
{
@@ -1529,21 +1638,31 @@ frame_unwind_address_in_block (struct fr
CORE_ADDR pc = frame_pc_unwind (next_frame);
/* If NEXT_FRAME was called by a signal frame or dummy frame, then
- we shold not adjust the unwound PC. These frames may not call
+ we should not adjust the unwound PC. These frames may not call
their next frame in the normal way; the operating system or GDB
may have pushed their resume address manually onto the stack, so
it may be the very first instruction. Even if the resume address
was not manually pushed, they expect to be returned to. */
- if (this_type != NORMAL_FRAME)
+ if (this_type != NORMAL_FRAME && this_type != INLINE_FRAME)
return pc;
+ /* If NEXT_FRAME was inlined into the current frame, we are really
+ interested in an even younger (newer) frame - the point where
+ execution left THIS function. */
+ while (get_frame_type (next_frame) == INLINE_FRAME)
+ next_frame = next_frame->next;
+
/* If THIS frame is not inner most (i.e., NEXT isn't the sentinel),
and NEXT is `normal' (i.e., not a sigtramp, dummy, ....) THIS
frame's PC ends up pointing at the instruction following the
"call". Adjust that PC value so that it falls on the call
instruction (which, hopefully, falls within THIS frame's code
block). So far it's proved to be a very good approximation. See
- get_frame_type() for why ->type can't be used. */
+ get_frame_type() for why ->type can't be used.
+
+ This is correct even if NEXT_FRAME describes an inlined function.
+ Inlined functions always live between two normal frames, and are
+ themselves normal. */
if (next_frame->level >= 0
&& get_frame_type (next_frame) == NORMAL_FRAME)
--pc;
@@ -1557,9 +1676,41 @@ get_frame_address_in_block (struct frame
get_frame_type (this_frame));
}
-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 ((next_frame != NULL && get_frame_type (next_frame) == INLINE_FRAME)
+ || (next_frame == NULL && inline_skipped_frames () > 0))
+ {
+ struct symbol *sym;
+
+ if (next_frame)
+ sym = get_frame_function (next_frame);
+ else
+ sym = inline_skipped_symbol ();
+
+ 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
@@ -1569,15 +1720,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
@@ -1658,7 +1802,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: frame.h
===================================================================
RCS file: /scratch/gcc/repos/src/src/gdb/frame.h,v
retrieving revision 1.164
diff -u -p -r1.164 frame.h
--- frame.h 21 May 2008 15:08:39 -0000 1.164
+++ frame.h 13 Jun 2008 14:57:59 -0000
@@ -122,6 +122,17 @@ 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;
+
+ /* A second code address, considered only if inline_depth != 0. The
+ block address lets GDB distinguish multiple functions inlined
+ into the same caller. This should be the first executed
+ instruction in the block corresponding to the inlined
+ function. */
+ CORE_ADDR block_addr;
};
/* Methods for constructing and comparing Frame IDs.
@@ -172,6 +183,11 @@ 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 and R identify frames associated with
+ the same underlying stack frame, although they may refer to
+ different inlined functions within the stack frame. */
+extern int frame_id_stack_eq (struct frame_id l, struct frame_id r);
+
/* 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);
@@ -198,6 +214,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,
@@ -222,7 +241,10 @@ enum frame_type
/* On demand, create the inner most frame using information found in
the inferior. If the inner most frame can't be created, throw an
- error. */
+ error. get_current_user_frame returns the frame we should display
+ to the user, which may be an inlined function; get_current_frame
+ returns the innermost non-inlined frame. */
+extern struct frame_info *get_current_user_frame (void);
extern struct frame_info *get_current_frame (void);
/* Invalidates the frame cache (this function should have been called
@@ -705,14 +727,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
@@ -723,4 +737,9 @@ extern void deprecated_update_frame_pc_h
extern void deprecated_update_frame_base_hack (struct frame_info *frame,
CORE_ADDR base);
+extern void set_skipped_inline_frames (int skip_ok);
+extern void step_into_inline_frame (void);
+extern int inline_skipped_frames (void);
+extern struct symbol *inline_skipped_symbol (void);
+
#endif /* !defined (FRAME_H) */
Index: infcall.c
===================================================================
RCS file: /scratch/gcc/repos/src/src/gdb/infcall.c,v
retrieving revision 1.99
diff -u -p -r1.99 infcall.c
--- infcall.c 10 Jun 2008 10:23:53 -0000 1.99
+++ infcall.c 13 Jun 2008 14:57:59 -0000
@@ -768,11 +768,8 @@ call_function_by_hand (struct value *fun
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: infcmd.c
===================================================================
RCS file: /scratch/gcc/repos/src/src/gdb/infcmd.c,v
retrieving revision 1.187
diff -u -p -r1.187 infcmd.c
--- infcmd.c 10 Jun 2008 10:23:53 -0000 1.187
+++ infcmd.c 13 Jun 2008 14:57:59 -0000
@@ -666,6 +666,17 @@ continue_command (char *proc_count_exp,
proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 0);
}
\f
+/* Record the starting point of a "step" or "next" command. */
+
+static void
+set_step_frame (void)
+{
+ struct symtab_and_line sal;
+
+ find_frame_sal (get_current_user_frame (), &sal);
+ set_step_info (get_frame_id (get_current_user_frame ()), sal);
+}
+
/* Step until outside of current statement. */
static void
@@ -742,64 +753,20 @@ 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, just use the regular for loop. */
if (!target_can_async_p ())
{
for (; count > 0; count--)
- {
- clear_proceed_status ();
-
- frame = get_current_frame ();
- if (!frame) /* Avoid coredump here. Why tho? */
- error (_("No current frame"));
- step_frame_id = get_frame_id (frame);
-
- if (!single_inst)
- {
- find_pc_line_pc_range (stop_pc, &step_range_start, &step_range_end);
- if (step_range_end == 0)
- {
- char *name;
- if (find_pc_partial_function (stop_pc, &name, &step_range_start,
- &step_range_end) == 0)
- error (_("Cannot find bounds of current function"));
-
- target_terminal_ours ();
- printf_filtered (_("\
-Single stepping until exit from function %s, \n\
-which has no line number information.\n"), name);
- }
- }
- else
- {
- /* Say we are stepping, but stop after one insn whatever it does. */
- step_range_start = step_range_end = 1;
- if (!skip_subroutines)
- /* It is stepi.
- Don't step over function calls, not even to functions lacking
- line numbers. */
- step_over_calls = STEP_OVER_NONE;
- }
-
- if (skip_subroutines)
- step_over_calls = STEP_OVER_ALL;
-
- step_multi = (count > 1);
- proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 1);
-
- if (!stop_step)
- break;
- }
+ step_once (skip_subroutines, single_inst, count, thread);
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 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(). */
step_once (skip_subroutines, single_inst, count, thread);
/* We are running, and the continuation is installed. It will
disable the longjmp breakpoint as appropriate. */
@@ -857,22 +824,25 @@ step_once (int skip_subroutines, int sin
if (count > 0)
{
clear_proceed_status ();
-
- frame = get_current_frame ();
- if (!frame) /* Avoid coredump here. Why tho? */
- error (_("No current frame"));
- step_frame_id = get_frame_id (frame);
+ set_step_frame ();
if (!single_inst)
{
- find_pc_line_pc_range (stop_pc, &step_range_start, &step_range_end);
-
- /* If we have no line info, switch to stepi mode. */
- if (step_range_end == 0 && step_stop_if_no_debug)
+ /* Step at an inlined function behaves like "down". */
+ if (!skip_subroutines && !single_inst && inline_skipped_frames ())
{
- step_range_start = step_range_end = 1;
+ step_into_inline_frame ();
+ if (count > 1)
+ step_once (skip_subroutines, single_inst, count - 1, thread);
+ else
+ /* Pretend that we've stopped. */
+ normal_stop ();
+ return;
}
- else if (step_range_end == 0)
+
+ find_pc_line_pc_range (stop_pc, &step_range_start, &step_range_end);
+
+ if (step_range_end == 0)
{
char *name;
if (find_pc_partial_function (stop_pc, &name, &step_range_start,
@@ -900,24 +870,25 @@ which has no line number information.\n"
step_over_calls = STEP_OVER_ALL;
step_multi = (count > 1);
+
proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 1);
- arg1 =
- (struct continuation_arg *) xmalloc (sizeof (struct continuation_arg));
- arg2 =
- (struct continuation_arg *) xmalloc (sizeof (struct continuation_arg));
- arg3 =
- (struct continuation_arg *) xmalloc (sizeof (struct continuation_arg));
- arg4 =
- (struct continuation_arg *) xmalloc (sizeof (struct continuation_arg));
- arg1->next = arg2;
- arg1->data.integer = skip_subroutines;
- arg2->next = arg3;
- arg2->data.integer = single_inst;
- arg3->next = arg4;
- arg3->data.integer = count;
- arg4->next = NULL;
- arg4->data.integer = thread;
- add_intermediate_continuation (step_1_continuation, arg1);
+
+ if (target_can_async_p ())
+ {
+ arg1 = xmalloc (sizeof (struct continuation_arg));
+ arg2 = xmalloc (sizeof (struct continuation_arg));
+ arg3 = xmalloc (sizeof (struct continuation_arg));
+ arg4 = xmalloc (sizeof (struct continuation_arg));
+ arg1->next = arg2;
+ arg1->data.integer = skip_subroutines;
+ arg2->next = arg3;
+ arg2->data.integer = single_inst;
+ arg3->next = arg4;
+ arg3->data.integer = count;
+ arg4->next = NULL;
+ arg4->data.integer = thread;
+ add_intermediate_continuation (step_1_continuation, arg1);
+ }
}
}
@@ -1100,14 +1071,12 @@ 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;
clear_proceed_status ();
-
- frame = get_current_frame ();
+ set_step_frame ();
/* Step until either exited from this function or greater
than the current line (if in symbolic section) or pc (if
@@ -1135,7 +1104,6 @@ until_next_command (int from_tty)
}
step_over_calls = STEP_OVER_ALL;
- step_frame_id = get_frame_id (frame);
step_multi = 0; /* Only one call to proceed */
@@ -1351,6 +1319,35 @@ finish_command (char *arg, int from_tty)
clear_proceed_status ();
+ /* Finishing from an inline frame is completely different. We don't
+ try to show the "return value" - no way to locate it. So we do
+ not need a completion. */
+ if (get_frame_type (get_selected_frame (_("No selected frame.")))
+ == INLINE_FRAME)
+ {
+ /* Claim we are stepping in the calling frame. An empty step
+ range means that we will stop once we aren't in a function
+ called by that frame. We don't use the magic "1" value for
+ step_range_end, because then infrun will think this is nexti,
+ and not step over the rest of this inlined function call. */
+ struct symtab_and_line empty_sal;
+ init_sal (&empty_sal);
+ set_step_info (get_frame_id (frame), empty_sal);
+ step_range_start = step_range_end = get_frame_pc (frame);
+ 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;
+ }
+
sal = find_pc_line (get_frame_pc (frame), 0);
sal.pc = get_frame_pc (frame);
Index: inferior.h
===================================================================
RCS file: /scratch/gcc/repos/src/src/gdb/inferior.h,v
retrieving revision 1.91
diff -u -p -r1.91 inferior.h
--- inferior.h 10 Jun 2008 10:23:53 -0000 1.91
+++ inferior.h 13 Jun 2008 14:57:59 -0000
@@ -242,6 +242,8 @@ extern void get_last_target_status(ptid_
extern void follow_inferior_reset_breakpoints (void);
+void set_step_info (struct frame_id id, struct symtab_and_line sal);
+
/* From infcmd.c */
extern void tty_command (char *, int);
Index: infrun.c
===================================================================
RCS file: /scratch/gcc/repos/src/src/gdb/infrun.c,v
retrieving revision 1.280
diff -u -p -r1.280 infrun.c
--- infrun.c 10 Jun 2008 10:23:53 -0000 1.280
+++ infrun.c 13 Jun 2008 14:57:59 -0000
@@ -282,6 +282,11 @@ static int stop_print_frame;
/* Step-resume or longjmp-resume breakpoint. */
static struct breakpoint *step_resume_breakpoint = NULL;
+/* The source file and line at the beginning of the current step
+ operation. Only valid when step_frame_id is set. */
+static struct symtab *step_current_symtab;
+static int step_current_line;
+
/* This is a cached copy of the pid/waitstatus of the last event
returned by target_wait()/deprecated_target_wait_hook(). This
information is returned by get_last_target_status(). */
@@ -1379,8 +1384,6 @@ struct execution_control_state
CORE_ADDR stop_func_end;
char *stop_func_name;
struct symtab_and_line sal;
- int current_line;
- struct symtab *current_symtab;
ptid_t ptid;
ptid_t saved_inferior_ptid;
int step_after_step_resume_breakpoint;
@@ -1393,7 +1396,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);
@@ -1539,10 +1542,19 @@ fetch_inferior_event (void *client_data)
}
}
+/* Record the frame and location we're currently stepping through. */
+void
+set_step_info (struct frame_id id, struct symtab_and_line sal)
+{
+ step_frame_id = id;
+ step_current_symtab = sal.symtab;
+ step_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->stepping_over_breakpoint = 0;
@@ -1551,8 +1563,6 @@ init_execution_control_state (struct exe
ecs->stepping_through_solib_after_catch = 0;
ecs->stepping_through_solib_catchpoints = NULL;
ecs->sal = find_pc_line (prev_pc, 0);
- ecs->current_line = ecs->sal.line;
- ecs->current_symtab = ecs->sal.symtab;
ecs->infwait_state = infwait_normal_state;
ecs->waiton_ptid = pid_to_ptid (-1);
ecs->wp = &(ecs->ws);
@@ -1605,7 +1615,7 @@ context_switch (struct execution_control
ecs->stepping_over_breakpoint,
ecs->stepping_through_solib_after_catch,
ecs->stepping_through_solib_catchpoints,
- ecs->current_line, ecs->current_symtab);
+ step_current_line, step_current_symtab);
/* Load infrun state for the new thread. */
load_infrun_state (ecs->ptid, &prev_pc,
@@ -1615,7 +1625,7 @@ context_switch (struct execution_control
&ecs->stepping_over_breakpoint,
&ecs->stepping_through_solib_after_catch,
&ecs->stepping_through_solib_catchpoints,
- &ecs->current_line, &ecs->current_symtab);
+ &step_current_line, &step_current_symtab);
}
switch_to_thread (ecs->ptid);
@@ -1695,6 +1705,22 @@ adjust_pc_after_break (struct execution_
}
}
+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. */
@@ -1755,6 +1781,7 @@ handle_inferior_event (struct execution_
ecs->infwait_state = infwait_normal_state;
reinit_frame_cache ();
+ set_skipped_inline_frames (0);
/* If it's a new process, add it to the thread database */
@@ -2319,6 +2346,12 @@ handle_inferior_event (struct execution_
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 (step_range_end != 1)
+ set_skipped_inline_frames (1);
+
if (stop_signal == TARGET_SIGNAL_TRAP
&& stepping_over_breakpoint
&& gdbarch_single_step_through_delay_p (current_gdbarch)
@@ -2527,8 +2560,8 @@ process_event_stop_test:
if (step_range_end != 0
&& stop_signal != TARGET_SIGNAL_0
&& stop_pc >= step_range_start && stop_pc < step_range_end
- && frame_id_eq (get_frame_id (get_current_frame ()),
- step_frame_id)
+ && frame_id_stack_eq (get_frame_id (get_current_frame ()),
+ step_frame_id)
&& step_resume_breakpoint == NULL)
{
/* The inferior is about to take a signal that will take it
@@ -2863,12 +2896,7 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (
until we exit the run time loader code and reach the callee's
address. */
if (step_over_calls == STEP_OVER_UNDEBUGGABLE
-#ifdef IN_SOLIB_DYNSYM_RESOLVE_CODE
- && IN_SOLIB_DYNSYM_RESOLVE_CODE (stop_pc)
-#else
- && in_solib_dynsym_resolve_code (stop_pc)
-#endif
- )
+ && in_solib_dynsym_resolve_code (stop_pc))
{
CORE_ADDR pc_after_resolver =
gdbarch_skip_solib_resolver (current_gdbarch, stop_pc);
@@ -2912,11 +2940,12 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (
previous frame's ID is sufficient - but it is a common case and
cheaper than checking the previous frame's ID.
- NOTE: frame_id_eq will never report two invalid frame IDs as
+ NOTE: frame_id_stack_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 ()), step_frame_id)
- && frame_id_eq (frame_unwind_id (get_current_frame ()), step_frame_id))
+ if (!frame_id_stack_eq (get_frame_id (get_current_frame ()), step_frame_id)
+ && frame_id_stack_eq (frame_unwind_id (get_current_frame ()),
+ step_frame_id))
{
CORE_ADDR real_stop_pc;
@@ -2961,13 +2990,7 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (
if (real_stop_pc != 0)
ecs->stop_func_start = real_stop_pc;
- if (
-#ifdef IN_SOLIB_DYNSYM_RESOLVE_CODE
- IN_SOLIB_DYNSYM_RESOLVE_CODE (ecs->stop_func_start)
-#else
- in_solib_dynsym_resolve_code (ecs->stop_func_start)
-#endif
-)
+ if (real_stop_pc != 0 && in_solib_dynsym_resolve_code (real_stop_pc))
{
struct symtab_and_line sr_sal;
init_sal (&sr_sal);
@@ -3115,9 +3138,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_user_frame ()), step_frame_id)
+ && inline_skipped_frames ())
+ {
+ struct symtab_and_line call_sal;
+
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: stepped into inlined function\n");
+
+ find_frame_sal (get_current_user_frame (), &call_sal);
+
+ if (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 == step_current_line
+ && call_sal.symtab == step_current_symtab)
+ step_into_inline_frame ();
+
+ 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 == step_current_line
+ && call_sal.symtab == step_current_symtab)
+ keep_going (ecs);
+ else
+ {
+ 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_user_frame ()) == INLINE_FRAME
+ && !frame_id_eq (get_frame_id (get_current_user_frame ()), step_frame_id)
+ && stepped_in_from (get_current_user_frame (), step_frame_id))
+ {
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: stepping through inlined function\n");
+
+ if (step_over_calls == STEP_OVER_ALL)
+ keep_going (ecs);
+ else
+ {
+ stop_step = 1;
+ print_stop_reason (END_STEPPING_RANGE, 0);
+ stop_stepping (ecs);
+ }
+ return;
+ }
+
if ((stop_pc == ecs->sal.pc)
- && (ecs->current_line != ecs->sal.line
- || ecs->current_symtab != ecs->sal.symtab))
+ && (step_current_line != ecs->sal.line
+ || step_current_symtab != ecs->sal.symtab))
{
/* We are at the start of a different line. So stop. Note that
we don't stop if we step into the middle of a different line.
@@ -3140,13 +3236,11 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (
step_range_start = ecs->sal.pc;
step_range_end = ecs->sal.end;
- step_frame_id = get_frame_id (get_current_frame ());
- ecs->current_line = ecs->sal.line;
- ecs->current_symtab = ecs->sal.symtab;
+ set_step_info (get_frame_id (get_current_user_frame ()), ecs->sal);
/* In the case where we just stepped out of a function into the
middle of a line of the caller, continue stepping, but
- step_frame_id must be modified to current frame */
+ step_frame_id must be modified to current frame (above). */
#if 0
/* NOTE: cagney/2003-10-16: I think this frame ID inner test is too
generous. It will trigger on things like a step into a frameless
@@ -3163,13 +3257,6 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (
and we're willing to introduce frame unwind logic into this
function. Fortunately, those days are nearly upon us. */
#endif
- {
- struct frame_info *frame = get_current_frame ();
- struct frame_id current_frame = get_frame_id (frame);
- if (!(frame_id_inner (get_frame_arch (frame), current_frame,
- step_frame_id)))
- step_frame_id = current_frame;
- }
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: keep going\n");
@@ -3609,17 +3696,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 ())
@@ -3649,7 +3725,7 @@ Further execution is probably impossible
display the frame below, but the current SAL will be incorrect
during a user hook-stop function. */
if (target_has_stack && !stop_stack_dummy)
- set_current_sal_from_frame (get_current_frame (), 1);
+ set_current_sal_from_frame (get_current_user_frame (), 1);
/* Look up the hook_stop and run it (CLI internally handles problem
of stop_command's pre-hook not existing). */
@@ -3659,7 +3735,6 @@ Further execution is probably impossible
if (!target_has_stack)
{
-
goto done;
}
@@ -3670,7 +3745,7 @@ Further execution is probably impossible
if (!stop_stack_dummy)
{
- select_frame (get_current_frame ());
+ select_frame (get_current_user_frame ());
/* Print current location without a level number, if
we have changed functions or hit a breakpoint.
@@ -3708,7 +3783,7 @@ Further execution is probably impossible
should) use that when doing a frame comparison. */
if (stop_step
&& frame_id_eq (step_frame_id,
- get_frame_id (get_current_frame ()))
+ get_frame_id (get_selected_frame (NULL)))
&& step_start_function == find_pc_function (stop_pc))
source_flag = SRC_LINE; /* finished step, just print source line */
else
@@ -3767,7 +3842,7 @@ Further execution is probably impossible
Can't rely on restore_inferior_status because that only gets
called if we don't stop in the called function. */
stop_pc = read_pc ();
- select_frame (get_current_frame ());
+ select_frame (get_current_user_frame ());
}
done:
@@ -4288,7 +4363,7 @@ restore_inferior_status (struct inferior
RETURN_MASK_ERROR) == 0)
/* Error in restoring the selected frame. Select the innermost
frame. */
- select_frame (get_current_frame ());
+ select_frame (get_current_user_frame ());
}
Index: inline-frame.c
===================================================================
RCS file: inline-frame.c
diff -N inline-frame.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ inline-frame.c 13 Jun 2008 14:57:59 -0000
@@ -0,0 +1,261 @@
+/* Inline frame unwinder for GDB.
+
+ Copyright (C) 2007 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 2 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include "defs.h"
+#include "addrmap.h"
+#include "block.h"
+#include "frame-unwind.h"
+#include "symtab.h"
+
+#include "gdb_assert.h"
+
+static int inline_skip_frames;
+
+/* Only valid if INLINE_SKIP_FRAMES is non-zero. This is the PC used
+ when calculating INLINE_SKIP_FRAMES; used to check whether we have
+ moved to a new location by user request. */
+static CORE_ADDR inline_skip_pc;
+
+/* Only valid if INLINE_SKIP_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. */
+static struct symbol *inline_skip_symbol;
+
+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).block_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)
+{
+ return frame_unwind_got_register (this_frame, regnum, 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;
+
+ 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 whether we were requested to skip some frames, so they
+ can be stepped into later. */
+ if (inline_skip_frames > 0 && frame_relative_level (this_frame) == 0)
+ {
+ if (this_pc != inline_skip_pc)
+ inline_skip_frames = 0;
+ else
+ {
+ gdb_assert (depth >= inline_skip_frames);
+ depth -= inline_skip_frames;
+ }
+ }
+
+ if (depth == 0)
+ return 0;
+
+ /* There are inlined functions here. Check how many of them 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 all the inlined functions 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;
+}
+
+void
+set_skipped_inline_frames (int skip_ok)
+{
+ CORE_ADDR this_pc;
+ struct block *frame_block, *cur_block;
+ struct symbol *last_sym = NULL;
+ int skip_count = 0;
+
+ if (!skip_ok)
+ {
+ if (inline_skip_frames != 0)
+ {
+ inline_skip_frames = 0;
+ reinit_frame_cache ();
+ }
+ return;
+ }
+
+ /* 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_user_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);
+ }
+ }
+
+ inline_skip_pc = this_pc;
+ inline_skip_symbol = last_sym;
+
+ if (inline_skip_frames != skip_count)
+ {
+ inline_skip_frames = skip_count;
+ reinit_frame_cache ();
+ }
+}
+
+void
+step_into_inline_frame (void)
+{
+ gdb_assert (inline_skip_frames > 0);
+ inline_skip_frames--;
+ reinit_frame_cache ();
+}
+
+int
+inline_skipped_frames (void)
+{
+ return inline_skip_frames;
+}
+
+struct symbol *
+inline_skipped_symbol (void)
+{
+ return inline_skip_symbol;
+}
Index: minsyms.c
===================================================================
RCS file: /scratch/gcc/repos/src/src/gdb/minsyms.c,v
retrieving revision 1.59
diff -u -p -r1.59 minsyms.c
--- minsyms.c 16 May 2008 12:58:48 -0000 1.59
+++ minsyms.c 13 Jun 2008 14:57:59 -0000
@@ -761,7 +761,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: s390-tdep.c
===================================================================
RCS file: /scratch/gcc/repos/src/src/gdb/s390-tdep.c,v
retrieving revision 1.175
diff -u -p -r1.175 s390-tdep.c
--- s390-tdep.c 16 May 2008 00:27:23 -0000 1.175
+++ s390-tdep.c 13 Jun 2008 14:57:59 -0000
@@ -1216,7 +1216,8 @@ s390_prologue_frame_unwind_cache (struct
needed, instead the code should simpliy rely on its
analysis. */
if (get_next_frame (this_frame)
- && get_frame_type (get_next_frame (this_frame)) == NORMAL_FRAME)
+ && (get_frame_type (get_next_frame (this_frame)) == NORMAL_FRAME
+ || get_frame_type (get_next_frame (this_frame)) == INLINE_FRAME))
return 0;
/* If we really have a frameless function, %r14 must be valid
@@ -1263,7 +1264,8 @@ s390_prologue_frame_unwind_cache (struct
instead the code should simpliy rely on its analysis. */
if (size > 0
&& (!get_next_frame (this_frame)
- || get_frame_type (get_next_frame (this_frame)) != NORMAL_FRAME))
+ || get_frame_type (get_next_frame (this_frame)) != NORMAL_FRAME
+ || get_frame_type (get_next_frame (this_frame)) != INLINE_FRAME))
{
/* See the comment in s390_in_function_epilogue_p on why this is
not completely reliable ... */
Index: stack.c
===================================================================
RCS file: /scratch/gcc/repos/src/src/gdb/stack.c,v
retrieving revision 1.171
diff -u -p -r1.171 stack.c
--- stack.c 27 May 2008 19:29:51 -0000 1.171
+++ stack.c 13 Jun 2008 14:57:59 -0000
@@ -96,6 +96,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 () > 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
@@ -532,7 +556,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,
@@ -586,7 +610,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
@@ -607,8 +631,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)
@@ -674,7 +703,7 @@ print_frame (struct frame_info *frame, i
frame_relative_level (frame));
}
if (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 ();
@@ -823,7 +852,7 @@ parse_frame_specification_1 (const char
{
struct frame_info *fid;
int level = value_as_long (args[0]);
- fid = find_relative_frame (get_current_frame (), &level);
+ fid = find_relative_frame (get_current_user_frame (), &level);
if (level == 0)
/* find_relative_frame was successful */
return fid;
@@ -848,7 +877,7 @@ parse_frame_specification_1 (const char
what (s)he gets. Still, give the highest one that matches.
(NOTE: cagney/2004-10-29: Why highest, or outer-most, I don't
know). */
- for (fid = get_current_frame ();
+ for (fid = get_current_user_frame ();
fid != NULL;
fid = get_prev_frame (fid))
{
@@ -996,8 +1025,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)),
@@ -1174,7 +1205,7 @@ backtrace_command_1 (char *count_exp, in
variable TRAILING to the frame from which we should start
printing. Second, it must set the variable count to the number
of frames which we should print, or -1 if all of them. */
- trailing = get_current_frame ();
+ trailing = get_current_user_frame ();
/* The target can be in a state where there is no valid frames
(e.g., just connected). */
@@ -1458,7 +1489,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);
@@ -1529,7 +1562,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);
@@ -1793,6 +1828,9 @@ return_command (char *retval_exp, int fr
thisfun = get_frame_function (get_selected_frame ("No selected frame."));
+ if (get_frame_type (get_current_user_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: symtab.c
===================================================================
RCS file: /scratch/gcc/repos/src/src/gdb/symtab.c,v
retrieving revision 1.190
diff -u -p -r1.190 symtab.c
--- symtab.c 11 Jun 2008 22:03:49 -0000 1.190
+++ symtab.c 13 Jun 2008 14:59:03 -0000
@@ -1367,10 +1367,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;
}
@@ -2596,6 +2599,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);
@@ -2634,6 +2638,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_BFD_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;
}
@@ -3636,6 +3659,24 @@ language_search_unquoted_string (char *t
return p;
}
+static void
+completion_list_add_fields (struct symbol *sym, char *sym_text,
+ int sym_text_len, char *text, char *word)
+{
+ if (SYMBOL_CLASS (sym) == LOC_TYPEDEF)
+ {
+ struct type *t = SYMBOL_TYPE (sym);
+ enum type_code c = TYPE_CODE (t);
+ int j;
+
+ if (c == TYPE_CODE_UNION || c == TYPE_CODE_STRUCT)
+ for (j = TYPE_N_BASECLASSES (t); j < TYPE_NFIELDS (t); j++)
+ if (TYPE_FIELD_NAME (t, j))
+ completion_list_add_name (TYPE_FIELD_NAME (t, j),
+ sym_text, sym_text_len, text, word);
+ }
+}
+
char **
default_make_symbol_completion_list (char *text, char *word)
{
@@ -3648,9 +3689,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;
@@ -3760,41 +3801,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. */
@@ -3813,9 +3856,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);
@@ -4194,6 +4234,7 @@ skip_prologue_using_sal (CORE_ADDR func_
struct symtab_and_line prologue_sal;
CORE_ADDR start_pc;
CORE_ADDR end_pc;
+ struct block *bl;
/* Get an initial range for the function. */
find_pc_partial_function (func_addr, NULL, &start_pc, &end_pc);
@@ -4218,6 +4259,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: symtab.h
===================================================================
RCS file: /scratch/gcc/repos/src/src/gdb/symtab.h,v
retrieving revision 1.128
diff -u -p -r1.128 symtab.h
--- symtab.h 27 May 2008 19:29:51 -0000 1.128
+++ symtab.h 13 Jun 2008 14:57:59 -0000
@@ -568,9 +568,15 @@ 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? */
+ /* Line number of this symbol's definition, except for inlined
+ functions. For an inlined function (class LOC_BLOCK and
+ BLOCK_INLINE 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;
Index: thread.c
===================================================================
RCS file: /scratch/gcc/repos/src/src/gdb/thread.c,v
retrieving revision 1.72
diff -u -p -r1.72 thread.c
--- thread.c 10 Jun 2008 10:23:53 -0000 1.72
+++ thread.c 13 Jun 2008 14:57:59 -0000
@@ -693,7 +693,7 @@ thread_apply_all_command (char *cmd, int
do_cleanups (old_chain);
/* Print stack frame only if we changed thread. */
if (thread_has_changed)
- print_stack_frame (get_current_frame (), 1, SRC_LINE);
+ print_stack_frame (get_selected_frame (NULL), 1, SRC_LINE);
}
@@ -778,7 +778,7 @@ thread_apply_command (char *tidlist, int
do_cleanups (old_chain);
/* Print stack frame only if we changed thread. */
if (thread_has_changed)
- print_stack_frame (get_current_frame (), 1, SRC_LINE);
+ print_stack_frame (get_selected_frame (NULL), 1, SRC_LINE);
}
/* Switch to the specified thread. Will dispatch off to thread_apply_command
Index: tracepoint.c
===================================================================
RCS file: /scratch/gcc/repos/src/src/gdb/tracepoint.c,v
retrieving revision 1.106
diff -u -p -r1.106 tracepoint.c
--- tracepoint.c 27 May 2008 19:29:51 -0000 1.106
+++ tracepoint.c 13 Jun 2008 14:57:59 -0000
@@ -2129,7 +2129,7 @@ trace_find_line_command (char *args, int
{
if (args == 0 || *args == 0)
{
- sal = find_pc_line (get_frame_pc (get_current_frame ()), 0);
+ sal = find_pc_line (get_frame_pc (get_selected_frame (NULL)), 0);
sals.nelts = 1;
sals.sals = (struct symtab_and_line *)
xmalloc (sizeof (struct symtab_and_line));
Index: valops.c
===================================================================
RCS file: /scratch/gcc/repos/src/src/gdb/valops.c,v
retrieving revision 1.191
diff -u -p -r1.191 valops.c
--- valops.c 6 Jun 2008 18:29:00 -0000 1.191
+++ valops.c 13 Jun 2008 14:57:59 -0000
@@ -974,7 +974,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: doc/gdb.texinfo
===================================================================
RCS file: /scratch/gcc/repos/src/src/gdb/doc/gdb.texinfo,v
retrieving revision 1.504
diff -u -p -r1.504 gdb.texinfo
--- doc/gdb.texinfo 10 Jun 2008 10:23:53 -0000 1.504
+++ doc/gdb.texinfo 13 Jun 2008 14:57:59 -0000
@@ -146,6 +146,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
@@ -1803,7 +1804,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.
@@ -1812,22 +1813,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
@@ -7803,6 +7789,100 @@ $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} cannot 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 on the non-inlined copy of the function.
+
+@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.
+
+@item
+@value{GDBN} cannot locate the return value of inlined calls after
+using the @code{finish} command.
+
+@end itemize
+
+
@node Macros
@chapter C Preprocessor Macros
Index: mi/mi-cmd-stack.c
===================================================================
RCS file: /scratch/gcc/repos/src/src/gdb/mi/mi-cmd-stack.c,v
retrieving revision 1.40
diff -u -p -r1.40 mi-cmd-stack.c
--- mi/mi-cmd-stack.c 27 May 2008 19:29:51 -0000 1.40
+++ mi/mi-cmd-stack.c 13 Jun 2008 14:57:59 -0000
@@ -66,7 +66,7 @@ mi_cmd_stack_list_frames (char *command,
/* Let's position fi on the frame at which to start the
display. Could be the innermost frame if the whole stack needs
displaying, or if frame_low is 0. */
- for (i = 0, fi = get_current_frame ();
+ for (i = 0, fi = get_current_user_frame ();
fi && i < frame_low;
i++, fi = get_prev_frame (fi));
@@ -109,7 +109,7 @@ mi_cmd_stack_info_depth (char *command,
the stack. */
frame_high = -1;
- for (i = 0, fi = get_current_frame ();
+ for (i = 0, fi = get_current_user_frame ();
fi && (i < frame_high || frame_high == -1);
i++, fi = get_prev_frame (fi))
QUIT;
@@ -181,7 +181,7 @@ mi_cmd_stack_list_args (char *command, c
/* Let's position fi on the frame at which to start the
display. Could be the innermost frame if the whole stack needs
displaying, or if frame_low is 0. */
- for (i = 0, fi = get_current_frame ();
+ for (i = 0, fi = get_current_user_frame ();
fi && i < frame_low;
i++, fi = get_prev_frame (fi));
Index: testsuite/gdb.opt/Makefile.in
===================================================================
RCS file: /scratch/gcc/repos/src/src/gdb/testsuite/gdb.opt/Makefile.in,v
retrieving revision 1.2
diff -u -p -r1.2 Makefile.in
--- testsuite/gdb.opt/Makefile.in 17 Apr 2008 23:06:54 -0000 1.2
+++ testsuite/gdb.opt/Makefile.in 13 Jun 2008 14:57:59 -0000
@@ -1,7 +1,7 @@
VPATH = @srcdir@
srcdir = @srcdir@
-EXECUTABLES = hello/hello
+EXECUTABLES = hello/hello inline-bt
MISCELLANEOUS =
Index: testsuite/gdb.opt/inline-bt.c
===================================================================
RCS file: testsuite/gdb.opt/inline-bt.c
diff -N testsuite/gdb.opt/inline-bt.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.opt/inline-bt.c 13 Jun 2008 14:57:59 -0000
@@ -0,0 +1,52 @@
+/* Copyright (C) 2007 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 2 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ USA. */
+
+int x, y;
+volatile int result;
+
+void bar(void)
+{
+ x += y; /* set breakpoint 1 here */
+}
+
+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: testsuite/gdb.opt/inline-bt.exp
===================================================================
RCS file: testsuite/gdb.opt/inline-bt.exp
diff -N testsuite/gdb.opt/inline-bt.exp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.opt/inline-bt.exp 13 Jun 2008 14:57:59 -0000
@@ -0,0 +1,60 @@
+# Copyright 2007 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 2 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, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+set testfile "inline-bt"
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${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"]
+gdb_breakpoint $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: testsuite/gdb.opt/inline-cmds.c
===================================================================
RCS file: testsuite/gdb.opt/inline-cmds.c
diff -N testsuite/gdb.opt/inline-cmds.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.opt/inline-cmds.c 13 Jun 2008 14:57:59 -0000
@@ -0,0 +1,81 @@
+/* Copyright (C) 2007 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 2 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ USA. */
+
+int x, y;
+volatile int result;
+
+void bar(void)
+{
+ x += y; /* set breakpoint 1 here */
+}
+
+void marker(void)
+{
+ x += y; /* set breakpoint 2 here */
+}
+
+inline int func1(void)
+{
+ bar ();
+ return x * y;
+}
+
+inline int func2(void)
+{
+ return x * func1 ();
+}
+
+inline void func3(void)
+{
+ bar ();
+}
+
+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 ();
+
+ return 0;
+}
Index: testsuite/gdb.opt/inline-cmds.exp
===================================================================
RCS file: testsuite/gdb.opt/inline-cmds.exp
diff -N testsuite/gdb.opt/inline-cmds.exp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.opt/inline-cmds.exp 13 Jun 2008 14:57:59 -0000
@@ -0,0 +1,237 @@
+# Copyright 2007 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 2 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, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+set testfile "inline-cmds"
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${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-bt.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"]
+gdb_breakpoint $line1
+set line2 [gdb_get_line_number "set breakpoint 2 here"]
+gdb_breakpoint $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"
Index: testsuite/gdb.opt/inline-locals.c
===================================================================
RCS file: testsuite/gdb.opt/inline-locals.c
diff -N testsuite/gdb.opt/inline-locals.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.opt/inline-locals.c 13 Jun 2008 14:57:59 -0000
@@ -0,0 +1,57 @@
+/* Copyright (C) 2007 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 2 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ USA. */
+
+int x, y;
+volatile int result;
+volatile int *array_p;
+
+void bar(void)
+{
+ x += y; /* set breakpoint 1 here */
+}
+
+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: testsuite/gdb.opt/inline-locals.exp
===================================================================
RCS file: testsuite/gdb.opt/inline-locals.exp
diff -N testsuite/gdb.opt/inline-locals.exp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.opt/inline-locals.exp 13 Jun 2008 14:57:59 -0000
@@ -0,0 +1,89 @@
+# Copyright 2007 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 2 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, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+set testfile "inline-locals"
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${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"]
+gdb_breakpoint $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)"
+ gdb_test "info args" "arg1 = $decimal" "info args above bar (2)"
+} 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)"
+ gdb_test "info args" "arg1 = $decimal" "info args above bar (3)"
+} 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: testsuite/lib/gdb.exp
===================================================================
RCS file: /scratch/gcc/repos/src/src/gdb/testsuite/lib/gdb.exp,v
retrieving revision 1.102
diff -u -p -r1.102 gdb.exp
--- testsuite/lib/gdb.exp 4 May 2008 04:04:11 -0000 1.102
+++ testsuite/lib/gdb.exp 13 Jun 2008 14:57:59 -0000
@@ -1358,6 +1358,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
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch
2008-06-13 19:39 [FYI] Inlining support, rough patch Daniel Jacobowitz
@ 2008-06-13 19:43 ` Daniel Jacobowitz
2008-06-23 12:03 ` Jan Kratochvil
2008-07-15 19:21 ` Daniel Jacobowitz
2 siblings, 0 replies; 38+ messages in thread
From: Daniel Jacobowitz @ 2008-06-13 19:43 UTC (permalink / raw)
To: gdb-patches
On Fri, Jun 13, 2008 at 11:27:54AM -0400, Daniel Jacobowitz wrote:
> Enjoy.
Forgot one thing I meant to add.
There are several test cases in the patch. These have been very
useful while developing it; I've run them on five architectures and a
variety of compiler versions. I didn't fix all the failures on some
of those versions.
I'm not sure what to do with them. They're really hard to maintain at
no failures; every time you change one of the tests to fix a failure
on one platform, you run a higher than usual risk of breaking it on
another platform. Tested for optimized code behavior are hard :-(
Also, I did test it on non-toy programs, obviously. It does a
passibly good job stepping through the dwarf2 reader. But the
behavior is a little quirky, especially due to scheduling. And GDB is
very sensitive to compiler debug info bugs. And GCC has a lot of
them. Overall, with this patch merged I think the next places to
go for optimized code debugging are the two GCC branches currently
testbeds for improved optimized debug info generation. After that,
it's back to the age-old debugging scheduled code problem.
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch
2008-06-13 19:39 [FYI] Inlining support, rough patch Daniel Jacobowitz
2008-06-13 19:43 ` Daniel Jacobowitz
@ 2008-06-23 12:03 ` Jan Kratochvil
2008-06-23 14:23 ` Daniel Jacobowitz
2008-07-02 19:15 ` Daniel Jacobowitz
2008-07-15 19:21 ` Daniel Jacobowitz
2 siblings, 2 replies; 38+ messages in thread
From: Jan Kratochvil @ 2008-06-23 12:03 UTC (permalink / raw)
To: gdb-patches; +Cc: Daniel Jacobowitz
[-- Attachment #1: Type: text/plain, Size: 416 bytes --]
Hi,
here are some fixes on top of it. I find now your patch useful on top of CVS
HEAD. I expect some of its problems were probably due to the patch separation
from the CodeSourcery patchset.
On x86+x86_64+ppc64 with gcc-4.3.1-2 (Fedora) there are no regressions.
On x86+x86_64 with gcc-4.3.1-2 (Fedora) the provided tests PASS.
(On ppc64 the tests FAIL as multi-PC breakpoints do not work there.)
Thanks,
Jan
[-- Attachment #2: gdb-inline-works3.patch --]
[-- Type: text/plain, Size: 24866 bytes --]
diff -up -u -X /home/jkratoch/.diffi.list -rupxCVS sources-codesourceryinline-gdb/blockframe.c sources/gdb/blockframe.c
--- sources-codesourceryinline-gdb/blockframe.c 2008-06-20 15:59:08.000000000 +0200
+++ sources/gdb/blockframe.c 2008-06-23 00:51:34.000000000 +0200
@@ -72,18 +72,22 @@ get_frame_block (struct frame_info *fram
if (bl == NULL)
return NULL;
- next_frame = get_next_frame (frame);
+ inline_count = 0;
+
+ /* Any possibly inlined frames (inlined functions) behind NORMAL_FRAME
+ (non-inlined function) will already be at a different code location and
+ not found by BLOCK_FOR_PC at this PC moment. */
+ for (next_frame = get_next_frame (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.
+ INLINE_SKIPPED_FRAMES will never exceed the number of the most-inner
+ frames. But if we are unwinding already outer frames from some
+ non-inlined frame this frames skipping cannot apply. */
if (next_frame == NULL)
- inline_count = inline_skipped_frames ();
- else
- {
- inline_count = 0;
- while (next_frame != NULL && get_frame_type (next_frame) == INLINE_FRAME)
- {
- inline_count++;
- next_frame = get_next_frame (next_frame);
- }
- }
+ inline_count += inline_skipped_frames ();
while (inline_count > 0)
{
diff -up -u -X /home/jkratoch/.diffi.list -rupxCVS sources-codesourceryinline-gdb/breakpoint.c sources/gdb/breakpoint.c
--- sources-codesourceryinline-gdb/breakpoint.c 2008-06-20 15:59:08.000000000 +0200
+++ sources/gdb/breakpoint.c 2008-06-22 23:45:57.000000000 +0200
@@ -3076,6 +3076,11 @@ bpstat_stop_status (CORE_ADDR bp_addr, p
bs->print = 0;
}
bs->commands = copy_command_lines (bs->commands);
+
+ /* We display the innermost inlined frame at a breakpont as it gives to
+ most of the available information. */
+ if (b->type != bp_until && b->type != bp_finish)
+ set_skipped_inline_frames (0);
}
/* Print nothing for this entry if we dont stop or if we dont print. */
diff -up -u -X /home/jkratoch/.diffi.list -rupxCVS sources-codesourceryinline-gdb/frame-unwind.c sources/gdb/frame-unwind.c
--- sources-codesourceryinline-gdb/frame-unwind.c 2008-06-20 15:59:09.000000000 +0200
+++ sources/gdb/frame-unwind.c 2008-06-22 23:53:09.000000000 +0200
@@ -93,15 +93,38 @@ frame_unwind_find_by_frame (struct frame
struct frame_unwind_table *table = gdbarch_data (gdbarch, frame_unwind_data);
struct frame_unwind_table_entry *entry;
struct cleanup *old_cleanup;
+ const struct frame_unwind *inline_unwinder = NULL;
+
for (entry = table->list; entry != NULL; entry = entry->next)
{
struct cleanup *old_cleanup;
+ /* We delay the INLINE_FRAME unwinders as last as they need to base their
+ decisions on their outer frames. The inline unwinding sniffer cannot
+ access its outer frames as any frame must be free to fully access its
+ inner frames which leads to deadlocks. Therefore we first do
+ a regular unwind first and then override it (nullify the unwinding
+ operations) by setting it to the inlining unwinder (if it was detected
+ for such frame). */
+ if (entry->unwinder->type == INLINE_FRAME)
+ {
+ gdb_assert (inline_unwinder == NULL);
+ inline_unwinder = entry->unwinder;
+ continue;
+ }
old_cleanup = frame_prepare_for_sniffer (this_frame, entry->unwinder);
if (entry->unwinder->sniffer (entry->unwinder, this_frame,
this_cache))
{
discard_cleanups (old_cleanup);
+ if (inline_unwinder)
+ {
+ /* Pass the complete information about the regular unwinder. */
+ if (inline_unwinder->sniffer (entry->unwinder, this_frame,
+ this_cache))
+
+ return inline_unwinder;
+ }
return entry->unwinder;
}
do_cleanups (old_cleanup);
diff -up -u -X /home/jkratoch/.diffi.list -rupxCVS sources-codesourceryinline-gdb/frame.c sources/gdb/frame.c
--- sources-codesourceryinline-gdb/frame.c 2008-06-20 15:59:09.000000000 +0200
+++ sources/gdb/frame.c 2008-06-22 18:01:02.000000000 +0200
@@ -422,12 +422,12 @@ 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
+ else if (l.inline_depth < r.inline_depth
&& l.stack_addr == r.stack_addr
&& l.code_addr_p == r.code_addr_p
- && l.code_addr == r.code_addr
+ && (!l.code_addr_p || l.code_addr == r.code_addr)
&& l.special_addr_p == r.special_addr_p
- && l.special_addr == r.special_addr)
+ && (!l.special_addr_p || l.special_addr == r.special_addr))
{
/* Same function, different inlined functions. */
struct block *lb, *rb;
@@ -1697,7 +1697,7 @@ find_frame_sal (struct frame_info *frame
sym = inline_skipped_symbol ();
init_sal (sal);
- if (SYMBOL_LINE (sym) != 0)
+ if (sym && SYMBOL_LINE (sym) != 0)
{
sal->symtab = SYMBOL_SYMTAB (sym);
sal->line = SYMBOL_LINE (sym);
diff -up -u -X /home/jkratoch/.diffi.list -rupxCVS sources-codesourceryinline-gdb/frame.h sources/gdb/frame.h
--- sources-codesourceryinline-gdb/frame.h 2008-06-20 15:59:09.000000000 +0200
+++ sources/gdb/frame.h 2008-06-22 23:54:08.000000000 +0200
@@ -214,8 +214,11 @@ 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. */
+ /* A frame representing an inlined function, associated with an upcoming
+ (next, inner, younger) NORMAL_FRAME. Its unwinder execution is delayed
+ after the other unwinders (see FRAME_UNWIND_FIND_BY_FRAME). This
+ unwinder expects the regular unwinder and its existing cache to be passed
+ as the parameters. */
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. */
diff -up -u -X /home/jkratoch/.diffi.list -rupxCVS sources-codesourceryinline-gdb/infcmd.c sources/gdb/infcmd.c
--- sources-codesourceryinline-gdb/infcmd.c 2008-06-20 15:59:09.000000000 +0200
+++ sources/gdb/infcmd.c 2008-06-17 20:57:03.000000000 +0200
@@ -758,7 +758,13 @@ step_1 (int skip_subroutines, int single
if (!target_can_async_p ())
{
for (; count > 0; count--)
- step_once (skip_subroutines, single_inst, count, thread);
+ {
+ step_once (skip_subroutines, single_inst, count, thread);
+
+ /* Unexpected breakpoint terminates our multistepping. */
+ if (!stop_step)
+ break;
+ }
do_cleanups (cleanups);
}
diff -up -u -X /home/jkratoch/.diffi.list -rupxCVS sources-codesourceryinline-gdb/inline-frame.c sources/gdb/inline-frame.c
--- sources-codesourceryinline-gdb/inline-frame.c 2008-06-20 15:59:09.000000000 +0200
+++ sources/gdb/inline-frame.c 2008-06-23 00:54:53.000000000 +0200
@@ -39,20 +39,46 @@ static CORE_ADDR inline_skip_pc;
call site of the current frame. */
static struct symbol *inline_skip_symbol;
+/* Transparently wrap the former regular unwinder. Allocated by
+ FRAME_OBSTACK_ZALLOC. */
+struct inline_cache
+ {
+ const struct frame_unwind *unwind_regular;
+ void *cache_regular;
+ };
+
+/* We just must remap wrapped THIS_CACHE. */
+
+static void
+inline_frame_dealloc (struct frame_info *self, void *this_cache)
+{
+ struct inline_cache *inline_cache = this_cache;
+
+ /* The same condition as in REINIT_FRAME_CACHE. */
+ if (inline_cache->unwind_regular->dealloc_cache
+ && inline_cache->cache_regular)
+ inline_cache->unwind_regular->dealloc_cache (self,
+ inline_cache->cache_regular);
+}
+
static void
inline_frame_this_id (struct frame_info *this_frame,
void **this_cache,
struct frame_id *this_id)
{
struct symbol *func;
+ struct frame_info *next_frame;
+ struct inline_cache *inline_cache = *this_cache;
- /* 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));
+ gdb_assert (get_frame_type (this_frame) == INLINE_FRAME);
+
+ /* 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. We must not call GET_PREV_FRAME as each frame expects its inner
+ frames to be already fully accessible. This would open a can of worms
+ with unwinding deadlocks. */
+ inline_cache->unwind_regular->this_id (this_frame,
+ &inline_cache->cache_regular, this_id);
/* 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
@@ -68,13 +94,27 @@ inline_frame_this_id (struct frame_info
func = get_frame_function (this_frame);
gdb_assert (func != NULL);
(*this_id).block_addr = BLOCK_START (SYMBOL_BLOCK_VALUE (func));
- (*this_id).inline_depth++;
+
+ /* The regular unwinder knows no INLINE_DEPTH. */
+ gdb_assert ((*this_id).inline_depth == 0);
+
+ /* Inlined frames (inlined functions) can never exist behind a normal frame
+ (non-inlined function). Any possible inlined frames (inlined functions)
+ behind such frame will already be at a different code location and not
+ found by BLOCK_FOR_PC at this PC moment. */
+ for (next_frame = this_frame;
+ next_frame && get_frame_type (next_frame) == INLINE_FRAME;
+ next_frame = get_next_frame (next_frame))
+ (*this_id).inline_depth++;
+
+ gdb_assert ((*this_id).inline_depth > 0);
}
static struct value *
inline_frame_prev_register (struct frame_info *this_frame, void **this_cache,
int regnum)
{
+ /* Copy all the registers unchanged. */
return frame_unwind_got_register (this_frame, regnum, regnum);
}
@@ -90,12 +130,22 @@ inline_frame_sniffer (const struct frame
struct block *frame_block, *cur_block;
int depth;
struct frame_info *next_frame;
+ struct inline_cache *inline_cache;
+
+ /* INLINE_FRAME unwinder is an exception, it is passed the regular unwinder
+ detected for this frame. */
+ gdb_assert (self != inline_frame_unwind);
this_pc = get_frame_address_in_block (this_frame);
frame_block = block_for_pc (this_pc);
if (frame_block == NULL)
return 0;
+ /* INLINE_SKIP_SYMBOL does not need to be set for each specific frame. But
+ * it needs to be recalculated after STEP_INTO_INLINE_FRAME according to new
+ * INLINE_SKIP_FRAMES - therefore SET_SKIPPED_INLINE_FRAMES is too early. */
+ inline_skip_symbol = NULL;
+
/* Calculate DEPTH, the number of inlined functions at this
location. */
depth = 0;
@@ -104,13 +154,27 @@ inline_frame_sniffer (const struct frame
{
if (block_inlined_p (cur_block))
depth++;
+ if (depth == inline_skip_frames && inline_skip_symbol == NULL)
+ inline_skip_symbol = BLOCK_FUNCTION (cur_block);
cur_block = BLOCK_SUPERBLOCK (cur_block);
}
+ /* There are inlined functions here. Check how many of them already have
+ frames. Any possibly inlined frames (inlined functions) behind
+ NORMAL_FRAME (non-inlined function) will already be at a different code
+ location and not found by BLOCK_FOR_PC at this PC moment. */
+ 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--;
+ }
+
/* Check whether we were requested to skip some frames, so they
can be stepped into later. */
- if (inline_skip_frames > 0 && frame_relative_level (this_frame) == 0)
+ if (inline_skip_frames > 0 && next_frame == NULL)
{
if (this_pc != inline_skip_pc)
inline_skip_frames = 0;
@@ -121,36 +185,30 @@ inline_frame_sniffer (const struct frame
}
}
- if (depth == 0)
- return 0;
-
- /* There are inlined functions here. Check how many of them 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 all the inlined functions already have frames, then pass to the
normal unwinder for this PC. */
if (depth == 0)
return 0;
+ /* Transparently wrap the former regular unwinder. */
+ inline_cache = frame_obstack_zalloc (sizeof (*inline_cache));
+ inline_cache->unwind_regular = self;
+ inline_cache->cache_regular = *this_cache;
+ *this_cache = inline_cache;
+
/* 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 = {
+static const struct frame_unwind inline_frame_unwinder = {
INLINE_FRAME,
inline_frame_this_id,
inline_frame_prev_register,
NULL,
- inline_frame_sniffer
+ inline_frame_sniffer,
+ inline_frame_dealloc
};
const struct frame_unwind *const inline_frame_unwind = {
@@ -189,7 +247,6 @@ set_skipped_inline_frames (int skip_ok)
{
CORE_ADDR this_pc;
struct block *frame_block, *cur_block;
- struct symbol *last_sym = NULL;
int skip_count = 0;
if (!skip_ok)
@@ -219,19 +276,23 @@ set_skipped_inline_frames (int skip_ok)
of BLOCK_START. */
if (BLOCK_START (cur_block) == this_pc
|| block_starting_point_at (this_pc, cur_block))
+ skip_count++;
+ else
{
- skip_count++;
- last_sym = BLOCK_FUNCTION (cur_block);
+ /* Here we will stop at any possible inlining after we
+ already crossed any first non-inlined function. We could
+ possibly detect such functions generating NORMAL_FRAME
+ by `!block_inlined_p && BLOCK_FUNCTION' (as
+ `!block_inlined_p && !BLOCK_FUNCTION' are just anonymous
+ local { ... } blocks). */
+ break;
}
- else
- break;
}
cur_block = BLOCK_SUPERBLOCK (cur_block);
}
}
inline_skip_pc = this_pc;
- inline_skip_symbol = last_sym;
if (inline_skip_frames != skip_count)
{
diff -up -u -X /home/jkratoch/.diffi.list -rupxCVS sources-codesourceryinline-gdb/testsuite/gdb.cp/annota2.exp sources/gdb/testsuite/gdb.cp/annota2.exp
--- sources-codesourceryinline-gdb/testsuite/gdb.cp/annota2.exp 2008-06-20 15:58:04.000000000 +0200
+++ sources/gdb/testsuite/gdb.cp/annota2.exp 2008-06-18 18:44:11.000000000 +0200
@@ -119,13 +119,14 @@ 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)" }
+ -re ".*$gdb_prompt$" { fail "continue until exit" }
+ timeout { fail "continue until exit (timeout)" }
}
#
diff -up -u -X /home/jkratoch/.diffi.list -rupxCVS sources-codesourceryinline-gdb/testsuite/gdb.opt/inline-bt.c sources/gdb/testsuite/gdb.opt/inline-bt.c
--- sources-codesourceryinline-gdb/testsuite/gdb.opt/inline-bt.c 2008-06-20 15:59:09.000000000 +0200
+++ sources/gdb/testsuite/gdb.opt/inline-bt.c 2008-06-22 23:56:29.000000000 +0200
@@ -15,7 +15,9 @@
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA. */
-int x, y;
+/* VOLATILE forces all the inlining to happen as otherwise the whole program
+ gets optimized by CSE to just simple assignments of the results. */
+volatile int x, y;
volatile int result;
void bar(void)
diff -up -u -X /home/jkratoch/.diffi.list -rupxCVS sources-codesourceryinline-gdb/testsuite/gdb.opt/inline-bt.exp sources/gdb/testsuite/gdb.opt/inline-bt.exp
--- sources-codesourceryinline-gdb/testsuite/gdb.opt/inline-bt.exp 2008-06-20 15:59:09.000000000 +0200
+++ sources/gdb/testsuite/gdb.opt/inline-bt.exp 2008-06-21 18:05:40.000000000 +0200
@@ -43,13 +43,14 @@ gdb_breakpoint $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 "info frame" ".*inlined into frame.*" "bar 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)"
+# gcc-4.3.1 omits the line number information for (2).
+#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.*" \
diff -up -u -X /home/jkratoch/.diffi.list -rupxCVS sources-codesourceryinline-gdb/testsuite/gdb.opt/inline-cmds.c sources/gdb/testsuite/gdb.opt/inline-cmds.c
--- sources-codesourceryinline-gdb/testsuite/gdb.opt/inline-cmds.c 2008-06-20 15:59:09.000000000 +0200
+++ sources/gdb/testsuite/gdb.opt/inline-cmds.c 2008-06-23 00:18:26.000000000 +0200
@@ -15,7 +15,9 @@
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA. */
-int x, y;
+/* VOLATILE forces all the inlining to happen as otherwise the whole program
+ gets optimized by CSE to just simple assignments of the results. */
+volatile int x, y;
volatile int result;
void bar(void)
@@ -23,6 +25,9 @@ void bar(void)
x += y; /* set breakpoint 1 here */
}
+#ifdef __GNUC__
+__attribute__((__noinline__))
+#endif
void marker(void)
{
x += y; /* set breakpoint 2 here */
@@ -44,6 +49,25 @@ inline void func3(void)
bar ();
}
+#ifdef __GNUC__
+__attribute__((__noinline__))
+#endif
+void noinline(void)
+{
+ bar (); /* inlined */
+}
+
+
+inline void outer_inline1(void)
+{
+ noinline ();
+}
+
+inline void outer_inline2(void)
+{
+ outer_inline1 ();
+}
+
int main (void)
{ /* start of main */
int val;
@@ -75,7 +99,9 @@ int main (void)
marker ();
func1 ();
func3 ();
- marker ();
+ marker (); /* set breakpoint 6 here */
+
+ outer_inline2 ();
return 0;
}
diff -up -u -X /home/jkratoch/.diffi.list -rupxCVS sources-codesourceryinline-gdb/testsuite/gdb.opt/inline-cmds.exp sources/gdb/testsuite/gdb.opt/inline-cmds.exp
--- sources-codesourceryinline-gdb/testsuite/gdb.opt/inline-cmds.exp 2008-06-20 15:59:09.000000000 +0200
+++ sources/gdb/testsuite/gdb.opt/inline-cmds.exp 2008-06-23 00:37:02.000000000 +0200
@@ -63,7 +63,7 @@ gdb_test "info frame" ".*inlined into fr
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"
+gdb_test "info frame" ".*\n called by frame.*" "marker not inlined"
# Next, check that we can next over inlined functions. We should not end up
# inside any of them.
@@ -235,3 +235,27 @@ gdb_test "finish" "func1 \\\(\\\);" "fin
gdb_test "step" "bar \\\(\\\);" "step into func1 for finish"
gdb_test "finish" "func3 \\\(\\\);" "finish from func1 to func3"
+
+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"
+gdb_test "bt" "#0 outer_inline1.*#1 outer_inline2.*#2 main.*" "backtrace at outer_inline1"
+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" "bar \\\(\\\) at .*" "enter bar from noinline"
+gdb_test "bt" "#0 bar.*#1 noinline.*#2 .*outer_inline1.*#3 .*outer_inline2.*#4 main.*" "backtrace at bar from noinline"
+gdb_test "info frame" ".*inlined into frame.*" "bar from noinline inlined"
+gdb_test "up" "#1 noinline.*" "up to noinline"
+gdb_test "info frame" ".*\n called by frame.*" "noinline from outer_inline1 not inlined"
+gdb_test "up" "#2 .*outer_inline1.*" "up to outer_inline1"
+gdb_test "info frame" ".*inlined into frame.*" "outer_inline1 inlined"
+gdb_test "up" "#3 .*outer_inline2.*" "up to outer_inline2"
+gdb_test "info frame" ".*inlined into frame.*" "outer_inline2 inlined"
+gdb_test "up" "#4 main.*" "up from outer_inline2"
+gdb_test "info frame" ".*\n caller of frame.*" "main not inlined"
diff -up -u -X /home/jkratoch/.diffi.list -rupxCVS sources-codesourceryinline-gdb/testsuite/gdb.opt/inline-locals.c sources/gdb/testsuite/gdb.opt/inline-locals.c
--- sources-codesourceryinline-gdb/testsuite/gdb.opt/inline-locals.c 2008-06-20 15:59:09.000000000 +0200
+++ sources/gdb/testsuite/gdb.opt/inline-locals.c 2008-06-22 23:56:42.000000000 +0200
@@ -15,7 +15,9 @@
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA. */
-int x, y;
+/* VOLATILE forces all the inlining to happen as otherwise the whole program
+ gets optimized by CSE to just simple assignments of the results. */
+volatile int x, y;
volatile int result;
volatile int *array_p;
diff -up -u -X /home/jkratoch/.diffi.list -rupxCVS sources-codesourceryinline-gdb/testsuite/gdb.opt/inline-locals.exp sources/gdb/testsuite/gdb.opt/inline-locals.exp
--- sources-codesourceryinline-gdb/testsuite/gdb.opt/inline-locals.exp 2008-06-20 15:59:09.000000000 +0200
+++ sources/gdb/testsuite/gdb.opt/inline-locals.exp 2008-06-23 00:07:43.000000000 +0200
@@ -53,7 +53,7 @@ if { ! $no_frames } {
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)"
- gdb_test "info args" "arg1 = $decimal" "info args above bar (2)"
+ gdb_test "info args" "arg1 = .*" "info args above bar (2)"
} else {
gdb_test "up" "#1 .*main .* at .*" "up from bar (2)"
gdb_test "info locals" ".*arg1 = 0.*" "info locals above bar (2)"
@@ -61,6 +61,9 @@ if { ! $no_frames } {
# Make sure that locals on the stack are found. This is an array to
# prevent it from living in a register.
+if [test_compiler_info "gcc-4-3-*"] {
+ setup_kfail *-*-* "gcc/debug.optimization"
+}
gdb_test "print array\[0\]" "\\\$$decimal = 0" "print local (2)"
if { ! $no_frames } {
@@ -79,11 +82,14 @@ if { ! $no_frames } {
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)"
- gdb_test "info args" "arg1 = $decimal" "info args above bar (3)"
+ gdb_test "info args" "arg1 = .*" "info args above bar (3)"
} 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)"
}
-
+
+if [test_compiler_info "gcc-4-3-*"] {
+ setup_kfail *-*-* "gcc/debug.optimization"
+}
gdb_test "print array\[0\]" "\\\$$decimal = 184" "print local (3)"
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch
2008-06-23 12:03 ` Jan Kratochvil
@ 2008-06-23 14:23 ` Daniel Jacobowitz
2008-07-02 19:15 ` Daniel Jacobowitz
1 sibling, 0 replies; 38+ messages in thread
From: Daniel Jacobowitz @ 2008-06-23 14:23 UTC (permalink / raw)
To: Jan Kratochvil; +Cc: gdb-patches
On Mon, Jun 23, 2008 at 01:54:22PM +0200, Jan Kratochvil wrote:
> Hi,
>
> here are some fixes on top of it. I find now your patch useful on top of CVS
> HEAD. I expect some of its problems were probably due to the patch separation
> from the CodeSourcery patchset.
Quite likely they're problems with our last merge. It worked well
relative to January's GDB but the merge from HEAD was only two weeks
ago and we haven't shaken all the bugs out of it yet.
Thanks for your changes! It'll take me a week or two to understand
them and then I'll try to figure out how to break up the patch for
HEAD...
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch
2008-06-23 12:03 ` Jan Kratochvil
2008-06-23 14:23 ` Daniel Jacobowitz
@ 2008-07-02 19:15 ` Daniel Jacobowitz
2008-07-03 11:22 ` [FYI] Inlining support, rough patch [break-by-function-name] Jan Kratochvil
2008-07-08 0:12 ` [FYI] Inlining support, rough patch Daniel Jacobowitz
1 sibling, 2 replies; 38+ messages in thread
From: Daniel Jacobowitz @ 2008-07-02 19:15 UTC (permalink / raw)
To: Jan Kratochvil; +Cc: gdb-patches
[-- Attachment #1: Type: text/plain, Size: 834 bytes --]
On Mon, Jun 23, 2008 at 01:54:22PM +0200, Jan Kratochvil wrote:
> Hi,
>
> here are some fixes on top of it. I find now your patch useful on top of CVS
> HEAD. I expect some of its problems were probably due to the patch separation
> from the CodeSourcery patchset.
>
> On x86+x86_64+ppc64 with gcc-4.3.1-2 (Fedora) there are no regressions.
> On x86+x86_64 with gcc-4.3.1-2 (Fedora) the provided tests PASS.
> (On ppc64 the tests FAIL as multi-PC breakpoints do not work there.)
Thanks for the patches. Here's a copy of your diff minus the parts
I've merged (or solved differently). I'm not planning to put in any
of these parts yet, but we can talk about them if you have examples
where they help.
Let's wait on that until the rest of the patch is ready, though.
I'm making good progress.
--
Daniel Jacobowitz
CodeSourcery
[-- Attachment #2: gdb-inline-works3.patch.left --]
[-- Type: text/plain, Size: 6003 bytes --]
diff -up -u -X /home/jkratoch/.diffi.list -rupxCVS sources-codesourceryinline-gdb/breakpoint.c sources/gdb/breakpoint.c
--- sources-codesourceryinline-gdb/breakpoint.c 2008-06-20 15:59:08.000000000 +0200
+++ sources/gdb/breakpoint.c 2008-06-22 23:45:57.000000000 +0200
@@ -3076,6 +3076,11 @@ bpstat_stop_status (CORE_ADDR bp_addr, p
bs->print = 0;
}
bs->commands = copy_command_lines (bs->commands);
+
+ /* We display the innermost inlined frame at a breakpont as it gives to
+ most of the available information. */
+ if (b->type != bp_until && b->type != bp_finish)
+ set_skipped_inline_frames (0);
}
/* Print nothing for this entry if we dont stop or if we dont print. */
diff -up -u -X /home/jkratoch/.diffi.list -rupxCVS sources-codesourceryinline-gdb/frame.c sources/gdb/frame.c
--- sources-codesourceryinline-gdb/frame.c 2008-06-20 15:59:09.000000000 +0200
+++ sources/gdb/frame.c 2008-06-22 18:01:02.000000000 +0200
@@ -422,12 +422,12 @@ 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
+ else if (l.inline_depth < r.inline_depth
&& l.stack_addr == r.stack_addr
&& l.code_addr_p == r.code_addr_p
- && l.code_addr == r.code_addr
+ && (!l.code_addr_p || l.code_addr == r.code_addr)
&& l.special_addr_p == r.special_addr_p
- && l.special_addr == r.special_addr)
+ && (!l.special_addr_p || l.special_addr == r.special_addr))
{
/* Same function, different inlined functions. */
struct block *lb, *rb;
@@ -1697,7 +1697,7 @@ find_frame_sal (struct frame_info *frame
sym = inline_skipped_symbol ();
init_sal (sal);
- if (SYMBOL_LINE (sym) != 0)
+ if (sym && SYMBOL_LINE (sym) != 0)
{
sal->symtab = SYMBOL_SYMTAB (sym);
sal->line = SYMBOL_LINE (sym);
diff -up -u -X /home/jkratoch/.diffi.list -rupxCVS sources-codesourceryinline-gdb/infcmd.c sources/gdb/infcmd.c
--- sources-codesourceryinline-gdb/infcmd.c 2008-06-20 15:59:09.000000000 +0200
+++ sources/gdb/infcmd.c 2008-06-17 20:57:03.000000000 +0200
@@ -758,7 +758,13 @@ step_1 (int skip_subroutines, int single
if (!target_can_async_p ())
{
for (; count > 0; count--)
- step_once (skip_subroutines, single_inst, count, thread);
+ {
+ step_once (skip_subroutines, single_inst, count, thread);
+
+ /* Unexpected breakpoint terminates our multistepping. */
+ if (!stop_step)
+ break;
+ }
do_cleanups (cleanups);
}
diff -up -u -X /home/jkratoch/.diffi.list -rupxCVS sources-codesourceryinline-gdb/inline-frame.c sources/gdb/inline-frame.c
--- sources-codesourceryinline-gdb/inline-frame.c 2008-06-20 15:59:09.000000000 +0200
+++ sources/gdb/inline-frame.c 2008-06-23 00:54:53.000000000 +0200
@@ -90,7 +130,12 @@ inline_frame_sniffer (const struct frame
frame_block = block_for_pc (this_pc);
if (frame_block == NULL)
return 0;
+ /* INLINE_SKIP_SYMBOL does not need to be set for each specific frame. But
+ * it needs to be recalculated after STEP_INTO_INLINE_FRAME according to new
+ * INLINE_SKIP_FRAMES - therefore SET_SKIPPED_INLINE_FRAMES is too early. */
+ inline_skip_symbol = NULL;
+
/* Calculate DEPTH, the number of inlined functions at this
location. */
depth = 0;
@@ -104,7 +154,9 @@ inline_frame_sniffer (const struct frame
{
if (block_inlined_p (cur_block))
depth++;
+ if (depth == inline_skip_frames && inline_skip_symbol == NULL)
+ inline_skip_symbol = BLOCK_FUNCTION (cur_block);
cur_block = BLOCK_SUPERBLOCK (cur_block);
}
@@ -189,7 +247,6 @@ set_skipped_inline_frames (int skip_ok)
{
CORE_ADDR this_pc;
struct block *frame_block, *cur_block;
- struct symbol *last_sym = NULL;
int skip_count = 0;
if (!skip_ok)
@@ -219,19 +276,23 @@ set_skipped_inline_frames (int skip_ok)
of BLOCK_START. */
if (BLOCK_START (cur_block) == this_pc
|| block_starting_point_at (this_pc, cur_block))
+ skip_count++;
+ else
{
- skip_count++;
- last_sym = BLOCK_FUNCTION (cur_block);
+ /* Here we will stop at any possible inlining after we
+ already crossed any first non-inlined function. We could
+ possibly detect such functions generating NORMAL_FRAME
+ by `!block_inlined_p && BLOCK_FUNCTION' (as
+ `!block_inlined_p && !BLOCK_FUNCTION' are just anonymous
+ local { ... } blocks). */
+ break;
}
- else
- break;
}
cur_block = BLOCK_SUPERBLOCK (cur_block);
}
}
inline_skip_pc = this_pc;
- inline_skip_symbol = last_sym;
if (inline_skip_frames != skip_count)
{
diff -up -u -X /home/jkratoch/.diffi.list -rupxCVS sources-codesourceryinline-gdb/testsuite/gdb.cp/annota2.exp sources/gdb/testsuite/gdb.cp/annota2.exp
--- sources-codesourceryinline-gdb/testsuite/gdb.cp/annota2.exp 2008-06-20 15:58:04.000000000 +0200
+++ sources/gdb/testsuite/gdb.cp/annota2.exp 2008-06-18 18:44:11.000000000 +0200
@@ -119,13 +119,14 @@ 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)" }
+ -re ".*$gdb_prompt$" { fail "continue until exit" }
+ timeout { fail "continue until exit (timeout)" }
}
#
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch [break-by-function-name]
2008-07-02 19:15 ` Daniel Jacobowitz
@ 2008-07-03 11:22 ` Jan Kratochvil
2008-07-03 16:01 ` Daniel Jacobowitz
2008-07-08 0:12 ` [FYI] Inlining support, rough patch Daniel Jacobowitz
1 sibling, 1 reply; 38+ messages in thread
From: Jan Kratochvil @ 2008-07-03 11:22 UTC (permalink / raw)
To: gdb-patches; +Cc: Daniel Jacobowitz
[-- Attachment #1: Type: text/plain, Size: 542 bytes --]
On Wed, 02 Jul 2008 21:14:38 +0200, Daniel Jacobowitz wrote:
...
> Let's wait on that until the rest of the patch is ready, though.
> I'm making good progress.
That would be great, while discussing the uncommitted patches here is one on
top of it to make `break inlined_function' work with the multi-PC breakpoints.
As you have to be aware of such solution it may have some drawbacks.
(It does not solve the multiple minimal symbols with the same name as was in:
http://sourceware.org/ml/gdb-patches/2008-05/msg00190.html
)
Regards,
Jan
[-- Attachment #2: gdb-inline-works3-byname.patch --]
[-- Type: text/plain, Size: 4345 bytes --]
diff -up -u -X /home/jkratoch/.diffi.list -rup sources-inline-works3-orig/gdb/ada-lang.c sources-inline-works3/gdb/ada-lang.c
--- sources-inline-works3-orig/gdb/ada-lang.c 2008-06-24 20:58:11.000000000 +0200
+++ sources-inline-works3/gdb/ada-lang.c 2008-06-26 15:29:33.000000000 +0200
@@ -4625,7 +4625,7 @@ remove_irrelevant_renamings (struct ada_
if (current_block == NULL)
return nsyms;
- current_function = block_linkage_function (current_block);
+ current_function = block_function (current_block);
if (current_function == NULL)
return nsyms;
@@ -6721,7 +6721,7 @@ ada_find_renaming_symbol (const char *na
static struct symbol *
find_old_style_renaming_symbol (const char *name, struct block *block)
{
- const struct symbol *function_sym = block_linkage_function (block);
+ const struct symbol *function_sym = block_function (block);
char *rename;
if (function_sym != NULL)
diff -up -u -X /home/jkratoch/.diffi.list -rup sources-inline-works3-orig/gdb/block.c sources-inline-works3/gdb/block.c
--- sources-inline-works3-orig/gdb/block.c 2008-06-24 20:58:11.000000000 +0200
+++ sources-inline-works3/gdb/block.c 2008-06-26 15:29:09.000000000 +0200
@@ -73,6 +73,19 @@ block_linkage_function (const struct blo
return BLOCK_FUNCTION (bl);
}
+/* Return the symbol for the function which contains a specified
+ lexical block, described by a struct block BL. Inlined functions
+ can be returned. */
+
+struct symbol *
+block_function (const struct block *bl)
+{
+ while (BLOCK_FUNCTION (bl) == NULL && BLOCK_SUPERBLOCK (bl) != NULL)
+ bl = BLOCK_SUPERBLOCK (bl);
+
+ return BLOCK_FUNCTION (bl);
+}
+
/* Return one if BLOCK represents an inlined function. */
int
diff -up -u -X /home/jkratoch/.diffi.list -rup sources-inline-works3-orig/gdb/block.h sources-inline-works3/gdb/block.h
--- sources-inline-works3-orig/gdb/block.h 2008-06-24 20:58:11.000000000 +0200
+++ sources-inline-works3/gdb/block.h 2008-06-26 15:29:20.000000000 +0200
@@ -137,6 +137,7 @@ struct blockvector
enum { GLOBAL_BLOCK = 0, STATIC_BLOCK = 1, FIRST_LOCAL_BLOCK = 2 };
extern struct symbol *block_linkage_function (const struct block *);
+extern struct symbol *block_function (const struct block *bl);
extern int block_inlined_p (const struct block *block);
diff -up -u -X /home/jkratoch/.diffi.list -rup sources-inline-works3-orig/gdb/blockframe.c sources-inline-works3/gdb/blockframe.c
--- sources-inline-works3-orig/gdb/blockframe.c 2008-06-24 20:58:25.000000000 +0200
+++ sources-inline-works3/gdb/blockframe.c 2008-06-26 15:34:44.000000000 +0200
@@ -157,7 +157,7 @@ find_pc_sect_function (CORE_ADDR pc, str
struct block *b = block_for_pc_sect (pc, section);
if (b == 0)
return 0;
- return block_linkage_function (b);
+ return block_function (b);
}
/* Return the function containing pc value PC.
diff -up -u -X /home/jkratoch/.diffi.list -rup sources-inline-works3-orig/gdb/breakpoint.c sources-inline-works3/gdb/breakpoint.c
--- sources-inline-works3-orig/gdb/breakpoint.c 2008-06-24 20:58:25.000000000 +0200
+++ sources-inline-works3/gdb/breakpoint.c 2008-06-26 15:28:35.000000000 +0200
@@ -5692,7 +5692,7 @@ resolve_sal_pc (struct symtab_and_line *
bv = blockvector_for_pc_sect (sal->pc, 0, &b, sal->symtab);
if (bv != NULL)
{
- sym = block_linkage_function (b);
+ sym = block_function (b);
if (sym != NULL)
{
fixup_symbol_section (sym, sal->symtab->objfile);
diff -up -u -X /home/jkratoch/.diffi.list -rup sources-inline-works3-orig/gdb/testsuite/gdb.opt/inline-cmds.exp sources-inline-works3/gdb/testsuite/gdb.opt/inline-cmds.exp
--- sources-inline-works3-orig/gdb/testsuite/gdb.opt/inline-cmds.exp 2008-06-24 20:58:25.000000000 +0200
+++ sources-inline-works3/gdb/testsuite/gdb.opt/inline-cmds.exp 2008-06-26 15:49:12.000000000 +0200
@@ -42,8 +42,10 @@ if { [skip_inline_frame_tests] } {
# 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"]
-gdb_breakpoint $line1
+# We test also inlining by the function name, otherwise we would use:
+# set line1 [gdb_get_line_number "set breakpoint 1 here"]
+# gdb_breakpoint $line1
+gdb_breakpoint "bar"
set line2 [gdb_get_line_number "set breakpoint 2 here"]
gdb_breakpoint $line2
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch [break-by-function-name]
2008-07-03 11:22 ` [FYI] Inlining support, rough patch [break-by-function-name] Jan Kratochvil
@ 2008-07-03 16:01 ` Daniel Jacobowitz
2008-07-12 7:41 ` Jan Kratochvil
0 siblings, 1 reply; 38+ messages in thread
From: Daniel Jacobowitz @ 2008-07-03 16:01 UTC (permalink / raw)
To: Jan Kratochvil; +Cc: gdb-patches
On Thu, Jul 03, 2008 at 01:21:48PM +0200, Jan Kratochvil wrote:
> On Wed, 02 Jul 2008 21:14:38 +0200, Daniel Jacobowitz wrote:
> ...
> > Let's wait on that until the rest of the patch is ready, though.
> > I'm making good progress.
>
> That would be great, while discussing the uncommitted patches here is one on
> top of it to make `break inlined_function' work with the multi-PC breakpoints.
> As you have to be aware of such solution it may have some drawbacks.
>
> (It does not solve the multiple minimal symbols with the same name as was in:
> http://sourceware.org/ml/gdb-patches/2008-05/msg00190.html
> )
I can't see how this works, unless you're getting lucky. For instance
here:
> @@ -5692,7 +5692,7 @@ resolve_sal_pc (struct symtab_and_line *
> bv = blockvector_for_pc_sect (sal->pc, 0, &b, sal->symtab);
> if (bv != NULL)
> {
> - sym = block_linkage_function (b);
> + sym = block_function (b);
> if (sym != NULL)
> {
> fixup_symbol_section (sym, sal->symtab->objfile);
fixup_symbol_section is a symbol lookup. There has to be a real
function in the ELF symbol table with the same name, and here that
may be in a different section entirely.
I think Apple kept a list of inlined instances attached to the main
symbol entry for the function. That's probably the way to go, but it
requires support for functions in the symbol table that don't have any
associated block (in case no copy is emitted out of line). I tried
that once and got tangled up; crashes everywhere.
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch
2008-07-02 19:15 ` Daniel Jacobowitz
2008-07-03 11:22 ` [FYI] Inlining support, rough patch [break-by-function-name] Jan Kratochvil
@ 2008-07-08 0:12 ` Daniel Jacobowitz
1 sibling, 0 replies; 38+ messages in thread
From: Daniel Jacobowitz @ 2008-07-08 0:12 UTC (permalink / raw)
To: Jan Kratochvil, gdb-patches
On Wed, Jul 02, 2008 at 03:14:38PM -0400, Daniel Jacobowitz wrote:
> Thanks for the patches. Here's a copy of your diff minus the parts
> I've merged (or solved differently). I'm not planning to put in any
> of these parts yet, but we can talk about them if you have examples
> where they help.
Sorry, I hadn't done enough testing. I've also got a version of the
infcmd.c change and your annota2.exp fix merged now.
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch [break-by-function-name]
2008-07-03 16:01 ` Daniel Jacobowitz
@ 2008-07-12 7:41 ` Jan Kratochvil
0 siblings, 0 replies; 38+ messages in thread
From: Jan Kratochvil @ 2008-07-12 7:41 UTC (permalink / raw)
To: gdb-patches; +Cc: Daniel Jacobowitz
On Thu, 03 Jul 2008 18:01:02 +0200, Daniel Jacobowitz wrote:
> On Thu, Jul 03, 2008 at 01:21:48PM +0200, Jan Kratochvil wrote:
> > On Wed, 02 Jul 2008 21:14:38 +0200, Daniel Jacobowitz wrote:
> > ...
> > > Let's wait on that until the rest of the patch is ready, though.
> > > I'm making good progress.
> >
> > That would be great, while discussing the uncommitted patches here is one on
> > top of it to make `break inlined_function' work with the multi-PC breakpoints.
...
> I can't see how this works, unless you're getting lucky.
The only risk is code in multiple sections - but for non-embedded targets all
the final linked regular code possibly containing inlined concrete instances
is in .text.
> For instance
> here:
> > @@ -5692,7 +5692,7 @@ resolve_sal_pc (struct symtab_and_line *
> > bv = blockvector_for_pc_sect (sal->pc, 0, &b, sal->symtab);
> > if (bv != NULL)
> > {
> > - sym = block_linkage_function (b);
> > + sym = block_function (b);
> > if (sym != NULL)
> > {
> > fixup_symbol_section (sym, sal->symtab->objfile);
>
> fixup_symbol_section is a symbol lookup. There has to be a real
> function in the ELF symbol table with the same name, and here that
> may be in a different section entirely.
This problem should be fixed since:
[commit] Handle minimal symbols pointing to function descriptors
http://sourceware.org/ml/gdb-patches/2008-05/msg00120.html
fixup_section():
- if (msym)
+ /* First, check whether a minimal symbol with the same name exists
+ and points to the same address. The address check is required
+ e.g. on PowerPC64, where the minimal symbol for a function will
+ point to the function descriptor, while the debug symbol will
+ point to the actual function code. */
+ if (msym
+ && SYMBOL_VALUE_ADDRESS (msym) == ginfo->value.address)
Which is followed by `ginfo->value.address' lookup in the section table.
Thanks for the problem notice,
Jan
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch
2008-06-13 19:39 [FYI] Inlining support, rough patch Daniel Jacobowitz
2008-06-13 19:43 ` Daniel Jacobowitz
2008-06-23 12:03 ` Jan Kratochvil
@ 2008-07-15 19:21 ` Daniel Jacobowitz
2008-07-17 23:53 ` Mark Kettenis
` (3 more replies)
2 siblings, 4 replies; 38+ messages in thread
From: Daniel Jacobowitz @ 2008-07-15 19:21 UTC (permalink / raw)
To: gdb-patches
This version of inlining support, which applies to trunk, is
thoroughly tested. I've cleaned it up considerably, and except for a
changelog entry (sorry) I consider it ready to commit.
Eli, when you have a chance, could you look at the NEWS / gdb.texinfo
changes? I've added a new section, though it is somewhat sparse at
present.
I'm looking for any comments on the patch itself, test coverage on
different systems (of both the patch and its included test cases), and
for people to try it and tell me whether it works naturally or if any
of my design choices seem wrong. I don't know of any bugs in the GDB
patch, although there are some limitations (see the new manual
chapter). Beware, though, it makes GDB more sensitive to incorrect
debug info from GCC.
All comments and testing very much welcome!
> There is a chapter in the patched gdb.texinfo discussing optimized
> code debugging, including inlined functions. Basically, they work
> in the most obvious ways I could think of.
>
> When you set a breakpoint at a PC or interrupt the program with
> Control-C, the current line of code is shown. Backtrace will list
> any functions inlined at this point. But when you step onto
> an inlined function call, the call site is shown; you have to "step"
> again to enter it. You can "next" over it, or "finish" out of it.
>
> Finish will not show the return value. There's no way to find it,
> and on top of that problem it's hard to be sure you've finished the
> inlined function, not just returned to another scheduled line of code
> from the caller.
>
> Breakpoints by name on inlined functions are not supported, but
> breakpoints by file and line work. Breakpoints at the call site of an
> inlined function may or may not work - sometimes they are moved to
> after the call. That's quite a challenging problem in its own right
> so I declined to address it at the same time.
--
Daniel Jacobowitz
CodeSourcery
---
gdb/Makefile.in | 22 -
gdb/NEWS | 3
gdb/block.c | 23 +
gdb/block.h | 4
gdb/blockframe.c | 36 +-
gdb/breakpoint.c | 81 ++--
gdb/buildsym.c | 6
gdb/doc/gdb.texinfo | 114 +++++-
gdb/dwarf2loc.c | 10
gdb/dwarf2read.c | 58 ++-
gdb/frame-unwind.c | 5
gdb/frame.c | 218 ++++++++++---
gdb/frame.h | 27 +
gdb/infcall.c | 7
gdb/infcmd.c | 152 +++++----
gdb/inferior.h | 2
gdb/infrun.c | 180 +++++++---
gdb/inline-frame.c | 382 +++++++++++++++++++++++
gdb/inline-frame.h | 62 +++
gdb/minsyms.c | 2
gdb/s390-tdep.c | 11
gdb/stack.c | 57 ++-
gdb/symtab.c | 134 +++++---
gdb/symtab.h | 16
gdb/testsuite/gdb.base/break.exp | 7
gdb/testsuite/gdb.cp/annota2.exp | 3
gdb/testsuite/gdb.opt/Makefile.in | 2
gdb/testsuite/gdb.opt/clobbered-registers-O2.exp | 5
gdb/testsuite/gdb.opt/inline-bt.c | 47 ++
gdb/testsuite/gdb.opt/inline-bt.exp | 63 +++
gdb/testsuite/gdb.opt/inline-cmds.c | 85 +++++
gdb/testsuite/gdb.opt/inline-cmds.exp | 279 ++++++++++++++++
gdb/testsuite/gdb.opt/inline-locals.c | 52 +++
gdb/testsuite/gdb.opt/inline-locals.exp | 118 +++++++
gdb/testsuite/gdb.opt/inline-markers.c | 36 ++
gdb/testsuite/lib/gdb.exp | 31 +
gdb/valops.c | 2
37 files changed, 2028 insertions(+), 314 deletions(-)
Index: src/gdb/Makefile.in
===================================================================
--- src.orig/gdb/Makefile.in 2008-07-15 15:10:56.000000000 -0400
+++ src/gdb/Makefile.in 2008-07-15 15:11:48.000000000 -0400
@@ -615,6 +615,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 \
@@ -822,6 +823,7 @@ inf_loop_h = inf-loop.h
inflow_h = inflow.h $(terminal_h)
inf_ptrace_h = inf-ptrace.h
inf_ttrace_h = inf-ttrace.h
+inline_frame_h = inline-frame.h
interps_h = interps.h $(exceptions_h)
jv_lang_h = jv-lang.h
language_h = language.h
@@ -1082,6 +1084,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 \
@@ -1965,7 +1968,7 @@ block.o: block.c $(defs_h) $(block_h) $(
blockframe.o: blockframe.c $(defs_h) $(symtab_h) $(bfd_h) $(objfiles_h) \
$(frame_h) $(gdbcore_h) $(value_h) $(target_h) $(inferior_h) \
$(annotate_h) $(regcache_h) $(gdb_assert_h) $(dummy_frame_h) \
- $(command_h) $(gdbcmd_h) $(block_h)
+ $(command_h) $(gdbcmd_h) $(block_h) $(inline_frame_h)
breakpoint.o: breakpoint.c $(defs_h) $(symtab_h) $(frame_h) $(breakpoint_h) \
$(gdbtypes_h) $(expression_h) $(gdbcore_h) $(gdbcmd_h) $(value_h) \
$(command_h) $(inferior_h) $(gdbthread_h) $(target_h) $(language_h) \
@@ -2084,13 +2087,13 @@ dwarf2-frame.o: dwarf2-frame.c $(defs_h)
dwarf2loc.o: dwarf2loc.c $(defs_h) $(ui_out_h) $(value_h) $(frame_h) \
$(gdbcore_h) $(target_h) $(inferior_h) $(ax_h) $(ax_gdb_h) \
$(regcache_h) $(objfiles_h) $(exceptions_h) $(elf_dwarf2_h) \
- $(dwarf2expr_h) $(dwarf2loc_h) $(gdb_string_h) $(gdb_assert_h)
+ $(dwarf2expr_h) $(dwarf2loc_h) $(gdb_string_h) $(gdb_assert_h) $(block_h)
dwarf2read.o: dwarf2read.c $(defs_h) $(bfd_h) $(symtab_h) $(gdbtypes_h) \
$(objfiles_h) $(elf_dwarf2_h) $(buildsym_h) $(demangle_h) \
$(expression_h) $(filenames_h) $(macrotab_h) $(language_h) \
$(complaints_h) $(bcache_h) $(dwarf2expr_h) $(dwarf2loc_h) \
$(cp_support_h) $(hashtab_h) $(command_h) $(gdbcmd_h) \
- $(addrmap_h) $(gdb_string_h) $(gdb_assert_h)
+ $(block_h) $(addrmap_h) $(gdb_string_h) $(gdb_assert_h)
elfread.o: elfread.c $(defs_h) $(bfd_h) $(gdb_string_h) $(elf_bfd_h) \
$(elf_mips_h) $(symtab_h) $(symfile_h) $(objfiles_h) $(buildsym_h) \
$(stabsread_h) $(gdb_stabs_h) $(complaints_h) $(demangle_h) \
@@ -2142,10 +2145,10 @@ frame.o: frame.c $(defs_h) $(frame_h) $(
$(gdb_obstack_h) $(dummy_frame_h) $(sentinel_frame_h) $(gdbcore_h) \
$(annotate_h) $(language_h) $(frame_unwind_h) $(frame_base_h) \
$(command_h) $(gdbcmd_h) $(observer_h) $(objfiles_h) $(exceptions_h) \
- $(gdbthread_h)
+ $(gdbthread_h) $(block_h) $(inline_frame_h)
frame-unwind.o: frame-unwind.c $(defs_h) $(frame_h) $(frame_unwind_h) \
$(gdb_assert_h) $(dummy_frame_h) $(gdb_obstack_h) $(value_h) \
- $(regcache_h)
+ $(regcache_h) $(inline_frame_h)
frv-linux-tdep.o: frv-linux-tdep.c $(defs_h) $(gdbcore_h) $(target_h) \
$(frame_h) $(osabi_h) $(regcache_h) $(elf_bfd_h) $(elf_frv_h) \
$(frv_tdep_h) $(trad_frame_h) $(frame_unwind_h) $(regset_h) \
@@ -2320,7 +2323,8 @@ infcmd.o: infcmd.c $(defs_h) $(gdb_strin
$(objfiles_h) $(completer_h) $(ui_out_h) $(event_top_h) \
$(parser_defs_h) $(regcache_h) $(reggroups_h) $(block_h) \
$(solib_h) $(gdb_assert_h) $(observer_h) $(target_descriptions_h) \
- $(user_regs_h) $(exceptions_h) $(cli_decode_h) $(gdbthread_h)
+ $(user_regs_h) $(exceptions_h) $(cli_decode_h) $(gdbthread_h) \
+ $(inline_frame_h)
inf-loop.o: inf-loop.c $(defs_h) $(inferior_h) $(target_h) $(event_loop_h) \
$(event_top_h) $(inf_loop_h) $(remote_h) $(exceptions_h) \
$(language_h) $(gdbthread_h)
@@ -2335,10 +2339,12 @@ infrun.o: infrun.c $(defs_h) $(gdb_strin
$(gdbcore_h) $(gdbcmd_h) $(cli_script_h) $(target_h) $(gdbthread_h) \
$(annotate_h) $(symfile_h) $(top_h) $(inf_loop_h) $(regcache_h) \
$(value_h) $(observer_h) $(language_h) $(solib_h) $(gdb_assert_h) \
- $(mi_common_h) $(main_h) $(event_top_h)
+ $(mi_common_h) $(main_h) $(event_top_h) $(inline_frame_h)
inf-ttrace.o: inf-ttrace.c $(defs_h) $(command_h) $(gdbcore_h) \
$(gdbthread_h) $(inferior_h) $(target_h) \
$(gdb_assert_h) $(gdb_string_h) $(inf_child_h) $(inf_ttrace_h)
+inline-frame.o: inline-frame.c $(defs_h) $(frame_unwind_h) $(block_h) \
+ $(symtab_h) $(addrmap_h) $(gdb_assert_h) $(vec_h) $(inferior_h)
interps.o: interps.c $(defs_h) $(gdbcmd_h) $(ui_out_h) $(event_loop_h) \
$(event_top_h) $(interps_h) $(completer_h) $(gdb_string_h) \
$(gdb_events_h) $(gdb_assert_h) $(top_h) $(exceptions_h)
@@ -2881,7 +2887,7 @@ stack.o: stack.c $(defs_h) $(value_h) $(
$(target_h) $(source_h) $(breakpoint_h) $(demangle_h) $(inferior_h) \
$(annotate_h) $(ui_out_h) $(block_h) $(stack_h) $(dictionary_h) \
$(exceptions_h) $(reggroups_h) $(regcache_h) $(solib_h) \
- $(valprint_h) $(gdb_assert_h) $(gdb_string_h)
+ $(valprint_h) $(gdb_assert_h) $(gdb_string_h) $(inline_frame_h)
std-regs.o: std-regs.c $(defs_h) $(user_regs_h) $(frame_h) $(gdbtypes_h) \
$(value_h) $(gdb_string_h)
symfile.o: symfile.c $(defs_h) $(bfdlink_h) $(symtab_h) $(gdbtypes_h) \
Index: src/gdb/block.c
===================================================================
--- src.orig/gdb/block.c 2008-07-15 15:11:43.000000000 -0400
+++ src/gdb/block.c 2008-07-15 15:11:48.000000000 -0400
@@ -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: src/gdb/block.h
===================================================================
--- src.orig/gdb/block.h 2008-07-15 15:11:43.000000000 -0400
+++ src/gdb/block.h 2008-07-15 15:11:48.000000000 -0400
@@ -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: src/gdb/blockframe.c
===================================================================
--- src.orig/gdb/blockframe.c 2008-07-15 15:11:43.000000000 -0400
+++ src/gdb/blockframe.c 2008-07-15 15:11:48.000000000 -0400
@@ -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);
}
\f
@@ -359,8 +383,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: src/gdb/breakpoint.c
===================================================================
--- src.orig/gdb/breakpoint.c 2008-07-15 15:11:43.000000000 -0400
+++ src/gdb/breakpoint.c 2008-07-15 15:11:48.000000000 -0400
@@ -2641,19 +2641,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.
@@ -2665,10 +2667,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. */
@@ -2938,7 +2939,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_stack_eq (b->frame_id, get_frame_id (get_current_frame ())))
bs->stop = 0;
else if (bs->stop)
{
@@ -2953,8 +2954,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),
@@ -5833,7 +5838,6 @@ watch_command_1 (char *arg, int accessfl
struct block *exp_valid_block;
struct value *val, *mark;
struct frame_info *frame;
- struct frame_info *prev_frame = NULL;
char *exp_start = NULL;
char *exp_end = NULL;
char *tok, *id_tok_start, *end_tok;
@@ -5967,34 +5971,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_caller_id (frame)))
+ {
+ scope_breakpoint
+ = create_internal_breakpoint (frame_unwind_caller_pc (frame),
+ bp_watchpoint_scope);
- scope_breakpoint->enable_state = bp_enabled;
+ scope_breakpoint->enable_state = bp_enabled;
- /* Automatically delete the breakpoint when it hits. */
- scope_breakpoint->disposition = disp_del;
+ /* Automatically delete the breakpoint when it hits. */
+ scope_breakpoint->disposition = disp_del;
- /* Only break in the proper frame (help with recursion). */
- scope_breakpoint->frame_id = get_frame_id (prev_frame);
+ /* Only break in the proper frame (help with recursion). */
+ scope_breakpoint->frame_id = frame_unwind_caller_id (frame);
- /* Set the address at which we will stop. */
- scope_breakpoint->loc->requested_address
- = get_frame_pc (prev_frame);
- scope_breakpoint->loc->address
- = adjust_breakpoint_address (scope_breakpoint->loc->requested_address,
- scope_breakpoint->type);
+ /* Set the address at which we will stop. */
+ scope_breakpoint->loc->requested_address
+ = frame_unwind_caller_pc (frame);
+ scope_breakpoint->loc->address
+ = adjust_breakpoint_address (scope_breakpoint->loc->requested_address,
+ scope_breakpoint->type);
+ }
}
/* Now set up the breakpoint. */
@@ -6175,7 +6179,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;
@@ -6217,11 +6220,13 @@ until_break_command (char *arg, int from
/* Keep within the current frame, or in frames called by the current
one. */
- if (prev_frame)
+
+ if (frame_id_p (frame_unwind_caller_id (frame)))
{
- sal = find_pc_line (get_frame_pc (prev_frame), 0);
- sal.pc = get_frame_pc (prev_frame);
- breakpoint2 = set_momentary_breakpoint (sal, get_frame_id (prev_frame),
+ sal = find_pc_line (frame_unwind_caller_pc (frame), 0);
+ sal.pc = frame_unwind_caller_pc (frame);
+ breakpoint2 = set_momentary_breakpoint (sal,
+ frame_unwind_caller_id (frame),
bp_until);
make_cleanup_delete_breakpoint (breakpoint2);
}
Index: src/gdb/buildsym.c
===================================================================
--- src.orig/gdb/buildsym.c 2008-07-15 15:10:56.000000000 -0400
+++ src/gdb/buildsym.c 2008-07-15 15:11:48.000000000 -0400
@@ -1171,6 +1171,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: src/gdb/doc/gdb.texinfo
===================================================================
--- src.orig/gdb/doc/gdb.texinfo 2008-07-15 15:10:57.000000000 -0400
+++ src/gdb/doc/gdb.texinfo 2008-07-15 15:11:48.000000000 -0400
@@ -146,6 +146,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
@@ -1803,7 +1804,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.
@@ -1812,22 +1813,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
@@ -7854,6 +7840,100 @@ $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} cannot 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 on the non-inlined copy of the function.
+
+@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.
+
+@item
+@value{GDBN} cannot locate the return value of inlined calls after
+using the @code{finish} command.
+
+@end itemize
+
+
@node Macros
@chapter C Preprocessor Macros
Index: src/gdb/dwarf2loc.c
===================================================================
--- src.orig/gdb/dwarf2loc.c 2008-07-15 15:10:56.000000000 -0400
+++ src/gdb/dwarf2loc.c 2008-07-15 15:11:48.000000000 -0400
@@ -31,6 +31,7 @@
#include "regcache.h"
#include "objfiles.h"
#include "exceptions.h"
+#include "block.h"
#include "elf/dwarf2.h"
#include "dwarf2expr.h"
@@ -146,14 +147,19 @@ 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,
something has gone wrong. */
gdb_assert (framefunc != NULL);
- if (SYMBOL_OPS (framefunc) == &dwarf2_loclist_funcs)
+ if (SYMBOL_LOCATION_BATON (framefunc) == NULL)
+ *start = NULL;
+ else if (SYMBOL_OPS (framefunc) == &dwarf2_loclist_funcs)
{
struct dwarf2_loclist_baton *symbaton;
struct frame_info *frame = debaton->frame;
Index: src/gdb/dwarf2read.c
===================================================================
--- src.orig/gdb/dwarf2read.c 2008-07-15 15:10:56.000000000 -0400
+++ src/gdb/dwarf2read.c 2008-07-15 15:11:48.000000000 -0400
@@ -45,6 +45,7 @@
#include "hashtab.h"
#include "command.h"
#include "gdbcmd.h"
+#include "block.h"
#include "addrmap.h"
#include <fcntl.h>
@@ -2713,12 +2714,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:
@@ -2941,12 +2938,27 @@ 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;
const char *previous_prefix = processing_current_prefix;
struct cleanup *back_to = NULL;
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));
@@ -6690,8 +6702,9 @@ die_is_declaration (struct die_info *die
&& dwarf2_attr (die, DW_AT_specification, cu) == NULL);
}
-/* Return the die giving the specification for DIE, if there is
- one. */
+/* Return the die giving the specification for DIE, if there is one.
+ If there is no specification, but there is an abstract origin, that
+ is returned. */
static struct die_info *
die_specification (struct die_info *die, struct dwarf2_cu *cu)
@@ -6699,6 +6712,9 @@ die_specification (struct die_info *die,
struct attribute *spec_attr = dwarf2_attr (die, DW_AT_specification, cu);
if (spec_attr == NULL)
+ spec_attr = dwarf2_attr (die, DW_AT_abstract_origin, cu);
+
+ if (spec_attr == NULL)
return NULL;
else
return follow_die_ref (die, spec_attr, cu);
@@ -7375,6 +7391,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));
@@ -7402,13 +7419,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);
@@ -7455,6 +7476,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
@@ -7502,7 +7531,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: src/gdb/frame-unwind.c
===================================================================
--- src.orig/gdb/frame-unwind.c 2008-07-15 15:10:56.000000000 -0400
+++ src/gdb/frame-unwind.c 2008-07-15 15:11:48.000000000 -0400
@@ -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: src/gdb/frame.c
===================================================================
--- src.orig/gdb/frame.c 2008-07-15 15:10:56.000000000 -0400
+++ src/gdb/frame.c 2008-07-15 15:11:48.000000000 -0400
@@ -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
@@ -173,6 +179,11 @@ 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, ",block=0x%s", paddr_nz (id.block_addr));
+ }
fprintf_unfiltered (file, "}");
}
@@ -187,6 +198,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;
@@ -239,6 +256,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. */
@@ -277,7 +306,7 @@ frame_unwind_caller_id (struct frame_inf
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));
+ return get_frame_id (get_prev_frame_1 (skip_inlined_frames (next_frame)));
}
const struct frame_id null_frame_id; /* All zeros. */
@@ -331,8 +360,8 @@ frame_id_p (struct frame_id l)
return p;
}
-int
-frame_id_eq (struct frame_id l, struct frame_id r)
+static int
+frame_id_eq_1 (struct frame_id l, struct frame_id r)
{
int eq;
if (!l.stack_addr_p || !r.stack_addr_p)
@@ -342,21 +371,52 @@ 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
/* Frames are equal. */
eq = 1;
- else
- /* No luck. */
+
+ return eq;
+}
+
+int
+frame_id_stack_eq (struct frame_id l, struct frame_id r)
+{
+ int eq = frame_id_eq_1 (l, r);
+
+ if (frame_debug)
+ {
+ fprintf_unfiltered (gdb_stdlog, "{ frame_id_stack_eq (l=");
+ fprint_frame_id (gdb_stdlog, l);
+ fprintf_unfiltered (gdb_stdlog, ",r=");
+ fprint_frame_id (gdb_stdlog, r);
+ fprintf_unfiltered (gdb_stdlog, ") -> %d }\n", eq);
+ }
+
+ return eq;
+}
+
+int
+frame_id_eq (struct frame_id l, struct frame_id r)
+{
+ int eq = frame_id_eq_1 (l, r);
+
+ if (l.inline_depth != r.inline_depth)
+ /* If inline depths are different, the frames must be different. */
eq = 0;
+ else if (l.block_addr != r.block_addr)
+ /* If the inlined block has a different start address, the frames
+ must be different. */
+ eq = 0;
+
if (frame_debug)
{
fprintf_unfiltered (gdb_stdlog, "{ frame_id_eq (l=");
@@ -365,6 +425,7 @@ frame_id_eq (struct frame_id l, struct f
fprint_frame_id (gdb_stdlog, r);
fprintf_unfiltered (gdb_stdlog, ") -> %d }\n", eq);
}
+
return eq;
}
@@ -375,6 +436,28 @@ 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.code_addr == r.code_addr
+ && l.special_addr_p == r.special_addr_p
+ && l.special_addr == r.special_addr)
+ {
+ /* Same function, different inlined functions. */
+ struct block *lb, *rb;
+
+ lb = block_for_pc (l.block_addr);
+ rb = block_for_pc (r.block_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
@@ -421,8 +504,8 @@ frame_find_by_id (struct frame_id id)
return NULL;
}
-CORE_ADDR
-frame_unwind_caller_pc (struct frame_info *this_frame)
+static CORE_ADDR
+frame_unwind_pc (struct frame_info *this_frame)
{
if (!this_frame->prev_pc.p)
{
@@ -461,6 +544,12 @@ frame_unwind_caller_pc (struct frame_inf
}
CORE_ADDR
+frame_unwind_caller_pc (struct frame_info *this_frame)
+{
+ return frame_unwind_pc (skip_inlined_frames (this_frame));
+}
+
+CORE_ADDR
get_frame_func (struct frame_info *this_frame)
{
struct frame_info *next_frame = this_frame->next;
@@ -1164,7 +1253,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;
@@ -1204,6 +1292,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. */
@@ -1272,7 +1368,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;
@@ -1301,6 +1398,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
@@ -1460,7 +1568,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
@@ -1506,8 +1614,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 (gdb_stdlog, this_frame, "inside entry func");
@@ -1518,7 +1627,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)
{
@@ -1533,7 +1643,7 @@ CORE_ADDR
get_frame_pc (struct frame_info *frame)
{
gdb_assert (frame->next != NULL);
- return frame_unwind_caller_pc (frame->next);
+ return frame_unwind_pc (frame->next);
}
/* Return an address that falls within THIS_FRAME's code block. */
@@ -1578,17 +1688,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
@@ -1598,15 +1749,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
@@ -1687,7 +1831,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: src/gdb/frame.h
===================================================================
--- src.orig/gdb/frame.h 2008-07-15 15:10:56.000000000 -0400
+++ src/gdb/frame.h 2008-07-15 15:11:48.000000000 -0400
@@ -127,6 +127,17 @@ 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;
+
+ /* A second code address, considered only if inline_depth != 0. The
+ block address lets GDB distinguish multiple functions inlined
+ into the same caller. This should be the first executed
+ instruction in the block corresponding to the inlined
+ function. */
+ CORE_ADDR block_addr;
};
/* Methods for constructing and comparing Frame IDs.
@@ -177,6 +188,11 @@ 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 and R identify frames associated with
+ the same underlying stack frame, although they may refer to
+ different inlined functions within the stack frame. */
+extern int frame_id_stack_eq (struct frame_id l, struct frame_id r);
+
/* 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);
@@ -203,6 +219,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,
@@ -696,14 +715,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: src/gdb/infcall.c
===================================================================
--- src.orig/gdb/infcall.c 2008-07-15 15:10:56.000000000 -0400
+++ src/gdb/infcall.c 2008-07-15 15:11:48.000000000 -0400
@@ -769,11 +769,8 @@ call_function_by_hand (struct value *fun
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: src/gdb/infcmd.c
===================================================================
--- src.orig/gdb/infcmd.c 2008-07-15 15:10:56.000000000 -0400
+++ src/gdb/infcmd.c 2008-07-15 15:11:48.000000000 -0400
@@ -51,6 +51,7 @@
#include "exceptions.h"
#include "cli/cli-decode.h"
#include "gdbthread.h"
+#include "inline-frame.h"
/* Functions exported for general use, in inferior.h: */
@@ -718,6 +719,17 @@ Can't resume all threads and specify pro
continue_1 (all_threads);
}
\f
+/* Record the starting point of a "step" or "next" command. */
+
+static void
+set_step_frame (void)
+{
+ struct symtab_and_line sal;
+
+ find_frame_sal (get_current_frame (), &sal);
+ set_step_info (get_frame_id (get_current_frame ()), sal);
+}
+
/* Step until outside of current statement. */
static void
@@ -795,65 +807,33 @@ 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--)
{
- clear_proceed_status ();
-
- frame = get_current_frame ();
- if (!frame) /* Avoid coredump here. Why tho? */
- error (_("No current frame"));
- step_frame_id = get_frame_id (frame);
-
- if (!single_inst)
- {
- find_pc_line_pc_range (stop_pc, &step_range_start, &step_range_end);
- if (step_range_end == 0)
- {
- char *name;
- if (find_pc_partial_function (stop_pc, &name, &step_range_start,
- &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 (!step_multi || !stop_step)
{
- /* Say we are stepping, but stop after one insn whatever it does. */
- step_range_start = step_range_end = 1;
- if (!skip_subroutines)
- /* It is stepi.
- Don't step over function calls, not even to functions lacking
- line numbers. */
- step_over_calls = STEP_OVER_NONE;
+ /* If we stopped for some reason that is not stepping
+ there are no further steps to make. */
+ step_multi = 0;
+ break;
}
-
- if (skip_subroutines)
- step_over_calls = STEP_OVER_ALL;
-
- step_multi = (count > 1);
- proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 1);
-
- if (!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);
@@ -901,27 +881,30 @@ 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)
{
clear_proceed_status ();
-
- frame = get_current_frame ();
- if (!frame) /* Avoid coredump here. Why tho? */
- error (_("No current frame"));
- step_frame_id = get_frame_id (frame);
+ set_step_frame ();
if (!single_inst)
{
- find_pc_line_pc_range (stop_pc, &step_range_start, &step_range_end);
-
- /* If we have no line info, switch to stepi mode. */
- if (step_range_end == 0 && step_stop_if_no_debug)
+ /* Step at an inlined function behaves like "down". */
+ if (!skip_subroutines && !single_inst
+ && inline_skipped_frames (inferior_ptid))
{
- step_range_start = step_range_end = 1;
+ 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;
}
- else if (step_range_end == 0)
+
+ find_pc_line_pc_range (stop_pc, &step_range_start, &step_range_end);
+
+ if (step_range_end == 0)
{
char *name;
if (find_pc_partial_function (stop_pc, &name, &step_range_start,
@@ -951,12 +934,21 @@ which has no line number information.\n"
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 (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 (step_1_continuation, args, xfree);
+ }
}
}
@@ -1141,14 +1133,12 @@ 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;
clear_proceed_status ();
-
- frame = get_current_frame ();
+ set_step_frame ();
/* Step until either exited from this function or greater
than the current line (if in symbolic section) or pc (if
@@ -1176,7 +1166,6 @@ until_next_command (int from_tty)
}
step_over_calls = STEP_OVER_ALL;
- step_frame_id = get_frame_id (frame);
step_multi = 0; /* Only one call to proceed */
@@ -1402,6 +1391,35 @@ finish_command (char *arg, int from_tty)
clear_proceed_status ();
+ /* Finishing from an inline frame is completely different. We don't
+ try to show the "return value" - no way to locate it. So we do
+ not need a completion. */
+ if (get_frame_type (get_selected_frame (_("No selected frame.")))
+ == INLINE_FRAME)
+ {
+ /* Claim we are stepping in the calling frame. An empty step
+ range means that we will stop once we aren't in a function
+ called by that frame. We don't use the magic "1" value for
+ step_range_end, because then infrun will think this is nexti,
+ and not step over the rest of this inlined function call. */
+ struct symtab_and_line empty_sal;
+ init_sal (&empty_sal);
+ set_step_info (get_frame_id (frame), empty_sal);
+ step_range_start = step_range_end = get_frame_pc (frame);
+ 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;
+ }
+
sal = find_pc_line (get_frame_pc (frame), 0);
sal.pc = get_frame_pc (frame);
Index: src/gdb/inferior.h
===================================================================
--- src.orig/gdb/inferior.h 2008-07-15 15:10:56.000000000 -0400
+++ src/gdb/inferior.h 2008-07-15 15:11:48.000000000 -0400
@@ -251,6 +251,8 @@ extern void error_is_running (void);
/* Calls error_is_running if the current thread is running. */
extern void ensure_not_running (void);
+void set_step_info (struct frame_id id, struct symtab_and_line sal);
+
/* From infcmd.c */
extern void tty_command (char *, int);
Index: src/gdb/infrun.c
===================================================================
--- src.orig/gdb/infrun.c 2008-07-15 15:10:56.000000000 -0400
+++ src/gdb/infrun.c 2008-07-15 15:11:48.000000000 -0400
@@ -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. */
@@ -295,8 +296,12 @@ struct thread_stepping_state
/* Should we step over breakpoint next time keep_going
is called? */
int stepping_over_breakpoint;
+
+ /* 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;
+
int step_after_step_resume_breakpoint;
int stepping_through_solib_after_catch;
bpstat stepping_through_solib_catchpoints;
@@ -1082,6 +1087,8 @@ a command like `return' or `jump' to con
step = 0;
}
+ clear_inline_frame_state (resume_ptid);
+
if (debug_displaced
&& use_displaced_stepping (gdbarch)
&& stepping_over_breakpoint)
@@ -1390,6 +1397,9 @@ init_wait_for_inferior (void)
init_infwait_state ();
displaced_step_clear ();
+
+ /* Discard any skipped inlined frames. */
+ clear_inline_frame_state (minus_one_ptid);
}
\f
@@ -1439,7 +1449,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);
@@ -1593,10 +1603,19 @@ fetch_inferior_event (void *client_data)
display_gdb_prompt (0);
}
+/* Record the frame and location we're currently stepping through. */
+void
+set_step_info (struct frame_id id, struct symtab_and_line sal)
+{
+ step_frame_id = id;
+ tss->current_symtab = sal.symtab;
+ tss->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;
@@ -1607,16 +1626,10 @@ init_execution_control_state (struct exe
void
init_thread_stepping_state (struct thread_stepping_state *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 (prev_pc, 0);
- tss->current_line = sal.line;
- tss->current_symtab = sal.symtab;
}
/* Return the cached copy of the last pid/waitstatus returned by
@@ -1805,6 +1818,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. */
@@ -2463,6 +2492,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 (step_range_end != 1)
+ skip_inline_frames (ecs->ptid);
+
if (stop_signal == TARGET_SIGNAL_TRAP
&& stepping_over_breakpoint
&& gdbarch_single_step_through_delay_p (current_gdbarch)
@@ -2678,8 +2713,8 @@ process_event_stop_test:
if (step_range_end != 0
&& stop_signal != TARGET_SIGNAL_0
&& stop_pc >= step_range_start && stop_pc < step_range_end
- && frame_id_eq (get_frame_id (get_current_frame ()),
- step_frame_id)
+ && frame_id_stack_eq (get_frame_id (get_current_frame ()),
+ step_frame_id)
&& step_resume_breakpoint == NULL)
{
/* The inferior is about to take a signal that will take it
@@ -3014,12 +3049,7 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (
until we exit the run time loader code and reach the callee's
address. */
if (step_over_calls == STEP_OVER_UNDEBUGGABLE
-#ifdef IN_SOLIB_DYNSYM_RESOLVE_CODE
- && IN_SOLIB_DYNSYM_RESOLVE_CODE (stop_pc)
-#else
- && in_solib_dynsym_resolve_code (stop_pc)
-#endif
- )
+ && in_solib_dynsym_resolve_code (stop_pc))
{
CORE_ADDR pc_after_resolver =
gdbarch_skip_solib_resolver (current_gdbarch, stop_pc);
@@ -3063,12 +3093,12 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (
previous frame's ID is sufficient - but it is a common case and
cheaper than checking the previous frame's ID.
- NOTE: frame_id_eq will never report two invalid frame IDs as
+ NOTE: frame_id_stack_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 ()), step_frame_id)
- && frame_id_eq (frame_unwind_caller_id (get_current_frame ()),
- step_frame_id))
+ if (!frame_id_stack_eq (get_frame_id (get_current_frame ()), step_frame_id)
+ && frame_id_stack_eq (frame_unwind_caller_id (get_current_frame ()),
+ step_frame_id))
{
CORE_ADDR real_stop_pc;
@@ -3113,13 +3143,7 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (
if (real_stop_pc != 0)
ecs->stop_func_start = real_stop_pc;
- if (
-#ifdef IN_SOLIB_DYNSYM_RESOLVE_CODE
- IN_SOLIB_DYNSYM_RESOLVE_CODE (ecs->stop_func_start)
-#else
- in_solib_dynsym_resolve_code (ecs->stop_func_start)
-#endif
-)
+ if (real_stop_pc != 0 && in_solib_dynsym_resolve_code (real_stop_pc))
{
struct symtab_and_line sr_sal;
init_sal (&sr_sal);
@@ -3267,6 +3291,79 @@ 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 ()), 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 (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 == tss->current_line
+ && call_sal.symtab == tss->current_symtab)
+ step_into_inline_frame (ecs->ptid);
+
+ 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 == tss->current_line
+ && call_sal.symtab == tss->current_symtab)
+ keep_going (ecs);
+ else
+ {
+ 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 ()), step_frame_id)
+ && stepped_in_from (get_current_frame (), step_frame_id))
+ {
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: stepping through inlined function\n");
+
+ if (step_over_calls == STEP_OVER_ALL)
+ keep_going (ecs);
+ else
+ {
+ stop_step = 1;
+ print_stop_reason (END_STEPPING_RANGE, 0);
+ stop_stepping (ecs);
+ }
+ return;
+ }
+
if ((stop_pc == stop_pc_sal.pc)
&& (tss->current_line != stop_pc_sal.line
|| tss->current_symtab != stop_pc_sal.symtab))
@@ -3292,13 +3389,11 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (
step_range_start = stop_pc_sal.pc;
step_range_end = stop_pc_sal.end;
- step_frame_id = get_frame_id (get_current_frame ());
- tss->current_line = stop_pc_sal.line;
- tss->current_symtab = stop_pc_sal.symtab;
+ set_step_info (get_frame_id (get_current_frame ()), stop_pc_sal);
/* In the case where we just stepped out of a function into the
middle of a line of the caller, continue stepping, but
- step_frame_id must be modified to current frame */
+ step_frame_id must be modified to current frame (above). */
#if 0
/* NOTE: cagney/2003-10-16: I think this frame ID inner test is too
generous. It will trigger on things like a step into a frameless
@@ -3315,13 +3410,6 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (
and we're willing to introduce frame unwind logic into this
function. Fortunately, those days are nearly upon us. */
#endif
- {
- struct frame_info *frame = get_current_frame ();
- struct frame_id current_frame = get_frame_id (frame);
- if (!(frame_id_inner (get_frame_arch (frame), current_frame,
- step_frame_id)))
- step_frame_id = current_frame;
- }
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: keep going\n");
@@ -3767,17 +3855,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 ())
@@ -3817,7 +3894,6 @@ Further execution is probably impossible
if (!target_has_stack)
{
-
goto done;
}
@@ -3866,7 +3942,7 @@ Further execution is probably impossible
should) use that when doing a frame comparison. */
if (stop_step
&& frame_id_eq (step_frame_id,
- get_frame_id (get_current_frame ()))
+ get_frame_id (get_selected_frame (NULL)))
&& step_start_function == find_pc_function (stop_pc))
source_flag = SRC_LINE; /* finished step, just print source line */
else
Index: src/gdb/inline-frame.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/inline-frame.c 2008-07-15 15:11:48.000000000 -0400
@@ -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).block_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: src/gdb/inline-frame.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/inline-frame.h 2008-07-15 15:11:48.000000000 -0400
@@ -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: src/gdb/minsyms.c
===================================================================
--- src.orig/gdb/minsyms.c 2008-07-15 15:10:56.000000000 -0400
+++ src/gdb/minsyms.c 2008-07-15 15:11:48.000000000 -0400
@@ -761,7 +761,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: src/gdb/s390-tdep.c
===================================================================
--- src.orig/gdb/s390-tdep.c 2008-07-15 15:10:57.000000000 -0400
+++ src/gdb/s390-tdep.c 2008-07-15 15:11:48.000000000 -0400
@@ -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: src/gdb/stack.c
===================================================================
--- src.orig/gdb/stack.c 2008-07-15 15:10:57.000000000 -0400
+++ src/gdb/stack.c 2008-07-15 15:11:48.000000000 -0400
@@ -44,6 +44,7 @@
#include "solib.h"
#include "valprint.h"
#include "gdbthread.h"
+#include "inline-frame.h"
#include "gdb_assert.h"
#include <ctype.h>
@@ -97,6 +98,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
@@ -533,7 +558,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,
@@ -587,7 +612,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
@@ -608,8 +633,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)
@@ -675,7 +705,7 @@ print_frame (struct frame_info *frame, i
frame_relative_level (frame));
}
if (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 ();
@@ -1005,8 +1035,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)),
@@ -1467,7 +1499,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);
@@ -1538,7 +1572,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);
@@ -1805,6 +1841,9 @@ return_command (char *retval_exp, int fr
thisfun = get_frame_function (get_selected_frame ("No selected frame."));
+ 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: src/gdb/symtab.c
===================================================================
--- src.orig/gdb/symtab.c 2008-07-15 15:10:57.000000000 -0400
+++ src/gdb/symtab.c 2008-07-15 15:11:48.000000000 -0400
@@ -1367,10 +1367,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;
}
@@ -2596,6 +2599,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);
@@ -2634,6 +2638,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_BFD_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;
}
@@ -3636,6 +3659,24 @@ language_search_unquoted_string (char *t
return p;
}
+static void
+completion_list_add_fields (struct symbol *sym, char *sym_text,
+ int sym_text_len, char *text, char *word)
+{
+ if (SYMBOL_CLASS (sym) == LOC_TYPEDEF)
+ {
+ struct type *t = SYMBOL_TYPE (sym);
+ enum type_code c = TYPE_CODE (t);
+ int j;
+
+ if (c == TYPE_CODE_UNION || c == TYPE_CODE_STRUCT)
+ for (j = TYPE_N_BASECLASSES (t); j < TYPE_NFIELDS (t); j++)
+ if (TYPE_FIELD_NAME (t, j))
+ completion_list_add_name (TYPE_FIELD_NAME (t, j),
+ sym_text, sym_text_len, text, word);
+ }
+}
+
char **
default_make_symbol_completion_list (char *text, char *word)
{
@@ -3648,9 +3689,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;
@@ -3760,41 +3801,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. */
@@ -3813,9 +3856,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);
@@ -4194,6 +4234,7 @@ skip_prologue_using_sal (CORE_ADDR func_
struct symtab_and_line prologue_sal;
CORE_ADDR start_pc;
CORE_ADDR end_pc;
+ struct block *bl;
/* Get an initial range for the function. */
find_pc_partial_function (func_addr, NULL, &start_pc, &end_pc);
@@ -4218,6 +4259,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: src/gdb/symtab.h
===================================================================
--- src.orig/gdb/symtab.h 2008-07-15 15:10:57.000000000 -0400
+++ src/gdb/symtab.h 2008-07-15 15:11:48.000000000 -0400
@@ -568,9 +568,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;
@@ -601,6 +610,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: src/gdb/testsuite/gdb.base/break.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/break.exp 2008-07-15 15:10:57.000000000 -0400
+++ src/gdb/testsuite/gdb.base/break.exp 2008-07-15 15:11:48.000000000 -0400
@@ -883,6 +883,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: src/gdb/testsuite/gdb.cp/annota2.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.cp/annota2.exp 2008-07-15 15:10:57.000000000 -0400
+++ src/gdb/testsuite/gdb.cp/annota2.exp 2008-07-15 15:11:48.000000000 -0400
@@ -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: src/gdb/testsuite/gdb.opt/Makefile.in
===================================================================
--- src.orig/gdb/testsuite/gdb.opt/Makefile.in 2008-07-15 15:10:57.000000000 -0400
+++ src/gdb/testsuite/gdb.opt/Makefile.in 2008-07-15 15:11:48.000000000 -0400
@@ -1,7 +1,7 @@
VPATH = @srcdir@
srcdir = @srcdir@
-EXECUTABLES = hello/hello
+EXECUTABLES = hello/hello inline-bt
MISCELLANEOUS =
Index: src/gdb/testsuite/gdb.opt/clobbered-registers-O2.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.opt/clobbered-registers-O2.exp 2008-07-15 15:10:57.000000000 -0400
+++ src/gdb/testsuite/gdb.opt/clobbered-registers-O2.exp 2008-07-15 15:11:48.000000000 -0400
@@ -2,7 +2,7 @@
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
+# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
@@ -11,8 +11,7 @@
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# This file is part of the gdb testsuite.
Index: src/gdb/testsuite/gdb.opt/inline-bt.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.opt/inline-bt.c 2008-07-15 15:11:48.000000000 -0400
@@ -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: src/gdb/testsuite/gdb.opt/inline-bt.exp
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.opt/inline-bt.exp 2008-07-15 15:11:48.000000000 -0400
@@ -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: src/gdb/testsuite/gdb.opt/inline-cmds.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.opt/inline-cmds.c 2008-07-15 15:11:48.000000000 -0400
@@ -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: src/gdb/testsuite/gdb.opt/inline-cmds.exp
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.opt/inline-cmds.exp 2008-07-15 15:11:48.000000000 -0400
@@ -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: src/gdb/testsuite/gdb.opt/inline-locals.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.opt/inline-locals.c 2008-07-15 15:11:48.000000000 -0400
@@ -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: src/gdb/testsuite/gdb.opt/inline-locals.exp
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.opt/inline-locals.exp 2008-07-15 15:11:48.000000000 -0400
@@ -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: src/gdb/testsuite/gdb.opt/inline-markers.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.opt/inline-markers.c 2008-07-15 15:11:48.000000000 -0400
@@ -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: src/gdb/testsuite/lib/gdb.exp
===================================================================
--- src.orig/gdb/testsuite/lib/gdb.exp 2008-07-15 15:10:57.000000000 -0400
+++ src/gdb/testsuite/lib/gdb.exp 2008-07-15 15:11:48.000000000 -0400
@@ -1358,6 +1358,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: src/gdb/valops.c
===================================================================
--- src.orig/gdb/valops.c 2008-07-15 15:10:57.000000000 -0400
+++ src/gdb/valops.c 2008-07-15 15:11:48.000000000 -0400
@@ -974,7 +974,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: src/gdb/NEWS
===================================================================
--- src.orig/gdb/NEWS 2008-07-15 15:15:22.000000000 -0400
+++ src/gdb/NEWS 2008-07-15 15:16:05.000000000 -0400
@@ -17,6 +17,9 @@ For instance, consider:
If the user types TAB at the end of this command line, the available
completions will be "f1" and "f2".
+* Inlined functions are now supported. They show up in backtraces, and
+the "step", "next", and "finish" commands handle them automatically.
+
* New remote packets
qSearch:memory:
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch
2008-07-15 19:21 ` Daniel Jacobowitz
@ 2008-07-17 23:53 ` Mark Kettenis
2008-07-18 13:03 ` Daniel Jacobowitz
2008-07-18 2:02 ` Paul Pluzhnikov
` (2 subsequent siblings)
3 siblings, 1 reply; 38+ messages in thread
From: Mark Kettenis @ 2008-07-17 23:53 UTC (permalink / raw)
To: drow; +Cc: gdb-patches
> Date: Tue, 15 Jul 2008 15:20:20 -0400
> From: Daniel Jacobowitz <drow@false.org>
>
> I'm looking for any comments on the patch itself, test coverage on
> different systems (of both the patch and its included test cases), and
> for people to try it and tell me whether it works naturally or if any
> of my design choices seem wrong. I don't know of any bugs in the GDB
> patch, although there are some limitations (see the new manual
> chapter). Beware, though, it makes GDB more sensitive to incorrect
> debug info from GCC.
OK, I've looked a bit more at this diff, but I don't think I
completely understand it yet. I really, really, don't like the way
you turn the frame unwinding completely upside down though. Im afraid
that to me, thist suggests that your approach is seriously flawed.
Another indication is that you seem to need state for the inline
unwinder.
I can see why it would be desirable to have inlined functions appear
in a backtrace. And I don't necessarily disagree with doing this
through inlined frames. While they're not real stack frames, they're
not too different from frames we create for leaf calls that don't have
a stack frame of their own. The important question is what the frame
ID for such a frame should be. It seems you use the stack address and
entry point of the function in which the code was inlined. As a
consequence you need to extend the frame ID to distinguish it from
that surrounding frame. I think the correct thing to do is to use the
code address (low address or entry point) of the inlined function and
perhaps the stack pointer at that point to construct the frame ID.
I think this is doable if the inline unwinder is integrated with the
dwarf2 unwinder, or at least shares code with it. That way the
generic frame unwinding code can stay much the way it is. These
frames probably need to be marked and the unwinding code probably
needs a way to skip them. But that will only have a fairly limited
impact.
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch
2008-07-15 19:21 ` Daniel Jacobowitz
2008-07-17 23:53 ` Mark Kettenis
@ 2008-07-18 2:02 ` Paul Pluzhnikov
2008-07-18 3:07 ` Daniel Jacobowitz
2008-07-20 14:41 ` Eli Zaretskii
2008-07-25 13:54 ` Eli Zaretskii
3 siblings, 1 reply; 38+ messages in thread
From: Paul Pluzhnikov @ 2008-07-18 2:02 UTC (permalink / raw)
To: gdb-patches
On Tue, Jul 15, 2008 at 12:20 PM, Daniel Jacobowitz <drow@false.org> wrote:
> This version of inlining support, which applies to trunk, is
> thoroughly tested.
I've had some trouble applying this patch to current CVS Head,
and the patch appears to be incomplete, especially WRT renaming
frame_pc_unwind() -> frame_unwind_caller_pc().
[I do not see prototype for frame_unwind_caller_pc() being added
to frame.h in the patch.]
I think I resolved errors correctly.
Running tests now ...
--
Paul Pluzhnikov
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch
2008-07-18 2:02 ` Paul Pluzhnikov
@ 2008-07-18 3:07 ` Daniel Jacobowitz
0 siblings, 0 replies; 38+ messages in thread
From: Daniel Jacobowitz @ 2008-07-18 3:07 UTC (permalink / raw)
To: Paul Pluzhnikov; +Cc: gdb-patches
On Thu, Jul 17, 2008 at 07:01:42PM -0700, Paul Pluzhnikov wrote:
> On Tue, Jul 15, 2008 at 12:20 PM, Daniel Jacobowitz <drow@false.org> wrote:
>
> > This version of inlining support, which applies to trunk, is
> > thoroughly tested.
>
> I've had some trouble applying this patch to current CVS Head,
> and the patch appears to be incomplete, especially WRT renaming
> frame_pc_unwind() -> frame_unwind_caller_pc().
>
> [I do not see prototype for frame_unwind_caller_pc() being added
> to frame.h in the patch.]
You need the previous patch, which I applied and then reverted:
http://sourceware.org/ml/gdb-patches/2008-07/msg00317.html
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch
2008-07-17 23:53 ` Mark Kettenis
@ 2008-07-18 13:03 ` Daniel Jacobowitz
[not found] ` <200807251446.m6PEkfwc027635@brahms.sibelius.xs4all.nl>
0 siblings, 1 reply; 38+ messages in thread
From: Daniel Jacobowitz @ 2008-07-18 13:03 UTC (permalink / raw)
To: Mark Kettenis; +Cc: gdb-patches
Thanks for looking at this. I'll turn this conversation into internals
documentation later, no matter what version we end up with.
On Fri, Jul 18, 2008 at 01:53:01AM +0200, Mark Kettenis wrote:
> I can see why it would be desirable to have inlined functions appear
> in a backtrace. And I don't necessarily disagree with doing this
> through inlined frames. While they're not real stack frames, they're
> not too different from frames we create for leaf calls that don't have
> a stack frame of their own. The important question is what the frame
> ID for such a frame should be. It seems you use the stack address and
> entry point of the function in which the code was inlined. As a
> consequence you need to extend the frame ID to distinguish it from
> that surrounding frame. I think the correct thing to do is to use the
> code address (low address or entry point) of the inlined function and
> perhaps the stack pointer at that point to construct the frame ID.
I don't think I turned unwinding upside down; in fact I think the
unwinder changes are fairly unintrusive, but that may be because I saw
the earlier versions of this patch... The only substantive change is
the short-circuit test in get_prev_frame_1 to accomodate the
upside-down ID construction, which is itself isolated to inline
frames. Are there other specific changes that you're worried about,
or is it just the ID construction?
As for those IDs, I think they're necessary. I'd be delighted to be
wrong, so I'll explain the new fields and see if we can find a way to
be rid of them.
The inline_depth field is necessary because more than one function
can begin at a given instruction. For example:
volatile int x;
int inner (void)
{
return x;
}
int mid (void)
{
return inner();
}
int outer (void)
{
return mid();
}
No code will be generated for mid that doesn't come from inner, so
there's no code address that can be used to distinguish them.
The separate block address and code address, and the use of the
enclosing function's stack address, are necessary because of
frame_id_stack_eq. Given two frame IDs, there are several places we
must be able to find out if they belong to the same real stack frame.
I've just eliminated the separate block_addr; we can use code_addr
instead, with a few changes elsewhere - including, happily, the
elimination of frame_id_stack_eq. That removes the requirement
for the inlined frame's ID to have the same stack address as the
calling frame, though it still needs a stable stack address.
> I think this is doable if the inline unwinder is integrated with the
> dwarf2 unwinder, or at least shares code with it. That way the
> generic frame unwinding code can stay much the way it is. These
> frames probably need to be marked and the unwinding code probably
> needs a way to skip them. But that will only have a fairly limited
> impact.
That would make inlining dependent on .debug_frame. But I don't see
why it should be. We get inlining from .debug_info instead, and
conceptually there's no reason we couldn't get it from another debug
format.
While inlined frames no longer need to have the same stack address as
their caller, in my latest version, they do still need a stable stack
address. The only ways I see to get one are to unwind to the caller
and ask it, or to hard-code and enforce a requirement that there be
.debug_frame and ask the dwarf2 unwinder directly. The second seems
like a special case of the first, so I'm missing the advantage.
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch
2008-07-15 19:21 ` Daniel Jacobowitz
2008-07-17 23:53 ` Mark Kettenis
2008-07-18 2:02 ` Paul Pluzhnikov
@ 2008-07-20 14:41 ` Eli Zaretskii
2008-07-25 13:54 ` Eli Zaretskii
3 siblings, 0 replies; 38+ messages in thread
From: Eli Zaretskii @ 2008-07-20 14:41 UTC (permalink / raw)
To: Daniel Jacobowitz; +Cc: gdb-patches
> Date: Tue, 15 Jul 2008 15:20:20 -0400
> From: Daniel Jacobowitz <drow@false.org>
>
> Eli, when you have a chance, could you look at the NEWS / gdb.texinfo
> changes?
Will do, in about a week from now.
Thanks.
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch
2008-07-15 19:21 ` Daniel Jacobowitz
` (2 preceding siblings ...)
2008-07-20 14:41 ` Eli Zaretskii
@ 2008-07-25 13:54 ` Eli Zaretskii
2008-07-25 14:26 ` Daniel Jacobowitz
3 siblings, 1 reply; 38+ messages in thread
From: Eli Zaretskii @ 2008-07-25 13:54 UTC (permalink / raw)
To: Daniel Jacobowitz; +Cc: gdb-patches
> Date: Tue, 15 Jul 2008 15:20:20 -0400
> From: Daniel Jacobowitz <drow@false.org>
>
> Eli, when you have a chance, could you look at the NEWS / gdb.texinfo
> changes? I've added a new section, though it is somewhat sparse at
> present.
Yes, it would be good to populate it with additional information about
debugging optimized code.
> +There are some ways that @value{GDBN} cannot 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 on the non-inlined copy of the function.
> +
> +@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.
> +
> +@item
> +@value{GDBN} cannot locate the return value of inlined calls after
> +using the @code{finish} command.
> +
> +@end itemize
This is IMO too negative: you state several problems and never hint on
how to work around them. Please consider suggesting such workarounds
in each one of the above 3 situations. Using breakpoints and returned
values reported by `finish' are two very fundamental debugging
techniques; telling the readers that they are simply unavailable will
lead them to believe debugging code that uses inlined functions is
next to impossible.
Otherwise, this part is okay. Thanks.
> Index: src/gdb/NEWS
> ===================================================================
> --- src.orig/gdb/NEWS 2008-07-15 15:15:22.000000000 -0400
> +++ src/gdb/NEWS 2008-07-15 15:16:05.000000000 -0400
> @@ -17,6 +17,9 @@ For instance, consider:
> If the user types TAB at the end of this command line, the available
> completions will be "f1" and "f2".
>
> +* Inlined functions are now supported. They show up in backtraces, and
> +the "step", "next", and "finish" commands handle them automatically.
> +
This is fine.
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch
2008-07-25 13:54 ` Eli Zaretskii
@ 2008-07-25 14:26 ` Daniel Jacobowitz
2008-07-25 16:11 ` Daniel Jacobowitz
2008-07-26 5:58 ` Eli Zaretskii
0 siblings, 2 replies; 38+ messages in thread
From: Daniel Jacobowitz @ 2008-07-25 14:26 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: gdb-patches
Thanks for looking at this.
On Fri, Jul 25, 2008 at 04:54:10PM +0300, Eli Zaretskii wrote:
> > +There are some ways that @value{GDBN} cannot 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 on the non-inlined copy of the function.
> > +
> > +@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.
> > +
> > +@item
> > +@value{GDBN} cannot locate the return value of inlined calls after
> > +using the @code{finish} command.
> > +
> > +@end itemize
>
> This is IMO too negative: you state several problems and never hint on
> how to work around them. Please consider suggesting such workarounds
> in each one of the above 3 situations. Using breakpoints and returned
> values reported by `finish' are two very fundamental debugging
> techniques; telling the readers that they are simply unavailable will
> lead them to believe debugging code that uses inlined functions is
> next to impossible.
Is this better?
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
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch
2008-07-25 14:26 ` Daniel Jacobowitz
@ 2008-07-25 16:11 ` Daniel Jacobowitz
2008-07-26 5:58 ` Eli Zaretskii
1 sibling, 0 replies; 38+ messages in thread
From: Daniel Jacobowitz @ 2008-07-25 16:11 UTC (permalink / raw)
To: gdb-patches; +Cc: Eli Zaretskii
For reference, here is the latest version of the patch - changelog now
included. I'm still discussing the unwinder organization with Mark
and the manual changes with Eli.
--
Daniel Jacobowitz
CodeSourcery
2008-07-25 Daniel Jacobowitz <dan@codesourcery.com>
gdb/
* NEWS: Document inlined function support.
* Makefile.in (SFILES): Add inline-frame.c.
(inline_frame_h): New.
(COMMON_OBS): Add inline-frame.o.
(blockframe.o, dwarf2loc.o, dwarf2read.o, frame.o, frame-unwind.o)
(infcmd.o, infrun.o, stack.o): Update dependencies.
(inline-frame.o): New rule.
* block.c (contained_in): Rewrite to use lexical nesting.
(block_linkage_function): Skip inlined function blocks.
(block_inlined_p): New.
* block.h (struct block): Update comment.
(block_inlined_p): New prototype.
* blockframe.c (get_frame_block): Handle inlined functions.
(get_frame_function): Do not use block_linkage_function.
(block_innermost_frame): Use get_frame_block and contained_in.
* breakpoint.c (watchpoint_check): Remove extra reinit_frame_cache.
Skip over inlined functions. Simplify epilogue check.
(bpstat_check_breakpoint_conditions): Use get_stack_frame_id.
Update comments.
(set_momentary_breakpoint): Only accept non-inlined frames.
(watch_command_1): Use frame_unwind_caller_pc and
frame_unwind_caller_id instead of get_prev_frame.
(until_break_command): Likewise. Use get_stack_frame_id.
* buildsym.c (end_symtab): Set SYMBOL_SYMTAB for block functions.
* dwarf2loc.c (dwarf_expr_frame_base): Use block_linkage_function.
* dwarf2read.c (process_die): Handle DW_TAG_inlined_subroutine.
(read_func_scope, new_symbol): Likewise. Handle arguments specially
for inlined functions without call site information.
(die_specification): Treat DW_AT_abstract_origin as a specification.
* frame-unwind.c (frame_unwind_init): Add inline_frame_unwind.
* frame.c (fprint_frame_id): Print inline depth.
(fprint_frame_type): Handle INLINE_FRAME and SENTINEL_FRAME.
(skip_inlined_frames, get_stack_frame_id): New.
(frame_unwind_caller_id): Use skip_inlined_frames.
(frame_id_inlined_p): New.
(frame_id_eq): Make the logic match the comments. Add inline_depth
check.
(frame_id_inner): Handle inlined functions.
(frame_unwind_pc): New function, copied from frame_unwind_caller_pc.
(frame_unwind_caller_pc): Use skip_inlined_frames and frame_unwind_pc.
(get_prev_frame_1): Check for inline frames. Split out frame
allocation to get_prev_frame_raw.
(get_prev_frame_raw): New function.
(get_prev_frame): Handle inline frames.
(get_frame_pc): Use frame_unwind_pc.
(get_frame_address_in_block): Skip inlined frames on both sides.
(pc_notcurrent): Delete.
(find_frame_sal): Rewrite to handle inline call sites. Use
get_frame_address_in_block.
(deprecated_update_frame_pc_hack): Make static.
* frame.h: Update comments.
(struct frame_id): Add inline_depth.
(enum frame_type): Add INLINE_FRAME.
(deprecated_update_frame_pc_hack): Delete prototype.
(frame_id_inlined_p, get_stack_frame_id): New prototypes.
* infcall.c (call_function_by_hand): Do not pop the frame.
* infcmd.c (step_frame_id): Delete.
(set_step_frame): New function.
(step_1): Use step_once in both cases.
(step_once): Use set_step_frame. Handle inlined functions and
non-async targets. Remove step_stop_if_no_debug check not
present in sync case.
(until_next_command): Use set_step_frame.
(finish_command): Support inlined functions.
* inferior.h (set_step_info): New prototype.
(step_frame_id): Delete declartion.
* infrun.c (RESUME_ALL): Use minus_one_ptid.
(step_frame_id, step_stack_frame_id): New static variables.
(struct thread_stepping_state): Add a comment.
(resume): Use clear_inline_frame_state.
(clear_proceed_status): Clear step_stack_frame_id.
(init_wait_for_inferior): Call clear_inline_frame_state.
(init_execution_control_state): Make static.
(set_step_info): New function.
(init_thread_stepping_state): Do not set the symtab or line.
(stepped_in_from): New function.
(handle_inferior_event): Handle inlined functions. Delete some dead
code. Use set_step_info.
(normal_stop): Do not call deprecated_update_frame_pc_hack.
(struct inferior_status): Add step_stack_frame_id.
(save_inferior_status, restore_inferior_status): Save and restore
step_stack_frame_id.
* inline-frame.c, inline-frame.h: New files.
* minsyms.c (prim_record_minimal_symbol_and_info): Use XCALLOC.
* s390-tdep.c (s390_prologue_frame_unwind_cache): Handle INLINE_FRAME.
* stack.c (frame_show_address): New.
(print_frame_info): Use it.
(print_frame): Likewise. Use get_frame_function. Handle inlined
blocks.
(frame_info): Mark inlined functions.
(backtrace_command_1): Use get_current_user_frame.
(print_frame_local_vars, print_frame_label_vars): Update comments.
(return_command): Refuse inlined functions.
* symtab.c (lookup_symbol_aux_local): Stop at inlined function
boundaries.
(find_function_start_sal): Avoid inlined functions.
(completion_list_add_fields): New function.
(default_make_symbol_completion_list): Use it. Use block_static_block
and block_global_block. Check for inlined functions.
(skip_prologue_using_sal): Avoid line number comparison across
inlining.
* symtab.h (struct symbol): Add is_inlined.
(SYMBOL_INLINED): New.
* valops.c (value_of_variable): Check block_inlined_p.
gdb/testsuite/
* gdb.base/break.exp: Add an XFAIL for gcc/36748.
* gdb.cp/annota2.exp: Accept frames-invalid in more places.
* gdb.opt/Makefile.in (EXECUTABLES): Update.
* gdb.opt/clobbered-registers-O2.exp: Update to GPL v3.
* gdb.opt/inline-bt.c, gdb.opt/inline-bt.exp,
gdb.opt/inline-cmds.c, gdb.opt/inline-cmds.exp,
gdb.opt/inline-locals.c, gdb.opt/inline-locals.exp,
gdb.opt/inline-markers.c: New files.
* lib/gdb.exp (skip_inline_frame_tests): New function.
(skip_inline_var_tests): New function.
gdb/doc/
* gdb.texinfo (Debugging Optimized Code): New chapter.
(Compiling for Debugging): Reference it. Move some
text to the new section.
* Makefile.in (SFILES):
(inf_loop_h):
(COMMON_OBS):
(block.o):
(dwarf2-frame.o):
(frame.o):
(infcmd.o):
(infrun.o):
(stack.o):
* block.c (contained_in):
* block.h (struct):
(enum):
* blockframe.c (struct):
(block_innermost_frame):
* breakpoint.c (watchpoint_check):
(bpstat_check_breakpoint_conditions):
(set_momentary_breakpoint):
(watch_command_1):
(until_break_command):
* buildsym.c (end_symtab):
* dwarf2loc.c (dwarf_expr_frame_base):
* dwarf2read.c (process_die):
(read_func_scope):
(die_is_declaration):
(die_specification):
(new_symbol):
* frame-unwind.c (frame_unwind_init):
* frame.c (fprint_frame_id):
(fprint_frame_type):
(fprint_frame):
(get_frame_id):
(frame_id_p):
(frame_id_eq):
(frame_id_inner):
(frame_find_by_id):
(frame_unwind_caller_pc):
(frame_register_unwind_location):
(get_prev_frame_1):
(get_prev_frame):
(CORE_ADDR):
(get_frame_address_in_block):
(pc_notcurrent):
(get_frame_type):
* frame.h (struct):
(extern):
(enum):
* infcall.c (call_function_by_hand):
* infcmd.c (int):
(Can):
(step_1):
(static):
(which):
(signal_command):
(until_next_command):
(finish_command):
* inferior.h (extern):
* infrun.c (static):
(a):
(clear_proceed_status):
(init_wait_for_inferior):
(struct):
(fetch_inferior_event):
(init_execution_control_state):
(ensure_not_running):
(targets):
(process_event_stop_test):
(infrun):
(normal_stop):
(Further):
(save_inferior_status):
(restore_inferior_status):
* inline-frame.c:
* inline-frame.h:
* minsyms.c (prim_record_minimal_symbol_and_info):
* s390-tdep.c (s390_prologue_frame_unwind_cache):
* stack.c (print_stack_frame_stub):
(print_frame_info):
(print_frame):
(frame_info):
(print_frame_local_vars):
(print_frame_label_vars):
(return_command):
* symtab.c (lookup_symbol_aux_local):
(find_function_start_sal):
(language_search_unquoted_string):
(default_make_symbol_completion_list):
(skip_prologue_using_sal):
* symtab.h (struct):
* valops.c (value_of_variable):
* NEWS (For):
2008-07-25 Daniel Jacobowitz <dan@codesourcery.com>
* doc/gdb.texinfo (software):
(To):
(executables):
2008-07-25 Daniel Jacobowitz <dan@codesourcery.com>
* gdb.base/break.exp (gdb_expect):
* gdb.cp/annota2.exp (gdb_expect):
* gdb.opt/Makefile.in:
* gdb.opt/clobbered-registers-O2.exp:
* gdb.opt/inline-bt.c:
* gdb.opt/inline-bt.exp:
* gdb.opt/inline-cmds.c:
* gdb.opt/inline-cmds.exp:
* gdb.opt/inline-locals.c:
* gdb.opt/inline-locals.exp:
* gdb.opt/inline-markers.c:
* lib/gdb.exp (proc):
---
gdb/Makefile.in | 22 -
gdb/NEWS | 3
gdb/block.c | 23 +
gdb/block.h | 4
gdb/blockframe.c | 36 +-
gdb/breakpoint.c | 90 +++--
gdb/buildsym.c | 6
gdb/doc/gdb.texinfo | 121 ++++++-
gdb/dwarf2loc.c | 10
gdb/dwarf2read.c | 58 ++-
gdb/frame-unwind.c | 5
gdb/frame.c | 200 +++++++++---
gdb/frame.h | 27 +
gdb/infcall.c | 7
gdb/infcmd.c | 158 +++++----
gdb/inferior.h | 8
gdb/infrun.c | 188 ++++++++---
gdb/inline-frame.c | 382 +++++++++++++++++++++++
gdb/inline-frame.h | 62 +++
gdb/minsyms.c | 2
gdb/s390-tdep.c | 11
gdb/stack.c | 57 ++-
gdb/symtab.c | 134 +++++---
gdb/symtab.h | 16
gdb/testsuite/gdb.base/break.exp | 7
gdb/testsuite/gdb.cp/annota2.exp | 3
gdb/testsuite/gdb.opt/Makefile.in | 2
gdb/testsuite/gdb.opt/clobbered-registers-O2.exp | 5
gdb/testsuite/gdb.opt/inline-bt.c | 47 ++
gdb/testsuite/gdb.opt/inline-bt.exp | 63 +++
gdb/testsuite/gdb.opt/inline-cmds.c | 85 +++++
gdb/testsuite/gdb.opt/inline-cmds.exp | 279 ++++++++++++++++
gdb/testsuite/gdb.opt/inline-locals.c | 52 +++
gdb/testsuite/gdb.opt/inline-locals.exp | 118 +++++++
gdb/testsuite/gdb.opt/inline-markers.c | 36 ++
gdb/testsuite/lib/gdb.exp | 31 +
gdb/valops.c | 2
37 files changed, 2037 insertions(+), 323 deletions(-)
Index: src/gdb/Makefile.in
===================================================================
--- src.orig/gdb/Makefile.in 2008-07-18 07:37:20.000000000 -0400
+++ src/gdb/Makefile.in 2008-07-18 07:37:30.000000000 -0400
@@ -615,6 +615,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 \
@@ -822,6 +823,7 @@ inf_loop_h = inf-loop.h
inflow_h = inflow.h $(terminal_h)
inf_ptrace_h = inf-ptrace.h
inf_ttrace_h = inf-ttrace.h
+inline_frame_h = inline-frame.h
interps_h = interps.h $(exceptions_h)
jv_lang_h = jv-lang.h
language_h = language.h
@@ -1082,6 +1084,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 \
@@ -1965,7 +1968,7 @@ block.o: block.c $(defs_h) $(block_h) $(
blockframe.o: blockframe.c $(defs_h) $(symtab_h) $(bfd_h) $(objfiles_h) \
$(frame_h) $(gdbcore_h) $(value_h) $(target_h) $(inferior_h) \
$(annotate_h) $(regcache_h) $(gdb_assert_h) $(dummy_frame_h) \
- $(command_h) $(gdbcmd_h) $(block_h)
+ $(command_h) $(gdbcmd_h) $(block_h) $(inline_frame_h)
breakpoint.o: breakpoint.c $(defs_h) $(symtab_h) $(frame_h) $(breakpoint_h) \
$(gdbtypes_h) $(expression_h) $(gdbcore_h) $(gdbcmd_h) $(value_h) \
$(command_h) $(inferior_h) $(gdbthread_h) $(target_h) $(language_h) \
@@ -2084,13 +2087,13 @@ dwarf2-frame.o: dwarf2-frame.c $(defs_h)
dwarf2loc.o: dwarf2loc.c $(defs_h) $(ui_out_h) $(value_h) $(frame_h) \
$(gdbcore_h) $(target_h) $(inferior_h) $(ax_h) $(ax_gdb_h) \
$(regcache_h) $(objfiles_h) $(exceptions_h) $(elf_dwarf2_h) \
- $(dwarf2expr_h) $(dwarf2loc_h) $(gdb_string_h) $(gdb_assert_h)
+ $(dwarf2expr_h) $(dwarf2loc_h) $(gdb_string_h) $(gdb_assert_h) $(block_h)
dwarf2read.o: dwarf2read.c $(defs_h) $(bfd_h) $(symtab_h) $(gdbtypes_h) \
$(objfiles_h) $(elf_dwarf2_h) $(buildsym_h) $(demangle_h) \
$(expression_h) $(filenames_h) $(macrotab_h) $(language_h) \
$(complaints_h) $(bcache_h) $(dwarf2expr_h) $(dwarf2loc_h) \
$(cp_support_h) $(hashtab_h) $(command_h) $(gdbcmd_h) \
- $(addrmap_h) $(gdb_string_h) $(gdb_assert_h)
+ $(block_h) $(addrmap_h) $(gdb_string_h) $(gdb_assert_h)
elfread.o: elfread.c $(defs_h) $(bfd_h) $(gdb_string_h) $(elf_bfd_h) \
$(elf_mips_h) $(symtab_h) $(symfile_h) $(objfiles_h) $(buildsym_h) \
$(stabsread_h) $(gdb_stabs_h) $(complaints_h) $(demangle_h) \
@@ -2142,10 +2145,10 @@ frame.o: frame.c $(defs_h) $(frame_h) $(
$(gdb_obstack_h) $(dummy_frame_h) $(sentinel_frame_h) $(gdbcore_h) \
$(annotate_h) $(language_h) $(frame_unwind_h) $(frame_base_h) \
$(command_h) $(gdbcmd_h) $(observer_h) $(objfiles_h) $(exceptions_h) \
- $(gdbthread_h)
+ $(gdbthread_h) $(block_h) $(inline_frame_h)
frame-unwind.o: frame-unwind.c $(defs_h) $(frame_h) $(frame_unwind_h) \
$(gdb_assert_h) $(dummy_frame_h) $(gdb_obstack_h) $(value_h) \
- $(regcache_h)
+ $(regcache_h) $(inline_frame_h)
frv-linux-tdep.o: frv-linux-tdep.c $(defs_h) $(gdbcore_h) $(target_h) \
$(frame_h) $(osabi_h) $(regcache_h) $(elf_bfd_h) $(elf_frv_h) \
$(frv_tdep_h) $(trad_frame_h) $(frame_unwind_h) $(regset_h) \
@@ -2320,7 +2323,8 @@ infcmd.o: infcmd.c $(defs_h) $(gdb_strin
$(objfiles_h) $(completer_h) $(ui_out_h) $(event_top_h) \
$(parser_defs_h) $(regcache_h) $(reggroups_h) $(block_h) \
$(solib_h) $(gdb_assert_h) $(observer_h) $(target_descriptions_h) \
- $(user_regs_h) $(exceptions_h) $(cli_decode_h) $(gdbthread_h)
+ $(user_regs_h) $(exceptions_h) $(cli_decode_h) $(gdbthread_h) \
+ $(inline_frame_h)
inf-loop.o: inf-loop.c $(defs_h) $(inferior_h) $(target_h) $(event_loop_h) \
$(event_top_h) $(inf_loop_h) $(remote_h) $(exceptions_h) \
$(language_h) $(gdbthread_h)
@@ -2335,10 +2339,12 @@ infrun.o: infrun.c $(defs_h) $(gdb_strin
$(gdbcore_h) $(gdbcmd_h) $(cli_script_h) $(target_h) $(gdbthread_h) \
$(annotate_h) $(symfile_h) $(top_h) $(inf_loop_h) $(regcache_h) \
$(value_h) $(observer_h) $(language_h) $(solib_h) $(gdb_assert_h) \
- $(mi_common_h) $(main_h) $(event_top_h)
+ $(mi_common_h) $(main_h) $(event_top_h) $(inline_frame_h)
inf-ttrace.o: inf-ttrace.c $(defs_h) $(command_h) $(gdbcore_h) \
$(gdbthread_h) $(inferior_h) $(target_h) \
$(gdb_assert_h) $(gdb_string_h) $(inf_child_h) $(inf_ttrace_h)
+inline-frame.o: inline-frame.c $(defs_h) $(frame_unwind_h) $(block_h) \
+ $(symtab_h) $(addrmap_h) $(gdb_assert_h) $(vec_h) $(inferior_h)
interps.o: interps.c $(defs_h) $(gdbcmd_h) $(ui_out_h) $(event_loop_h) \
$(event_top_h) $(interps_h) $(completer_h) $(gdb_string_h) \
$(gdb_events_h) $(gdb_assert_h) $(top_h) $(exceptions_h)
@@ -2881,7 +2887,7 @@ stack.o: stack.c $(defs_h) $(value_h) $(
$(target_h) $(source_h) $(breakpoint_h) $(demangle_h) $(inferior_h) \
$(annotate_h) $(ui_out_h) $(block_h) $(stack_h) $(dictionary_h) \
$(exceptions_h) $(reggroups_h) $(regcache_h) $(solib_h) \
- $(valprint_h) $(gdb_assert_h) $(gdb_string_h)
+ $(valprint_h) $(gdb_assert_h) $(gdb_string_h) $(inline_frame_h)
std-regs.o: std-regs.c $(defs_h) $(user_regs_h) $(frame_h) $(gdbtypes_h) \
$(value_h) $(gdb_string_h)
symfile.o: symfile.c $(defs_h) $(bfdlink_h) $(symtab_h) $(gdbtypes_h) \
Index: src/gdb/block.c
===================================================================
--- src.orig/gdb/block.c 2008-07-18 07:37:20.000000000 -0400
+++ src/gdb/block.c 2008-07-18 07:37:30.000000000 -0400
@@ -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: src/gdb/block.h
===================================================================
--- src.orig/gdb/block.h 2008-07-18 07:37:20.000000000 -0400
+++ src/gdb/block.h 2008-07-18 07:37:30.000000000 -0400
@@ -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: src/gdb/blockframe.c
===================================================================
--- src.orig/gdb/blockframe.c 2008-07-18 07:37:20.000000000 -0400
+++ src/gdb/blockframe.c 2008-07-18 07:37:30.000000000 -0400
@@ -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);
}
\f
@@ -359,8 +383,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: src/gdb/breakpoint.c
===================================================================
--- src.orig/gdb/breakpoint.c 2008-07-18 07:37:20.000000000 -0400
+++ src/gdb/breakpoint.c 2008-07-18 08:26:42.000000000 -0400
@@ -2641,19 +2641,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.
@@ -2665,10 +2667,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. */
@@ -2938,7 +2939,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)
{
@@ -2953,8 +2954,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),
@@ -4846,6 +4851,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;
@@ -5833,7 +5843,6 @@ watch_command_1 (char *arg, int accessfl
struct block *exp_valid_block;
struct value *val, *mark;
struct frame_info *frame;
- struct frame_info *prev_frame = NULL;
char *exp_start = NULL;
char *exp_end = NULL;
char *tok, *id_tok_start, *end_tok;
@@ -5967,34 +5976,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_caller_id (frame)))
+ {
+ scope_breakpoint
+ = create_internal_breakpoint (frame_unwind_caller_pc (frame),
+ bp_watchpoint_scope);
- scope_breakpoint->enable_state = bp_enabled;
+ scope_breakpoint->enable_state = bp_enabled;
- /* Automatically delete the breakpoint when it hits. */
- scope_breakpoint->disposition = disp_del;
+ /* Automatically delete the breakpoint when it hits. */
+ scope_breakpoint->disposition = disp_del;
- /* Only break in the proper frame (help with recursion). */
- scope_breakpoint->frame_id = get_frame_id (prev_frame);
+ /* Only break in the proper frame (help with recursion). */
+ scope_breakpoint->frame_id = frame_unwind_caller_id (frame);
- /* Set the address at which we will stop. */
- scope_breakpoint->loc->requested_address
- = get_frame_pc (prev_frame);
- scope_breakpoint->loc->address
- = adjust_breakpoint_address (scope_breakpoint->loc->requested_address,
- scope_breakpoint->type);
+ /* Set the address at which we will stop. */
+ scope_breakpoint->loc->requested_address
+ = frame_unwind_caller_pc (frame);
+ scope_breakpoint->loc->address
+ = adjust_breakpoint_address (scope_breakpoint->loc->requested_address,
+ scope_breakpoint->type);
+ }
}
/* Now set up the breakpoint. */
@@ -6175,7 +6184,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;
@@ -6208,20 +6216,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_caller_id (frame)))
{
- sal = find_pc_line (get_frame_pc (prev_frame), 0);
- sal.pc = get_frame_pc (prev_frame);
- breakpoint2 = set_momentary_breakpoint (sal, get_frame_id (prev_frame),
+ sal = find_pc_line (frame_unwind_caller_pc (frame), 0);
+ sal.pc = frame_unwind_caller_pc (frame);
+ breakpoint2 = set_momentary_breakpoint (sal,
+ frame_unwind_caller_id (frame),
bp_until);
make_cleanup_delete_breakpoint (breakpoint2);
}
Index: src/gdb/buildsym.c
===================================================================
--- src.orig/gdb/buildsym.c 2008-07-18 07:37:20.000000000 -0400
+++ src/gdb/buildsym.c 2008-07-18 07:37:30.000000000 -0400
@@ -1171,6 +1171,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: src/gdb/doc/gdb.texinfo
===================================================================
--- src.orig/gdb/doc/gdb.texinfo 2008-07-18 07:37:21.000000000 -0400
+++ src/gdb/doc/gdb.texinfo 2008-07-25 10:25:29.000000000 -0400
@@ -146,6 +146,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
@@ -1803,7 +1804,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.
@@ -1812,22 +1813,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
@@ -7854,6 +7840,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: src/gdb/dwarf2loc.c
===================================================================
--- src.orig/gdb/dwarf2loc.c 2008-07-18 07:37:20.000000000 -0400
+++ src/gdb/dwarf2loc.c 2008-07-18 07:37:30.000000000 -0400
@@ -31,6 +31,7 @@
#include "regcache.h"
#include "objfiles.h"
#include "exceptions.h"
+#include "block.h"
#include "elf/dwarf2.h"
#include "dwarf2expr.h"
@@ -146,14 +147,19 @@ 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,
something has gone wrong. */
gdb_assert (framefunc != NULL);
- if (SYMBOL_OPS (framefunc) == &dwarf2_loclist_funcs)
+ if (SYMBOL_LOCATION_BATON (framefunc) == NULL)
+ *start = NULL;
+ else if (SYMBOL_OPS (framefunc) == &dwarf2_loclist_funcs)
{
struct dwarf2_loclist_baton *symbaton;
struct frame_info *frame = debaton->frame;
Index: src/gdb/dwarf2read.c
===================================================================
--- src.orig/gdb/dwarf2read.c 2008-07-18 07:37:20.000000000 -0400
+++ src/gdb/dwarf2read.c 2008-07-18 07:37:30.000000000 -0400
@@ -45,6 +45,7 @@
#include "hashtab.h"
#include "command.h"
#include "gdbcmd.h"
+#include "block.h"
#include "addrmap.h"
#include <fcntl.h>
@@ -2713,12 +2714,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:
@@ -2941,12 +2938,27 @@ 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;
const char *previous_prefix = processing_current_prefix;
struct cleanup *back_to = NULL;
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));
@@ -6690,8 +6702,9 @@ die_is_declaration (struct die_info *die
&& dwarf2_attr (die, DW_AT_specification, cu) == NULL);
}
-/* Return the die giving the specification for DIE, if there is
- one. */
+/* Return the die giving the specification for DIE, if there is one.
+ If there is no specification, but there is an abstract origin, that
+ is returned. */
static struct die_info *
die_specification (struct die_info *die, struct dwarf2_cu *cu)
@@ -6699,6 +6712,9 @@ die_specification (struct die_info *die,
struct attribute *spec_attr = dwarf2_attr (die, DW_AT_specification, cu);
if (spec_attr == NULL)
+ spec_attr = dwarf2_attr (die, DW_AT_abstract_origin, cu);
+
+ if (spec_attr == NULL)
return NULL;
else
return follow_die_ref (die, spec_attr, cu);
@@ -7375,6 +7391,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));
@@ -7402,13 +7419,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);
@@ -7455,6 +7476,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
@@ -7502,7 +7531,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: src/gdb/frame-unwind.c
===================================================================
--- src.orig/gdb/frame-unwind.c 2008-07-18 07:37:20.000000000 -0400
+++ src/gdb/frame-unwind.c 2008-07-18 07:37:30.000000000 -0400
@@ -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: src/gdb/frame.c
===================================================================
--- src.orig/gdb/frame.c 2008-07-18 07:37:20.000000000 -0400
+++ src/gdb/frame.c 2008-07-25 10:57:28.000000000 -0400
@@ -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
@@ -173,6 +179,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, "}");
}
@@ -187,6 +195,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;
@@ -239,6 +253,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. */
@@ -271,13 +297,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_caller_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. */
@@ -332,6 +366,15 @@ frame_id_p (struct frame_id l)
}
int
+frame_id_inlined_p (struct frame_id l)
+{
+ if (!frame_id_p (l))
+ return 0;
+
+ return (l.inline_depth != 0);
+}
+
+int
frame_id_eq (struct frame_id l, struct frame_id r)
{
int eq;
@@ -342,21 +385,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=");
@@ -375,6 +419,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
@@ -421,8 +488,8 @@ frame_find_by_id (struct frame_id id)
return NULL;
}
-CORE_ADDR
-frame_unwind_caller_pc (struct frame_info *this_frame)
+static CORE_ADDR
+frame_unwind_pc (struct frame_info *this_frame)
{
if (!this_frame->prev_pc.p)
{
@@ -461,6 +528,12 @@ frame_unwind_caller_pc (struct frame_inf
}
CORE_ADDR
+frame_unwind_caller_pc (struct frame_info *this_frame)
+{
+ return frame_unwind_pc (skip_inlined_frames (this_frame));
+}
+
+CORE_ADDR
get_frame_func (struct frame_info *this_frame)
{
struct frame_info *next_frame = this_frame->next;
@@ -1164,7 +1237,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;
@@ -1204,6 +1276,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. */
@@ -1272,7 +1352,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;
@@ -1301,6 +1382,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
@@ -1460,7 +1552,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
@@ -1506,8 +1598,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 (gdb_stdlog, this_frame, "inside entry func");
@@ -1518,7 +1611,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)
{
@@ -1533,7 +1627,7 @@ CORE_ADDR
get_frame_pc (struct frame_info *frame)
{
gdb_assert (frame->next != NULL);
- return frame_unwind_caller_pc (frame->next);
+ return frame_unwind_pc (frame->next);
}
/* Return an address that falls within THIS_FRAME's code block. */
@@ -1578,17 +1672,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
@@ -1598,15 +1733,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
@@ -1687,7 +1815,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: src/gdb/frame.h
===================================================================
--- src.orig/gdb/frame.h 2008-07-18 07:37:20.000000000 -0400
+++ src/gdb/frame.h 2008-07-18 08:46:58.000000000 -0400
@@ -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.
+
frame_unwind_caller_WHAT...(): Unwind WHAT for NEXT stack frame's
real caller. Any inlined functions in NEXT's stack frame are
skipped. Use these to ignore any potentially inlined functions,
@@ -106,6 +109,10 @@ struct frame_id
Typically, it is set to the address of the entry point of the
frame's function (as returned by get_frame_func).
+ For inlined functions (INLINE_DEPTH != 0), this is the address of
+ the first executed instruction in the block corresponding to the
+ inlined function.
+
This field is valid only if code_addr_p is true. Otherwise, this
frame is considered to have a wildcard code address, i.e. one that
matches every address value in frame comparisons. */
@@ -127,6 +134,10 @@ struct frame_id
unsigned int stack_addr_p : 1;
unsigned int code_addr_p : 1;
unsigned int special_addr_p : 1;
+
+ /* The inline depth of this frame. A frame representing a "called"
+ inlined function will have this set to a nonzero value. */
+ int inline_depth;
};
/* Methods for constructing and comparing Frame IDs.
@@ -177,6 +188,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);
@@ -203,6 +218,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,
@@ -366,6 +384,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_caller_id (struct frame_info *next_frame);
/* Assuming that a frame is `normal', return its base-address, or 0 if
@@ -696,14 +715,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: src/gdb/infcall.c
===================================================================
--- src.orig/gdb/infcall.c 2008-07-18 07:37:21.000000000 -0400
+++ src/gdb/infcall.c 2008-07-18 07:37:30.000000000 -0400
@@ -769,11 +769,8 @@ call_function_by_hand (struct value *fun
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: src/gdb/infcmd.c
===================================================================
--- src.orig/gdb/infcmd.c 2008-07-18 07:37:21.000000000 -0400
+++ src/gdb/infcmd.c 2008-07-18 08:38:29.000000000 -0400
@@ -51,6 +51,7 @@
#include "exceptions.h"
#include "cli/cli-decode.h"
#include "gdbthread.h"
+#include "inline-frame.h"
/* Functions exported for general use, in inferior.h: */
@@ -184,12 +185,6 @@ int stopped_by_random_signal;
CORE_ADDR step_range_start; /* Inclusive */
CORE_ADDR step_range_end; /* Exclusive */
-/* Stack frame address as of when stepping command was issued.
- 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;
-
enum step_over_calls_kind step_over_calls;
/* If stepping, nonzero means step count is > 1
@@ -718,6 +713,17 @@ Can't resume all threads and specify pro
continue_1 (all_threads);
}
\f
+/* Record the starting point of a "step" or "next" command. */
+
+static void
+set_step_frame (void)
+{
+ struct symtab_and_line sal;
+
+ find_frame_sal (get_current_frame (), &sal);
+ set_step_info (get_current_frame (), sal);
+}
+
/* Step until outside of current statement. */
static void
@@ -795,65 +801,33 @@ 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--)
{
- clear_proceed_status ();
+ step_once (skip_subroutines, single_inst, count, thread);
- frame = get_current_frame ();
- if (!frame) /* Avoid coredump here. Why tho? */
- error (_("No current frame"));
- step_frame_id = get_frame_id (frame);
-
- if (!single_inst)
+ if (!step_multi || !stop_step)
{
- find_pc_line_pc_range (stop_pc, &step_range_start, &step_range_end);
- if (step_range_end == 0)
- {
- char *name;
- if (find_pc_partial_function (stop_pc, &name, &step_range_start,
- &step_range_end) == 0)
- error (_("Cannot find bounds of current function"));
-
- target_terminal_ours ();
- printf_filtered (_("\
-Single stepping until exit from function %s, \n\
-which has no line number information.\n"), name);
- }
- }
- else
- {
- /* Say we are stepping, but stop after one insn whatever it does. */
- step_range_start = step_range_end = 1;
- if (!skip_subroutines)
- /* It is stepi.
- Don't step over function calls, not even to functions lacking
- line numbers. */
- step_over_calls = STEP_OVER_NONE;
+ /* If we stopped for some reason that is not stepping
+ there are no further steps to make. */
+ step_multi = 0;
+ break;
}
-
- if (skip_subroutines)
- step_over_calls = STEP_OVER_ALL;
-
- step_multi = (count > 1);
- proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 1);
-
- if (!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);
@@ -901,27 +875,30 @@ 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)
{
clear_proceed_status ();
-
- frame = get_current_frame ();
- if (!frame) /* Avoid coredump here. Why tho? */
- error (_("No current frame"));
- step_frame_id = get_frame_id (frame);
+ set_step_frame ();
if (!single_inst)
{
- find_pc_line_pc_range (stop_pc, &step_range_start, &step_range_end);
-
- /* If we have no line info, switch to stepi mode. */
- if (step_range_end == 0 && step_stop_if_no_debug)
+ /* Step at an inlined function behaves like "down". */
+ if (!skip_subroutines && !single_inst
+ && inline_skipped_frames (inferior_ptid))
{
- step_range_start = step_range_end = 1;
+ 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;
}
- else if (step_range_end == 0)
+
+ find_pc_line_pc_range (stop_pc, &step_range_start, &step_range_end);
+
+ if (step_range_end == 0)
{
char *name;
if (find_pc_partial_function (stop_pc, &name, &step_range_start,
@@ -951,12 +928,21 @@ which has no line number information.\n"
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 (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 (step_1_continuation, args, xfree);
+ }
}
}
@@ -1141,14 +1127,12 @@ 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;
clear_proceed_status ();
-
- frame = get_current_frame ();
+ set_step_frame ();
/* Step until either exited from this function or greater
than the current line (if in symbolic section) or pc (if
@@ -1176,7 +1160,6 @@ until_next_command (int from_tty)
}
step_over_calls = STEP_OVER_ALL;
- step_frame_id = get_frame_id (frame);
step_multi = 0; /* Only one call to proceed */
@@ -1402,6 +1385,35 @@ finish_command (char *arg, int from_tty)
clear_proceed_status ();
+ /* Finishing from an inline frame is completely different. We don't
+ try to show the "return value" - no way to locate it. So we do
+ not need a completion. */
+ if (get_frame_type (get_selected_frame (_("No selected frame.")))
+ == INLINE_FRAME)
+ {
+ /* Claim we are stepping in the calling frame. An empty step
+ range means that we will stop once we aren't in a function
+ called by that frame. We don't use the magic "1" value for
+ step_range_end, because then infrun will think this is nexti,
+ and not step over the rest of this inlined function call. */
+ struct symtab_and_line empty_sal;
+ init_sal (&empty_sal);
+ set_step_info (frame, empty_sal);
+ step_range_start = step_range_end = get_frame_pc (frame);
+ 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;
+ }
+
sal = find_pc_line (get_frame_pc (frame), 0);
sal.pc = get_frame_pc (frame);
Index: src/gdb/inferior.h
===================================================================
--- src.orig/gdb/inferior.h 2008-07-18 07:37:21.000000000 -0400
+++ src/gdb/inferior.h 2008-07-18 08:38:11.000000000 -0400
@@ -251,6 +251,8 @@ extern void error_is_running (void);
/* Calls error_is_running if the current thread is running. */
extern void ensure_not_running (void);
+void set_step_info (struct frame_info *frame, struct symtab_and_line sal);
+
/* From infcmd.c */
extern void tty_command (char *, int);
@@ -321,12 +323,6 @@ extern int stopped_by_random_signal;
extern CORE_ADDR step_range_start; /* Inclusive */
extern CORE_ADDR step_range_end; /* Exclusive */
-/* Stack frame address as of when stepping command was issued.
- 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. */
-
-extern struct frame_id step_frame_id;
-
/* 1 means step over all subroutine calls.
-1 means step over calls to undebuggable functions. */
Index: src/gdb/infrun.c
===================================================================
--- src.orig/gdb/infrun.c 2008-07-18 07:37:21.000000000 -0400
+++ src/gdb/infrun.c 2008-07-25 11:54:31.000000000 -0400
@@ -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. */
@@ -289,14 +290,27 @@ static struct breakpoint *step_resume_br
static ptid_t target_last_wait_ptid;
static struct target_waitstatus target_last_waitstatus;
+/* Stack frame address as of when stepping command was issued.
+ 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. */
+static struct frame_id step_frame_id;
+
+/* Similarly, the frame ID of the underlying stack frame (skipping any
+ inlined frames). */
+static struct frame_id step_stack_frame_id;
+
/* Context-switchable data. */
struct thread_stepping_state
{
/* Should we step over breakpoint next time keep_going
is called? */
int stepping_over_breakpoint;
+
+ /* 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;
+
int step_after_step_resume_breakpoint;
int stepping_through_solib_after_catch;
bpstat stepping_through_solib_catchpoints;
@@ -1082,6 +1096,8 @@ a command like `return' or `jump' to con
step = 0;
}
+ clear_inline_frame_state (resume_ptid);
+
if (debug_displaced
&& use_displaced_stepping (gdbarch)
&& stepping_over_breakpoint)
@@ -1114,6 +1130,7 @@ clear_proceed_status (void)
step_range_start = 0;
step_range_end = 0;
step_frame_id = null_frame_id;
+ step_stack_frame_id = null_frame_id;
step_over_calls = STEP_OVER_UNDEBUGGABLE;
stop_after_trap = 0;
stop_soon = NO_STOP_QUIETLY;
@@ -1390,6 +1407,9 @@ init_wait_for_inferior (void)
init_infwait_state ();
displaced_step_clear ();
+
+ /* Discard any skipped inlined frames. */
+ clear_inline_frame_state (minus_one_ptid);
}
\f
@@ -1439,7 +1459,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);
@@ -1593,10 +1613,20 @@ fetch_inferior_event (void *client_data)
display_gdb_prompt (0);
}
+/* Record the frame and location we're currently stepping through. */
+void
+set_step_info (struct frame_info *frame, struct symtab_and_line sal)
+{
+ step_frame_id = get_frame_id (frame);
+ step_stack_frame_id = get_stack_frame_id (frame);
+ tss->current_symtab = sal.symtab;
+ tss->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;
@@ -1607,16 +1637,10 @@ init_execution_control_state (struct exe
void
init_thread_stepping_state (struct thread_stepping_state *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 (prev_pc, 0);
- tss->current_line = sal.line;
- tss->current_symtab = sal.symtab;
}
/* Return the cached copy of the last pid/waitstatus returned by
@@ -1805,6 +1829,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. */
@@ -2463,6 +2503,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 (step_range_end != 1)
+ skip_inline_frames (ecs->ptid);
+
if (stop_signal == TARGET_SIGNAL_TRAP
&& stepping_over_breakpoint
&& gdbarch_single_step_through_delay_p (current_gdbarch)
@@ -2678,8 +2724,8 @@ process_event_stop_test:
if (step_range_end != 0
&& stop_signal != TARGET_SIGNAL_0
&& stop_pc >= step_range_start && stop_pc < step_range_end
- && frame_id_eq (get_frame_id (get_current_frame ()),
- step_frame_id)
+ && frame_id_eq (get_stack_frame_id (get_current_frame ()),
+ step_stack_frame_id)
&& step_resume_breakpoint == NULL)
{
/* The inferior is about to take a signal that will take it
@@ -3014,12 +3060,7 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (
until we exit the run time loader code and reach the callee's
address. */
if (step_over_calls == STEP_OVER_UNDEBUGGABLE
-#ifdef IN_SOLIB_DYNSYM_RESOLVE_CODE
- && IN_SOLIB_DYNSYM_RESOLVE_CODE (stop_pc)
-#else
- && in_solib_dynsym_resolve_code (stop_pc)
-#endif
- )
+ && in_solib_dynsym_resolve_code (stop_pc))
{
CORE_ADDR pc_after_resolver =
gdbarch_skip_solib_resolver (current_gdbarch, stop_pc);
@@ -3066,9 +3107,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 ()), step_frame_id)
+ if (!frame_id_eq (get_stack_frame_id (get_current_frame ()),
+ step_stack_frame_id)
&& frame_id_eq (frame_unwind_caller_id (get_current_frame ()),
- step_frame_id))
+ step_stack_frame_id))
{
CORE_ADDR real_stop_pc;
@@ -3113,13 +3155,7 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (
if (real_stop_pc != 0)
ecs->stop_func_start = real_stop_pc;
- if (
-#ifdef IN_SOLIB_DYNSYM_RESOLVE_CODE
- IN_SOLIB_DYNSYM_RESOLVE_CODE (ecs->stop_func_start)
-#else
- in_solib_dynsym_resolve_code (ecs->stop_func_start)
-#endif
-)
+ if (real_stop_pc != 0 && in_solib_dynsym_resolve_code (real_stop_pc))
{
struct symtab_and_line sr_sal;
init_sal (&sr_sal);
@@ -3267,6 +3303,79 @@ 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 ()), 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 (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 == tss->current_line
+ && call_sal.symtab == tss->current_symtab)
+ step_into_inline_frame (ecs->ptid);
+
+ 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 == tss->current_line
+ && call_sal.symtab == tss->current_symtab)
+ keep_going (ecs);
+ else
+ {
+ 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 ()), step_frame_id)
+ && stepped_in_from (get_current_frame (), step_frame_id))
+ {
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: stepping through inlined function\n");
+
+ if (step_over_calls == STEP_OVER_ALL)
+ keep_going (ecs);
+ else
+ {
+ stop_step = 1;
+ print_stop_reason (END_STEPPING_RANGE, 0);
+ stop_stepping (ecs);
+ }
+ return;
+ }
+
if ((stop_pc == stop_pc_sal.pc)
&& (tss->current_line != stop_pc_sal.line
|| tss->current_symtab != stop_pc_sal.symtab))
@@ -3292,13 +3401,11 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (
step_range_start = stop_pc_sal.pc;
step_range_end = stop_pc_sal.end;
- step_frame_id = get_frame_id (get_current_frame ());
- tss->current_line = stop_pc_sal.line;
- tss->current_symtab = stop_pc_sal.symtab;
+ set_step_info (get_current_frame (), stop_pc_sal);
/* In the case where we just stepped out of a function into the
middle of a line of the caller, continue stepping, but
- step_frame_id must be modified to current frame */
+ step_frame_id must be modified to current frame (above). */
#if 0
/* NOTE: cagney/2003-10-16: I think this frame ID inner test is too
generous. It will trigger on things like a step into a frameless
@@ -3315,13 +3422,6 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (
and we're willing to introduce frame unwind logic into this
function. Fortunately, those days are nearly upon us. */
#endif
- {
- struct frame_info *frame = get_current_frame ();
- struct frame_id current_frame = get_frame_id (frame);
- if (!(frame_id_inner (get_frame_arch (frame), current_frame,
- step_frame_id)))
- step_frame_id = current_frame;
- }
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: keep going\n");
@@ -3767,17 +3867,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 ())
@@ -4337,6 +4426,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;
@@ -4383,6 +4473,7 @@ save_inferior_status (int restore_stack_
inf_status->step_range_start = step_range_start;
inf_status->step_range_end = step_range_end;
inf_status->step_frame_id = step_frame_id;
+ inf_status->step_stack_frame_id = step_stack_frame_id;
inf_status->step_over_calls = step_over_calls;
inf_status->stop_after_trap = stop_after_trap;
inf_status->stop_soon = stop_soon;
@@ -4435,6 +4526,7 @@ restore_inferior_status (struct inferior
step_range_start = inf_status->step_range_start;
step_range_end = inf_status->step_range_end;
step_frame_id = inf_status->step_frame_id;
+ step_stack_frame_id = inf_status->step_stack_frame_id;
step_over_calls = inf_status->step_over_calls;
stop_after_trap = inf_status->stop_after_trap;
stop_soon = inf_status->stop_soon;
Index: src/gdb/inline-frame.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/inline-frame.c 2008-07-18 08:51:03.000000000 -0400
@@ -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: src/gdb/inline-frame.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/inline-frame.h 2008-07-18 07:37:30.000000000 -0400
@@ -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: src/gdb/minsyms.c
===================================================================
--- src.orig/gdb/minsyms.c 2008-07-18 07:37:21.000000000 -0400
+++ src/gdb/minsyms.c 2008-07-18 07:37:30.000000000 -0400
@@ -761,7 +761,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: src/gdb/s390-tdep.c
===================================================================
--- src.orig/gdb/s390-tdep.c 2008-07-18 07:37:21.000000000 -0400
+++ src/gdb/s390-tdep.c 2008-07-18 07:37:30.000000000 -0400
@@ -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: src/gdb/stack.c
===================================================================
--- src.orig/gdb/stack.c 2008-07-18 07:37:21.000000000 -0400
+++ src/gdb/stack.c 2008-07-18 07:37:30.000000000 -0400
@@ -44,6 +44,7 @@
#include "solib.h"
#include "valprint.h"
#include "gdbthread.h"
+#include "inline-frame.h"
#include "gdb_assert.h"
#include <ctype.h>
@@ -97,6 +98,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
@@ -533,7 +558,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,
@@ -587,7 +612,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
@@ -608,8 +633,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)
@@ -675,7 +705,7 @@ print_frame (struct frame_info *frame, i
frame_relative_level (frame));
}
if (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 ();
@@ -1005,8 +1035,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)),
@@ -1467,7 +1499,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);
@@ -1538,7 +1572,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);
@@ -1805,6 +1841,9 @@ return_command (char *retval_exp, int fr
thisfun = get_frame_function (get_selected_frame ("No selected frame."));
+ 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: src/gdb/symtab.c
===================================================================
--- src.orig/gdb/symtab.c 2008-07-18 07:37:21.000000000 -0400
+++ src/gdb/symtab.c 2008-07-18 07:37:30.000000000 -0400
@@ -1367,10 +1367,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;
}
@@ -2596,6 +2599,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);
@@ -2634,6 +2638,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_BFD_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;
}
@@ -3636,6 +3659,24 @@ language_search_unquoted_string (char *t
return p;
}
+static void
+completion_list_add_fields (struct symbol *sym, char *sym_text,
+ int sym_text_len, char *text, char *word)
+{
+ if (SYMBOL_CLASS (sym) == LOC_TYPEDEF)
+ {
+ struct type *t = SYMBOL_TYPE (sym);
+ enum type_code c = TYPE_CODE (t);
+ int j;
+
+ if (c == TYPE_CODE_UNION || c == TYPE_CODE_STRUCT)
+ for (j = TYPE_N_BASECLASSES (t); j < TYPE_NFIELDS (t); j++)
+ if (TYPE_FIELD_NAME (t, j))
+ completion_list_add_name (TYPE_FIELD_NAME (t, j),
+ sym_text, sym_text_len, text, word);
+ }
+}
+
char **
default_make_symbol_completion_list (char *text, char *word)
{
@@ -3648,9 +3689,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;
@@ -3760,41 +3801,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. */
@@ -3813,9 +3856,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);
@@ -4194,6 +4234,7 @@ skip_prologue_using_sal (CORE_ADDR func_
struct symtab_and_line prologue_sal;
CORE_ADDR start_pc;
CORE_ADDR end_pc;
+ struct block *bl;
/* Get an initial range for the function. */
find_pc_partial_function (func_addr, NULL, &start_pc, &end_pc);
@@ -4218,6 +4259,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: src/gdb/symtab.h
===================================================================
--- src.orig/gdb/symtab.h 2008-07-18 07:37:21.000000000 -0400
+++ src/gdb/symtab.h 2008-07-18 07:37:30.000000000 -0400
@@ -568,9 +568,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;
@@ -601,6 +610,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: src/gdb/testsuite/gdb.base/break.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/break.exp 2008-07-18 07:37:21.000000000 -0400
+++ src/gdb/testsuite/gdb.base/break.exp 2008-07-18 07:37:30.000000000 -0400
@@ -883,6 +883,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: src/gdb/testsuite/gdb.cp/annota2.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.cp/annota2.exp 2008-07-18 07:37:21.000000000 -0400
+++ src/gdb/testsuite/gdb.cp/annota2.exp 2008-07-18 07:37:30.000000000 -0400
@@ -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: src/gdb/testsuite/gdb.opt/Makefile.in
===================================================================
--- src.orig/gdb/testsuite/gdb.opt/Makefile.in 2008-07-18 07:37:21.000000000 -0400
+++ src/gdb/testsuite/gdb.opt/Makefile.in 2008-07-25 12:06:43.000000000 -0400
@@ -1,7 +1,7 @@
VPATH = @srcdir@
srcdir = @srcdir@
-EXECUTABLES = hello/hello
+EXECUTABLES = clobbered-registers-O2 inline-bt inline-cmds inline-locals
MISCELLANEOUS =
Index: src/gdb/testsuite/gdb.opt/clobbered-registers-O2.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.opt/clobbered-registers-O2.exp 2008-07-18 07:37:21.000000000 -0400
+++ src/gdb/testsuite/gdb.opt/clobbered-registers-O2.exp 2008-07-18 07:37:30.000000000 -0400
@@ -2,7 +2,7 @@
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
+# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
@@ -11,8 +11,7 @@
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# This file is part of the gdb testsuite.
Index: src/gdb/testsuite/gdb.opt/inline-bt.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.opt/inline-bt.c 2008-07-18 07:37:30.000000000 -0400
@@ -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: src/gdb/testsuite/gdb.opt/inline-bt.exp
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.opt/inline-bt.exp 2008-07-18 07:37:30.000000000 -0400
@@ -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: src/gdb/testsuite/gdb.opt/inline-cmds.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.opt/inline-cmds.c 2008-07-18 07:37:30.000000000 -0400
@@ -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: src/gdb/testsuite/gdb.opt/inline-cmds.exp
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.opt/inline-cmds.exp 2008-07-18 07:37:30.000000000 -0400
@@ -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: src/gdb/testsuite/gdb.opt/inline-locals.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.opt/inline-locals.c 2008-07-18 07:37:30.000000000 -0400
@@ -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: src/gdb/testsuite/gdb.opt/inline-locals.exp
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.opt/inline-locals.exp 2008-07-18 07:37:30.000000000 -0400
@@ -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: src/gdb/testsuite/gdb.opt/inline-markers.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.opt/inline-markers.c 2008-07-18 07:37:30.000000000 -0400
@@ -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: src/gdb/testsuite/lib/gdb.exp
===================================================================
--- src.orig/gdb/testsuite/lib/gdb.exp 2008-07-18 07:37:21.000000000 -0400
+++ src/gdb/testsuite/lib/gdb.exp 2008-07-18 07:37:30.000000000 -0400
@@ -1358,6 +1358,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: src/gdb/valops.c
===================================================================
--- src.orig/gdb/valops.c 2008-07-18 07:37:21.000000000 -0400
+++ src/gdb/valops.c 2008-07-18 07:37:30.000000000 -0400
@@ -974,7 +974,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: src/gdb/NEWS
===================================================================
--- src.orig/gdb/NEWS 2008-07-18 07:37:21.000000000 -0400
+++ src/gdb/NEWS 2008-07-18 07:37:30.000000000 -0400
@@ -17,6 +17,9 @@ For instance, consider:
If the user types TAB at the end of this command line, the available
completions will be "f1" and "f2".
+* Inlined functions are now supported. They show up in backtraces, and
+the "step", "next", and "finish" commands handle them automatically.
+
* New remote packets
qSearch:memory:
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch
[not found] ` <200807251446.m6PEkfwc027635@brahms.sibelius.xs4all.nl>
@ 2008-07-25 17:47 ` Daniel Jacobowitz
2009-03-31 3:06 ` Tom Tromey
0 siblings, 1 reply; 38+ messages in thread
From: Daniel Jacobowitz @ 2008-07-25 17:47 UTC (permalink / raw)
To: gdb-patches; +Cc: Mark Kettenis
Resending to list.
On Fri, Jul 25, 2008 at 04:46:41PM +0200, Mark Kettenis wrote:
> > I don't think I turned unwinding upside down; in fact I think the
> > unwinder changes are fairly unintrusive, but that may be because I saw
> > the earlier versions of this patch... The only substantive change is
> > the short-circuit test in get_prev_frame_1 to accomodate the
> > upside-down ID construction, which is itself isolated to inline
> > frames. Are there other specific changes that you're worried about,
> > or is it just the ID construction?
>
> It's the ID construction that I'm worried about. It is the very core
> of the unwinding code. I really think your diff violates the most
> fundamental principle of this bit of code and in that way, makes it
> much harder to understand it.
I don't understand what you mean when you say this makes the generic
code any harder to understand. Can you point to lines for me?
All of the code relating to inline ID construction is in
inline-frame.c. Here's the only related bit in frame.c:
+ /* 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);
Inlining is definitely a special case, yes. I'm happy to write
internals documentation and more comments. I'll do that whatever we
decide.
> > The separate block address and code address, and the use of the
> > enclosing function's stack address, are necessary because of
> > frame_id_stack_eq. Given two frame IDs, there are several places we
> > must be able to find out if they belong to the same real stack frame.
>
> Could you explain why? Is this because when we're stopped in an
> inlined function, you want to give the user access to local variables
> in the function in which that code was inlined? I'm not sure that's
> the right thing to do.
No, we don't do that. You use the frame chain normally - go "up" to
get them.
It's mostly in inferior control, dealing with the problem that
function calls nest more completely than inlined functions. For
instance, when we are handling "next" and enter a function call we
unwind the function's caller's frame ID and compare it to the ID of
the frame where we were previously stepping. If the function call was
the first instruction of an inlined function, then the frame ID will
have changed - we have to recognize what's happened so that we can
correctly skip over the function call.
We can do this by saving the non-inilned frame ID instead of peering
at the internal structure of frame IDs, though.
> > That would make inlining dependent on .debug_frame. But I don't see
> > why it should be. We get inlining from .debug_info instead, and
> > conceptually there's no reason we couldn't get it from another debug
> > format.
>
> The different sections are just a way to organize things, they're not
> really different debug formats.
But they're used independently. Sometimes we have to ignore
.debug_frame, either because it's missing or because of a bug in a
deployed compiler - both of these have happened to me in the past
year. I don't want to make inlining entwined with the use of the
dwarf2 frame unwinder if I can avoid it.
There's another approach we could take. We could delay creation of
the inlined frames until after the normal sniffer has run. This
requires stitching them in afterwards and adjusting frame->level.
I've tried this; it was much more intrusive and complicated
than the current version. That's a variant of the fix Jan made here,
on top of my original (broken) merge of this code to HEAD:
http://sourceware.org/ml/gdb-patches/2008-06/msg00379.html
Search for frame-unwind.c and struct inline_cache.
If you can think of a way to do this that doesn't involve complicating
the generic unwind machinery - exactly what we're both trying to avoid
- I'll give it another shot.
> > While inlined frames no longer need to have the same stack address as
> > their caller, in my latest version, they do still need a stable stack
> > address. The only ways I see to get one are to unwind to the caller
> > and ask it, or to hard-code and enforce a requirement that there be
> > .debug_frame and ask the dwarf2 unwinder directly. The second seems
> > like a special case of the first, so I'm missing the advantage.
>
> Well, it keeps the generic code straightforward and easier to
> understand. I think that's a majot benefit.
I'm just having trouble understanding how this would help.
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch
2008-07-25 14:26 ` Daniel Jacobowitz
2008-07-25 16:11 ` Daniel Jacobowitz
@ 2008-07-26 5:58 ` Eli Zaretskii
1 sibling, 0 replies; 38+ messages in thread
From: Eli Zaretskii @ 2008-07-26 5:58 UTC (permalink / raw)
To: Daniel Jacobowitz; +Cc: gdb-patches
> Date: Fri, 25 Jul 2008 10:25:51 -0400
> From: Daniel Jacobowitz <drow@false.org>
> Cc: gdb-patches@sourceware.org
>
> > This is IMO too negative: you state several problems and never hint on
> > how to work around them. Please consider suggesting such workarounds
> > in each one of the above 3 situations. Using breakpoints and returned
> > values reported by `finish' are two very fundamental debugging
> > techniques; telling the readers that they are simply unavailable will
> > lead them to believe debugging code that uses inlined functions is
> > next to impossible.
>
> Is this better?
>
> 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
Yes, thanks.
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch
2008-07-25 17:47 ` Daniel Jacobowitz
@ 2009-03-31 3:06 ` Tom Tromey
2009-03-31 20:49 ` Mark Kettenis
0 siblings, 1 reply; 38+ messages in thread
From: Tom Tromey @ 2009-03-31 3:06 UTC (permalink / raw)
To: gdb-patches; +Cc: Mark Kettenis
Resurrecting an old thread...
Mark> It's the ID construction that I'm worried about. It is the very core
Mark> of the unwinding code. I really think your diff violates the most
Mark> fundamental principle of this bit of code and in that way, makes it
Mark> much harder to understand it.
Daniel> I don't understand what you mean when you say this makes the generic
Daniel> code any harder to understand. Can you point to lines for me?
[...]
Daniel> If you can think of a way to do this that doesn't involve complicating
Daniel> the generic unwind machinery - exactly what we're both trying to avoid
Daniel> - I'll give it another shot.
Mark, could you answer Daniel's questions? This patch has been in
limbo since last July. I'd like to at least know what needs to be
done to move forward on this.
FWIW, we're shipping this in Archer. I think other organizations are
shipping it as well. Debugging inlined functions nicely is a
frequently requested feature; I answer questions about it on irc at
least once a week.
Tom
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch
2009-03-31 3:06 ` Tom Tromey
@ 2009-03-31 20:49 ` Mark Kettenis
2009-03-31 22:13 ` Daniel Jacobowitz
2009-04-20 15:49 ` Daniel Jacobowitz
0 siblings, 2 replies; 38+ messages in thread
From: Mark Kettenis @ 2009-03-31 20:49 UTC (permalink / raw)
To: tromey; +Cc: gdb-patches
> From: Tom Tromey <tromey@redhat.com>
> Date: Mon, 30 Mar 2009 17:00:56 -0600
>
> Resurrecting an old thread...
>
> Mark> It's the ID construction that I'm worried about. It is the very core
> Mark> of the unwinding code. I really think your diff violates the most
> Mark> fundamental principle of this bit of code and in that way, makes it
> Mark> much harder to understand it.
>
> Daniel> I don't understand what you mean when you say this makes the generic
> Daniel> code any harder to understand. Can you point to lines for me?
>
> [...]
>
> Daniel> If you can think of a way to do this that doesn't involve complicating
> Daniel> the generic unwind machinery - exactly what we're both trying to avoid
> Daniel> - I'll give it another shot.
>
> Mark, could you answer Daniel's questions? This patch has been in
> limbo since last July. I'd like to at least know what needs to be
> done to move forward on this.
A bit hard after more than 9 months :(.
IIRC Daniels diff really turned the whole stack unwinding upside down.
> FWIW, we're shipping this in Archer. I think other organizations are
> shipping it as well. Debugging inlined functions nicely is a
> frequently requested feature; I answer questions about it on irc at
> least once a week.
I agree that it is an important feature. I'll see if I can wrap my
head around this again now that I'm not in an airport every other
week again.
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch
2009-03-31 20:49 ` Mark Kettenis
@ 2009-03-31 22:13 ` Daniel Jacobowitz
2009-04-20 15:49 ` Daniel Jacobowitz
1 sibling, 0 replies; 38+ messages in thread
From: Daniel Jacobowitz @ 2009-03-31 22:13 UTC (permalink / raw)
To: Mark Kettenis; +Cc: tromey, gdb-patches
On Tue, Mar 31, 2009 at 10:42:18PM +0200, Mark Kettenis wrote:
> I agree that it is an important feature. I'll see if I can wrap my
> head around this again now that I'm not in an airport every other
> week again.
Thanks!
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch
2009-03-31 20:49 ` Mark Kettenis
2009-03-31 22:13 ` Daniel Jacobowitz
@ 2009-04-20 15:49 ` Daniel Jacobowitz
2009-04-20 15:54 ` Jan Kratochvil
2009-04-22 22:04 ` Mark Kettenis
1 sibling, 2 replies; 38+ messages in thread
From: Daniel Jacobowitz @ 2009-04-20 15:49 UTC (permalink / raw)
To: Mark Kettenis; +Cc: tromey, gdb-patches
On Tue, Mar 31, 2009 at 10:42:18PM +0200, Mark Kettenis wrote:
> I agree that it is an important feature. I'll see if I can wrap my
> head around this again now that I'm not in an airport every other
> week again.
Hi Mark,
Did you have a chance to look at this? If not, maybe we can find a
third party to review the patch? If someone volunteers, I'll refresh
the diffs from our internal tree. Nothing algorithmic has changed,
but we have a few bug fixes and improved testcases.
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch
2009-04-20 15:49 ` Daniel Jacobowitz
@ 2009-04-20 15:54 ` Jan Kratochvil
2009-06-27 18:01 ` Daniel Jacobowitz
2009-04-22 22:04 ` Mark Kettenis
1 sibling, 1 reply; 38+ messages in thread
From: Jan Kratochvil @ 2009-04-20 15:54 UTC (permalink / raw)
To: Daniel Jacobowitz; +Cc: Mark Kettenis, tromey, gdb-patches
On Mon, 20 Apr 2009 17:49:09 +0200, Daniel Jacobowitz wrote:
> Nothing algorithmic has changed,
> but we have a few bug fixes and improved testcases.
Fedora also has various fixes on top of it:
http://cvs.fedora.redhat.com/viewvc/rpms/gdb/devel/gdb-6.8-inlining-addon.patch?view=co
Regards,
Jan
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch
2009-04-20 15:49 ` Daniel Jacobowitz
2009-04-20 15:54 ` Jan Kratochvil
@ 2009-04-22 22:04 ` Mark Kettenis
2009-04-23 3:17 ` Eli Zaretskii
` (3 more replies)
1 sibling, 4 replies; 38+ messages in thread
From: Mark Kettenis @ 2009-04-22 22:04 UTC (permalink / raw)
To: drow; +Cc: tromey, gdb-patches
> Date: Mon, 20 Apr 2009 11:49:09 -0400
> From: Daniel Jacobowitz <drow@false.org>
>
> On Tue, Mar 31, 2009 at 10:42:18PM +0200, Mark Kettenis wrote:
> > I agree that it is an important feature. I'll see if I can wrap my
> > head around this again now that I'm not in an airport every other
> > week again.
>
> Hi Mark,
>
> Did you have a chance to look at this? If not, maybe we can find a
> third party to review the patch? If someone volunteers, I'll refresh
> the diffs from our internal tree. Nothing algorithmic has changed,
> but we have a few bug fixes and improved testcases.
Yes, I did spent some of my time looking at the diff again, and
thinking about the proper solution for the problem at hand. And I'm
afraid that it has reinforced my initial opinion about your approach.
Let me explain.
The fundamental principle behind unwinding the stack is, given the
register state of a process/thread executing code in a certain
function, figuring out the register state at the point where this
function was called. This register state may be incomplete because
information that is no longer necessary for the program to continue
past this point has been lost. However we assume that at least the
stack pointer and program counter can be recovered (if not we
terminate the unwinding).
I firmly believe that if we want to add the capability to unwind
through inlined functions, this fundamental principle should hold for
inlined functions as well. This means that if we can detect that the
current register state describes a process executing an inlined
function we should faithfully reconstruct the register state for the
call site of that inlined function. If I understand things correctly,
the DW_TAG_inlined_subroutine tag provides information about the call
site, which gives us the unwound program counter. But in order to
reconstruct the complete register state, we need more information.
The only viable source of that information is something like DWARF
CFI; you don't stand a chance of doing a proper job here by doing
instruction analysis.
That is why I still think integrating inline support in the DWARF
unwinder is the right way forward. I really think that unwinding
further down the stack to get information for an inlined frame like
you do in your diff is wrong, and I feel like you're digging a fairly
deep hole for yourself here.
That said, the proper response from me would be to hack up something
closer to what the right solution would be. But I'm afraid I simply
don't have the motivation to do such a thing anymore. The environment
in which GDB is being developed seems to have changed. It feels like
I'm the only one who is still hacking on code for fun, and that I'm
being surrounded by people for which writing GDB code is their job.
It sometimes feels like most of the effort goes into supporting
debugging embedded targets hosted on non-free operating systems. And
I increasingly find myself fighting against proposals to change
development practices (C++, XML) and a mentality to fix bugs by
slapping more code into GDB instead of taking some time and exploring
alternative solutions. Perhaps the time has come for me to simply
give up.
Mark
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch
2009-04-22 22:04 ` Mark Kettenis
@ 2009-04-23 3:17 ` Eli Zaretskii
2009-04-23 5:56 ` Stan Shebs
` (2 subsequent siblings)
3 siblings, 0 replies; 38+ messages in thread
From: Eli Zaretskii @ 2009-04-23 3:17 UTC (permalink / raw)
To: Mark Kettenis; +Cc: drow, tromey, gdb-patches
> Date: Thu, 23 Apr 2009 00:03:32 +0200 (CEST)
> From: Mark Kettenis <mark.kettenis@xs4all.nl>
> CC: tromey@redhat.com, gdb-patches@sourceware.org
>
> Perhaps the time has come for me to simply give up.
I hope, for GDB's sake, that time has not come yet.
Thanks.
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch
2009-04-22 22:04 ` Mark Kettenis
2009-04-23 3:17 ` Eli Zaretskii
@ 2009-04-23 5:56 ` Stan Shebs
2009-04-23 12:48 ` Daniel Jacobowitz
2009-04-24 21:44 ` Thiago Jung Bauermann
3 siblings, 0 replies; 38+ messages in thread
From: Stan Shebs @ 2009-04-23 5:56 UTC (permalink / raw)
To: Mark Kettenis; +Cc: drow, tromey, gdb-patches
Mark Kettenis wrote:
> That said, the proper response from me would be to hack up something
> closer to what the right solution would be. But I'm afraid I simply
> don't have the motivation to do such a thing anymore. The environment
> in which GDB is being developed seems to have changed. It feels like
> I'm the only one who is still hacking on code for fun, and that I'm
> being surrounded by people for which writing GDB code is their job.
> It sometimes feels like most of the effort goes into supporting
> debugging embedded targets hosted on non-free operating systems.
I think that's a little unfair. Red Hat and IBM people are doing only
Linux of course, and the majority of CodeSourcery's GDB work is
targeting either Linux or bare metal. What non-free operating systems
are you thinking of? DICOS? I'm looking at gdb/NEWS, and that's the only
non-free OS I'm seeing that has gotten any work at all in the past
several years. If anything, proprietary OS support is a rapidly
dwindling part of GDB work, certainly far less than it was in the 1990s,
when much time went into handling the combined idiosyncrasies of HP/UX,
Solaris, AIX, OSF/1, Irix, etc.
As for people hacking for fun vs pay, I don't really know how much
that's changed. In 1999, when we moved to a public repository, it was a
little bit of an act of faith that contributors were going to show up,
because prior to that almost of the GDB hacking was done by Cygnus
employees on the clock, and the patch submissions I received were almost
all related to the submitters' jobs. So if there was a golden age of
hacking GDB for fun, it had to be either before 1994 when I started with
it, or after 2000.
But to speak to motivation, if you're burned out on GDB, for whatever
reason, then take a break and kick back! I've spent 25 years - most of
my adult life - working on free software. I've been enthused, and burnt
out, and enthused again, multiple times. One of the beauties of the
large collaborative project is that any one of us can go do something
else for a while, and the project keeps right on going. In fact, that's
one way for participation to become non-fun, when we start to feel
indispensable, or like the sole gatekeeper for a part of the system, and
we feel like we have to do things out of necessity rather than free
choice. So each of us should self-monitor, and if we're starting to feel
stressed, disengage a bit and trust that our colleagues will carry on.
Stan
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch
2009-04-22 22:04 ` Mark Kettenis
2009-04-23 3:17 ` Eli Zaretskii
2009-04-23 5:56 ` Stan Shebs
@ 2009-04-23 12:48 ` Daniel Jacobowitz
2009-06-18 17:55 ` Tom Tromey
2009-04-24 21:44 ` Thiago Jung Bauermann
3 siblings, 1 reply; 38+ messages in thread
From: Daniel Jacobowitz @ 2009-04-23 12:48 UTC (permalink / raw)
To: Mark Kettenis; +Cc: tromey, gdb-patches
On Thu, Apr 23, 2009 at 12:03:32AM +0200, Mark Kettenis wrote:
> I firmly believe that if we want to add the capability to unwind
> through inlined functions, this fundamental principle should hold for
> inlined functions as well. This means that if we can detect that the
> current register state describes a process executing an inlined
> function we should faithfully reconstruct the register state for the
> call site of that inlined function. If I understand things correctly,
> the DW_TAG_inlined_subroutine tag provides information about the call
> site, which gives us the unwound program counter. But in order to
> reconstruct the complete register state, we need more information.
> The only viable source of that information is something like DWARF
> CFI; you don't stand a chance of doing a proper job here by doing
> instruction analysis.
DWARF CFI is not going to help with this; it only deals with 'real'
(i.e. not inlined) functions. There's no saved register state
from the virtual entry point. There isn't even an indicator
of where inlining occurs. Are you suggesting enhancing
the CFI information? I suspect the extra register state
would be generally unretrievable.
DWARF .debug_info is more helpful, but you still can't get a PC for
the call site; just the file and line number. I was able to
reconstruct a somewhat reliable entry address based on the block tree,
which may include DW_AT_entry_pc for the inlined function, but this
concept is quite fuzzy; there may not be a single entry point for the
inlined blocks. This leads to wrong scopes and wrong display of local
variables if you claim the outermost function is at the "unwound"
PC. For instance you'll get DW_AT_frame_base wrong if it's a
location list, and the inlined function pushes callee arguments
onto the stack.
In either case there's no instruction analysis involved.
An example might help. I'll use x86 - the nice thing about x86 for
this is that scheduling (particularly post-register-allocation) is
not too important, so blocks tend to be contiguous. That improves
debugger behavior and makes examples a heck of a lot easier to
read. Suppose you compile this:
inline int func1(void)
{
bar ();
return x * y;
}
inline int func2(void)
{
return x * func1 ();
}
You get this:
00000050 <func2>:
50: 55 push %ebp
51: 89 e5 mov %esp,%ebp
53: 83 ec 08 sub $0x8,%esp
56: e8 fc ff ff ff call 57 <func2+0x7>
57: R_386_PC32 bar
5b: a1 00 00 00 00 mov 0x0,%eax
5c: R_386_32 x
60: 0f af c0 imul %eax,%eax
63: 0f af 05 00 00 00 00 imul 0x0,%eax
66: R_386_32 y
6a: c9 leave
6b: c3 ret
6c: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
00000000 00000010 ffffffff CIE
Version: 1
Augmentation: ""
Code alignment factor: 1
Data alignment factor: -4
Return address column: 8
DW_CFA_def_cfa: r4 (esp) ofs 4
DW_CFA_offset: r8 (eip) at cfa-4
DW_CFA_nop
DW_CFA_nop
00000074 00000014 00000000 FDE cie=00000000 pc=00000050..0000006c
DW_CFA_advance_loc: 1 to 00000051
DW_CFA_def_cfa_offset: 8
DW_CFA_offset: r5 (ebp) at cfa-8
DW_CFA_advance_loc: 2 to 00000053
DW_CFA_def_cfa_register: r5 (ebp)
<1><8c>: Abbrev Number: 8 (DW_TAG_subprogram)
<8d> DW_AT_external : 1
<8e> DW_AT_name : (indirect string, offset: 0x73): func1
<92> DW_AT_decl_file : 1
<93> DW_AT_decl_line : 24
<94> DW_AT_prototyped : 1
<95> DW_AT_type : <0x9a>
<99> DW_AT_inline : 3 (declared as inline and inlined)
<1><b2>: Abbrev Number: 10 (DW_TAG_subprogram)
<b3> DW_AT_external : 1
<b4> DW_AT_name : (indirect string, offset: 0x79): func2
<b8> DW_AT_decl_file : 1
<b9> DW_AT_decl_line : 30
<ba> DW_AT_prototyped : 1
<bb> DW_AT_type : <0x9a>
<bf> DW_AT_inline : 3 (declared as inline and inlined)
<c0> DW_AT_sibling : <0xc6>
<2><c4>: Abbrev Number: 4 (DW_TAG_lexical_block)
<1><c6>: Abbrev Number: 6 (DW_TAG_subprogram)
<c7> DW_AT_abstract_origin: <0xb2>
<cb> DW_AT_low_pc : 0x50
<cf> DW_AT_high_pc : 0x6c
<d3> DW_AT_frame_base : 0xb0 (location list)
<d7> DW_AT_sibling : <0xeb>
<2><db>: Abbrev Number: 7 (DW_TAG_inlined_subroutine)
<dc> DW_AT_abstract_origin: <0x8c>
<e0> DW_AT_low_pc : 0x56
<e4> DW_AT_high_pc : 0x6a
<e8> DW_AT_call_file : 1
So at PC == 0x5b, we know that we're in func2 inlined into func1.
Location lists in func1 don't depend on the PC in this example,
but they would if -fomit-frame-pointer and bar took stack arguments.
So we have to use the correct PC to look up DW_AT_frame_base; func1's
state at 0x56 and 0x5b are not necessarily the same. When blocks
interleave due to scheduling after inlining, you also lose track of
which local variables have values and which are unavailable.
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch
2009-04-22 22:04 ` Mark Kettenis
` (2 preceding siblings ...)
2009-04-23 12:48 ` Daniel Jacobowitz
@ 2009-04-24 21:44 ` Thiago Jung Bauermann
3 siblings, 0 replies; 38+ messages in thread
From: Thiago Jung Bauermann @ 2009-04-24 21:44 UTC (permalink / raw)
To: Mark Kettenis; +Cc: drow, tromey, gdb-patches
El jue, 23-04-2009 a las 00:03 +0200, Mark Kettenis escribió:
> It feels like
> I'm the only one who is still hacking on code for fun, and that I'm
> being surrounded by people for which writing GDB code is their job.
I'm hacking on GDB both for fun, and also as part of my job. More than
half of the total effort I spent on Python scripting support in GDB has
been on my spare time (I'm grateful though that IBM let me do this also
on my office hours (not all of it though, I have other tasks to do too),
it sure helps a lot). It's just that all of my posts to the GDB mailing
lists are from my IBM e-mail address.
I also do have fun when hacking GDB on my work time. :-)
> and a mentality to fix bugs by
> slapping more code into GDB instead of taking some time and exploring
> alternative solutions.
I'm honestly interested in knowing if my fixes feel like "code slapping"
instead of elegant solutions, and willing to change my approach if it
doesn't imply an herculean effort to change half the toolchain plus
several standards to implement it.
> Perhaps the time has come for me to simply give up.
I join Eli to say that I hope not!
--
[]'s
Thiago Jung Bauermann
IBM Linux Technology Center
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch
2009-04-23 12:48 ` Daniel Jacobowitz
@ 2009-06-18 17:55 ` Tom Tromey
2009-06-20 9:57 ` Mark Kettenis
0 siblings, 1 reply; 38+ messages in thread
From: Tom Tromey @ 2009-06-18 17:55 UTC (permalink / raw)
To: Mark Kettenis; +Cc: gdb-patches
Ping.
Mark> I firmly believe that if we want to add the capability to unwind
Mark> through inlined functions, this fundamental principle should hold for
Mark> inlined functions as well. This means that if we can detect that the
Mark> current register state describes a process executing an inlined
Mark> function we should faithfully reconstruct the register state for the
Mark> call site of that inlined function. If I understand things correctly,
Mark> the DW_TAG_inlined_subroutine tag provides information about the call
Mark> site, which gives us the unwound program counter. But in order to
Mark> reconstruct the complete register state, we need more information.
Mark> The only viable source of that information is something like DWARF
Mark> CFI; you don't stand a chance of doing a proper job here by doing
Mark> instruction analysis.
Daniel> DWARF CFI is not going to help with this; it only deals with 'real'
Daniel> (i.e. not inlined) functions. There's no saved register state
Daniel> from the virtual entry point. There isn't even an indicator
Daniel> of where inlining occurs. Are you suggesting enhancing
Daniel> the CFI information? I suspect the extra register state
Daniel> would be generally unretrievable.
It has been two months since this response. I think Daniel addressed
your objections, at least to the extent they are addressable given the
existing Dwarf specification.
I would like it if this patch did not stay in limbo any longer. I
think that goes for others, too: according to Joel's summit notes,
this patch was explicitly asked about by attendees.
At a minimum, could you answer his question above? Thanks.
Tom
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch
2009-06-18 17:55 ` Tom Tromey
@ 2009-06-20 9:57 ` Mark Kettenis
2009-06-20 19:28 ` Samuel Bronson
0 siblings, 1 reply; 38+ messages in thread
From: Mark Kettenis @ 2009-06-20 9:57 UTC (permalink / raw)
To: tromey; +Cc: gdb-patches
> From: Tom Tromey <tromey@redhat.com>
> Date: Thu, 18 Jun 2009 11:55:42 -0600
>
> Ping.
>
> Mark> I firmly believe that if we want to add the capability to unwind
> Mark> through inlined functions, this fundamental principle should hold for
> Mark> inlined functions as well. This means that if we can detect that the
> Mark> current register state describes a process executing an inlined
> Mark> function we should faithfully reconstruct the register state for the
> Mark> call site of that inlined function. If I understand things correctly,
> Mark> the DW_TAG_inlined_subroutine tag provides information about the call
> Mark> site, which gives us the unwound program counter. But in order to
> Mark> reconstruct the complete register state, we need more information.
> Mark> The only viable source of that information is something like DWARF
> Mark> CFI; you don't stand a chance of doing a proper job here by doing
> Mark> instruction analysis.
>
> Daniel> DWARF CFI is not going to help with this; it only deals with 'real'
> Daniel> (i.e. not inlined) functions. There's no saved register state
> Daniel> from the virtual entry point. There isn't even an indicator
> Daniel> of where inlining occurs. Are you suggesting enhancing
> Daniel> the CFI information? I suspect the extra register state
> Daniel> would be generally unretrievable.
>
> It has been two months since this response. I think Daniel addressed
> your objections, at least to the extent they are addressable given the
> existing Dwarf specification.
>
> I would like it if this patch did not stay in limbo any longer. I
> think that goes for others, too: according to Joel's summit notes,
> this patch was explicitly asked about by attendees.
>
> At a minimum, could you answer his question above? Thanks.
Sorry, I have been travelling for the last month. I still think the
inline unwinder should not bend the rules we established for
unwinders. But since I'm obviously not capable of coming up with a
better way to do this, please use your own judgements about this diff.
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch
2009-06-20 9:57 ` Mark Kettenis
@ 2009-06-20 19:28 ` Samuel Bronson
0 siblings, 0 replies; 38+ messages in thread
From: Samuel Bronson @ 2009-06-20 19:28 UTC (permalink / raw)
To: Mark Kettenis; +Cc: tromey, gdb-patches
At Sat, 20 Jun 2009 11:56:40 +0200 (CEST),
Mark Kettenis wrote:
>
> > From: Tom Tromey <tromey@redhat.com>
> > Date: Thu, 18 Jun 2009 11:55:42 -0600
> >
> > Ping.
> >
> > Mark> I firmly believe that if we want to add the capability to unwind
> > Mark> through inlined functions, this fundamental principle should hold for
> > Mark> inlined functions as well. This means that if we can detect that the
> > Mark> current register state describes a process executing an inlined
> > Mark> function we should faithfully reconstruct the register state for the
> > Mark> call site of that inlined function. If I understand things correctly,
> > Mark> the DW_TAG_inlined_subroutine tag provides information about the call
> > Mark> site, which gives us the unwound program counter. But in order to
> > Mark> reconstruct the complete register state, we need more information.
> > Mark> The only viable source of that information is something like DWARF
> > Mark> CFI; you don't stand a chance of doing a proper job here by doing
> > Mark> instruction analysis.
> >
> > Daniel> DWARF CFI is not going to help with this; it only deals with 'real'
> > Daniel> (i.e. not inlined) functions. There's no saved register state
> > Daniel> from the virtual entry point. There isn't even an indicator
> > Daniel> of where inlining occurs. Are you suggesting enhancing
> > Daniel> the CFI information? I suspect the extra register state
> > Daniel> would be generally unretrievable.
FWIW, there is no "register state for the call site" of an inlined
function. The inlined function's variables compete for stack slots and
registers with those of the calling function, and their code may be
interspersed and re-ordered. The closest thing to "register state for
the call site" that exists in this context is, well, the same register
state as that seen in the inline function.
This doesn't have anything to do with limitations of the debug
information, either; the compiler could not have such a concept as
"register state for the call site" without seriously compromising
the benefits of doing the inlining in the first place.
> > It has been two months since this response. I think Daniel addressed
> > your objections, at least to the extent they are addressable given the
> > existing Dwarf specification.
> >
> > I would like it if this patch did not stay in limbo any longer. I
> > think that goes for others, too: according to Joel's summit notes,
> > this patch was explicitly asked about by attendees.
> >
> > At a minimum, could you answer his question above? Thanks.
>
> Sorry, I have been travelling for the last month. I still think the
> inline unwinder should not bend the rules we established for
> unwinders. But since I'm obviously not capable of coming up with a
> better way to do this, please use your own judgements about this diff.
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch
2009-04-20 15:54 ` Jan Kratochvil
@ 2009-06-27 18:01 ` Daniel Jacobowitz
2009-06-28 10:16 ` Jan Kratochvil
0 siblings, 1 reply; 38+ messages in thread
From: Daniel Jacobowitz @ 2009-06-27 18:01 UTC (permalink / raw)
To: Jan Kratochvil; +Cc: Mark Kettenis, tromey, gdb-patches
On Mon, Apr 20, 2009 at 05:54:05PM +0200, Jan Kratochvil wrote:
> On Mon, 20 Apr 2009 17:49:09 +0200, Daniel Jacobowitz wrote:
> > Nothing algorithmic has changed,
> > but we have a few bug fixes and improved testcases.
>
> Fedora also has various fixes on top of it:
> http://cvs.fedora.redhat.com/viewvc/rpms/gdb/devel/gdb-6.8-inlining-addon.patch?view=co
Thanks for the link. Some of these I've already got fixes for.
For instance, the changes in breakpoint.c about returning from a
function to an inlined frame; I fixed it somewhere else, but we
did discover that problem.
Others, particularly the testsuite changes, I don't understand. I'd
need to see a compiler that failed to work out why the changes were
needed. So if you still need patches after the latest version of the
patch is checked in, please let me know and I'll reproduce the
failures.
current_pc_is_notcurrent is interesting. Do I have the scenario
right?
* function() calls inlined() calls other()
* finish from other()
* show the end of inlined() instead of the next line of function()
I can't figure out if we should do this or not. It does seem useful.
But that's not where we are; we're showing the previous call site
instead of the next instruction.
I think we should consider it as a general change for finish instead
of specific to inlining. The comments in your patch suggested that
too.
I merged the read_type_die fix, thanks.
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch
2009-06-27 18:01 ` Daniel Jacobowitz
@ 2009-06-28 10:16 ` Jan Kratochvil
2009-06-28 13:35 ` Daniel Jacobowitz
2009-06-30 16:11 ` Tom Tromey
0 siblings, 2 replies; 38+ messages in thread
From: Jan Kratochvil @ 2009-06-28 10:16 UTC (permalink / raw)
To: gdb-patches; +Cc: Mark Kettenis, Tom Tromey
On Sat, 27 Jun 2009 20:01:22 +0200, Daniel Jacobowitz wrote:
> On Mon, Apr 20, 2009 at 05:54:05PM +0200, Jan Kratochvil wrote:
> > Fedora also has various fixes on top of it:
> > http://cvs.fedora.redhat.com/viewvc/rpms/gdb/devel/gdb-6.8-inlining-addon.patch?view=co
...
> Others, particularly the testsuite changes, I don't understand.
<smile>As I did not understand how the testsuites could work before</smile>
- it is clear it was due to some compiler differences. I should retest the
changes against FSF GCC first, my only concern was Fedora GCC for that patch.
> current_pc_is_notcurrent is interesting. Do I have the scenario
> right?
Yes, thanks for checking it (the changed testcases cover it somewhere).
> * function() calls inlined() calls other()
> * finish from other()
> * show the end of inlined() instead of the next line of function()
>
> I can't figure out if we should do this or not. It does seem useful.
> But that's not where we are; we're showing the previous call site
> instead of the next instruction.
>
> I think we should consider it as a general change for finish instead
> of specific to inlining. The comments in your patch suggested that
> too.
My goal was to:
(1) Keep non-inlined GDB behavior the same.
(2) Keep the majority of inlined cases behavior the same as non-inlined one.
For FSF GDB we should break the rule (1).
(gdb) l
1 /* 1 */ int func (void) { return 1; }
2 /* 2 */ int main (void) { int x, y;
3 /* 3 */ func ();
4 /* 4 */ y = -1;
5 /* 5 */ x = func ();
6 /* 6 */ y = 1;
7 /* 7 */ return 0; }
Temporary breakpoint 1, main () at finish.c:3
3 /* 3 */ func ();
(gdb) step
func () at finish.c:1
1 /* 1 */ int func (void) { return 1; }
(gdb) finish
Run till exit from #0 func () at finish.c:1
main () at finish.c:4
4 /* 4 */ y = -1;
Value returned is $1 = 1
### Here `finish' is at the _next_ line of the call. I would expect rather:
### (gdb) finish
### Run till exit from #0 func () at finish.c:1
### main () at finish.c:4
### 3 /* 3 */ func ();
### Value returned is $1 = 1
### Whether this or that case is shown is also dependent on the current
### architecture - currently the behavior differs depending of whether there
### is at least one instruction of the same source line after the call
### instruction. Next `step' will have to do _nothing_ to the inferior, just
### display the next line in GDB.
(gdb) step
5 /* 5 */ x = func ();
(gdb) step
func () at finish.c:1
1 /* 1 */ int func (void) { return 1; }
(gdb) finish
Run till exit from #0 func () at finish.c:1
0x00000000004004a0 in main () at finish.c:5
5 /* 5 */ x = func ();
Value returned is $2 = 1
(gdb) step
6 /* 6 */ y = 1;
(gdb)
TODOlisted to submit a patch for this behavior change of non-inlined functions
(which will cover even the inlined ones).
Thanks for the merge,
Jan
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch
2009-06-28 10:16 ` Jan Kratochvil
@ 2009-06-28 13:35 ` Daniel Jacobowitz
2009-06-30 16:11 ` Tom Tromey
1 sibling, 0 replies; 38+ messages in thread
From: Daniel Jacobowitz @ 2009-06-28 13:35 UTC (permalink / raw)
To: Jan Kratochvil; +Cc: gdb-patches, Mark Kettenis, Tom Tromey
On Sun, Jun 28, 2009 at 12:16:21PM +0200, Jan Kratochvil wrote:
> <smile>As I did not understand how the testsuites could work before</smile>
> - it is clear it was due to some compiler differences. I should retest the
> changes against FSF GCC first, my only concern was Fedora GCC for that patch.
OK, that makes sense. I should hunt up a Fedora system compiler for
testing.
> ### Here `finish' is at the _next_ line of the call. I would expect rather:
> ### (gdb) finish
> ### Run till exit from #0 func () at finish.c:1
> ### main () at finish.c:4
> ### 3 /* 3 */ func ();
> ### Value returned is $1 = 1
> ### Whether this or that case is shown is also dependent on the current
> ### architecture - currently the behavior differs depending of whether there
> ### is at least one instruction of the same source line after the call
> ### instruction. Next `step' will have to do _nothing_ to the inferior, just
> ### display the next line in GDB.
Thanks for the example. I think this would be a nice change.
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch
2009-06-28 10:16 ` Jan Kratochvil
2009-06-28 13:35 ` Daniel Jacobowitz
@ 2009-06-30 16:11 ` Tom Tromey
2009-06-30 16:50 ` Jan Kratochvil
1 sibling, 1 reply; 38+ messages in thread
From: Tom Tromey @ 2009-06-30 16:11 UTC (permalink / raw)
To: Jan Kratochvil; +Cc: gdb-patches
>>>>> "Jan" == Jan Kratochvil <jan.kratochvil@redhat.com> writes:
Jan> ### Whether this or that case is shown is also dependent on the current
Jan> ### architecture - currently the behavior differs depending of whether there
Jan> ### is at least one instruction of the same source line after the call
Jan> ### instruction. Next `step' will have to do _nothing_ to the inferior, just
Jan> ### display the next line in GDB.
This is interesting. Alexandre Oliva has talked about something
similar -- a dwarf extension which would let gdb users "step" through
a sequence of source statements, even when the compiler has optimized
them away. The idea would be to emit debug info describing the
variables virtually, and this "step" would simply advance through the
debuginfo without updating the PC. (I'm not really doing this
justice, but I can't find Alexandre's note to the gcc list atm.)
About your proposal: IIUC, it is trying a bit to hide more of the
underlying reality from the user. Can it really work in all cases?
I'm wondering about things like multiple levels of inlining (you may
need several do-nothing steps?); multiple levels of inlining where the
user wants to "finish" out of each one (you may need a do-nothing
finish as well?); or inlining that results in a tail-call optimization
being applied (there's no good spot to return to).
Does any of this make sense? I can't tell if I actually understand :-)
Tom
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FYI] Inlining support, rough patch
2009-06-30 16:11 ` Tom Tromey
@ 2009-06-30 16:50 ` Jan Kratochvil
0 siblings, 0 replies; 38+ messages in thread
From: Jan Kratochvil @ 2009-06-30 16:50 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
On Tue, 30 Jun 2009 18:11:14 +0200, Tom Tromey wrote:
> Alexandre Oliva has talked about something similar -- a dwarf extension
> which would let gdb users "step" through a sequence of source statements,
> even when the compiler has optimized them away.
.debug_loc key (+any attributes referencing PC) would need to not to state
only PC but a PC + sourceline pair. In an extreme case during the debugging
session PC does not need to change and no ptrace() needs to be called.
> I'm wondering about things like multiple levels of inlining (you may
> need several do-nothing steps?);
This part is step_into_inline_frame() already in the FSF GDB HEAD by Daniel J.
> multiple levels of inlining where the user wants to "finish" out of each one
> (you may need a do-nothing finish as well?); or inlining that results in
> a tail-call optimization being applied (there's no good spot to return to).
This is a Fedora extension of the patch by step_out_of_inline_frame() there.
Just I find .debug_line from GCC wrong a bit but I hope it gets fixed by the
is_stmt + VTA gcc patches so I have not bugreported the current state.
Thanks,
Jan
^ permalink raw reply [flat|nested] 38+ messages in thread
end of thread, other threads:[~2009-06-30 16:50 UTC | newest]
Thread overview: 38+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-06-13 19:39 [FYI] Inlining support, rough patch Daniel Jacobowitz
2008-06-13 19:43 ` Daniel Jacobowitz
2008-06-23 12:03 ` Jan Kratochvil
2008-06-23 14:23 ` Daniel Jacobowitz
2008-07-02 19:15 ` Daniel Jacobowitz
2008-07-03 11:22 ` [FYI] Inlining support, rough patch [break-by-function-name] Jan Kratochvil
2008-07-03 16:01 ` Daniel Jacobowitz
2008-07-12 7:41 ` Jan Kratochvil
2008-07-08 0:12 ` [FYI] Inlining support, rough patch Daniel Jacobowitz
2008-07-15 19:21 ` Daniel Jacobowitz
2008-07-17 23:53 ` Mark Kettenis
2008-07-18 13:03 ` Daniel Jacobowitz
[not found] ` <200807251446.m6PEkfwc027635@brahms.sibelius.xs4all.nl>
2008-07-25 17:47 ` Daniel Jacobowitz
2009-03-31 3:06 ` Tom Tromey
2009-03-31 20:49 ` Mark Kettenis
2009-03-31 22:13 ` Daniel Jacobowitz
2009-04-20 15:49 ` Daniel Jacobowitz
2009-04-20 15:54 ` Jan Kratochvil
2009-06-27 18:01 ` Daniel Jacobowitz
2009-06-28 10:16 ` Jan Kratochvil
2009-06-28 13:35 ` Daniel Jacobowitz
2009-06-30 16:11 ` Tom Tromey
2009-06-30 16:50 ` Jan Kratochvil
2009-04-22 22:04 ` Mark Kettenis
2009-04-23 3:17 ` Eli Zaretskii
2009-04-23 5:56 ` Stan Shebs
2009-04-23 12:48 ` Daniel Jacobowitz
2009-06-18 17:55 ` Tom Tromey
2009-06-20 9:57 ` Mark Kettenis
2009-06-20 19:28 ` Samuel Bronson
2009-04-24 21:44 ` Thiago Jung Bauermann
2008-07-18 2:02 ` Paul Pluzhnikov
2008-07-18 3:07 ` Daniel Jacobowitz
2008-07-20 14:41 ` Eli Zaretskii
2008-07-25 13:54 ` Eli Zaretskii
2008-07-25 14:26 ` Daniel Jacobowitz
2008-07-25 16:11 ` Daniel Jacobowitz
2008-07-26 5:58 ` Eli Zaretskii
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox