[FYI] Inlining support, rough patch

Daniel Jacobowitz
2008-06-13T15:28:44Z

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