Re: [FYI] Inlining support, rough patch

Jan Kratochvil
2008-06-23T11:54:55Z

[-- Attachment #1: Type: text/plain, Size: 416 bytes --]

Hi,

here are some fixes on top of it. I find now your patch useful on top of CVS
HEAD. I expect some of its problems were probably due to the patch separation
from the CodeSourcery patchset.

On x86+x86_64+ppc64 with gcc-4.3.1-2 (Fedora) there are no regressions.
On x86+x86_64 with gcc-4.3.1-2 (Fedora) the provided tests PASS.
(On ppc64 the tests FAIL as multi-PC breakpoints do not work there.)

Thanks,
Jan

[-- Attachment #2: gdb-inline-works3.patch --]
[-- Type: text/plain, Size: 24866 bytes --]

diff -up -u -X /home/jkratoch/.diffi.list -rupxCVS sources-codesourceryinline-gdb/blockframe.c sources/gdb/blockframe.c
--- sources-codesourceryinline-gdb/blockframe.c 2008-06-20 15:59:08.000000000 +0200
+++ sources/gdb/blockframe.c 2008-06-23 00:51:34.000000000 +0200
@@ -72,18 +72,22 @@ get_frame_block (struct frame_info *fram
if (bl == NULL)
return NULL;

- next_frame = get_next_frame (frame);
+ inline_count = 0;
+
+ /* Any possibly inlined frames (inlined functions) behind NORMAL_FRAME
+ (non-inlined function) will already be at a different code location and
+ not found by BLOCK_FOR_PC at this PC moment. */
+ for (next_frame = get_next_frame (frame);
+ next_frame && get_frame_type (next_frame) == INLINE_FRAME;
+ next_frame = get_next_frame (next_frame))
+ inline_count++;
+
+ /* Simulate some most-inner inlined frames which were suppressed.
+ INLINE_SKIPPED_FRAMES will never exceed the number of the most-inner
+ frames. But if we are unwinding already outer frames from some
+ non-inlined frame this frames skipping cannot apply. */
if (next_frame == NULL)
- inline_count = inline_skipped_frames ();
- else
- {
- inline_count = 0;
- while (next_frame != NULL && get_frame_type (next_frame) == INLINE_FRAME)
- {
- inline_count++;
- next_frame = get_next_frame (next_frame);
- }
- }
+ inline_count += inline_skipped_frames ();

while (inline_count > 0)
{
diff -up -u -X /home/jkratoch/.diffi.list -rupxCVS sources-codesourceryinline-gdb/breakpoint.c sources/gdb/breakpoint.c
--- sources-codesourceryinline-gdb/breakpoint.c 2008-06-20 15:59:08.000000000 +0200
+++ sources/gdb/breakpoint.c 2008-06-22 23:45:57.000000000 +0200
@@ -3076,6 +3076,11 @@ bpstat_stop_status (CORE_ADDR bp_addr, p
bs->print = 0;
}
bs->commands = copy_command_lines (bs->commands);
+
+ /* We display the innermost inlined frame at a breakpont as it gives to
+ most of the available information. */
+ if (b->type != bp_until && b->type != bp_finish)
+ set_skipped_inline_frames (0);
}

/* Print nothing for this entry if we dont stop or if we dont print. */
diff -up -u -X /home/jkratoch/.diffi.list -rupxCVS sources-codesourceryinline-gdb/frame-unwind.c sources/gdb/frame-unwind.c
--- sources-codesourceryinline-gdb/frame-unwind.c 2008-06-20 15:59:09.000000000 +0200
+++ sources/gdb/frame-unwind.c 2008-06-22 23:53:09.000000000 +0200
@@ -93,15 +93,38 @@ frame_unwind_find_by_frame (struct frame
struct frame_unwind_table *table = gdbarch_data (gdbarch, frame_unwind_data);
struct frame_unwind_table_entry *entry;
struct cleanup *old_cleanup;
+ const struct frame_unwind *inline_unwinder = NULL;
+
for (entry = table->list; entry != NULL; entry = entry->next)
{
struct cleanup *old_cleanup;

+ /* We delay the INLINE_FRAME unwinders as last as they need to base their
+ decisions on their outer frames. The inline unwinding sniffer cannot
+ access its outer frames as any frame must be free to fully access its
+ inner frames which leads to deadlocks. Therefore we first do
+ a regular unwind first and then override it (nullify the unwinding
+ operations) by setting it to the inlining unwinder (if it was detected
+ for such frame). */
+ if (entry->unwinder->type == INLINE_FRAME)
+ {
+ gdb_assert (inline_unwinder == NULL);
+ inline_unwinder = entry->unwinder;
+ continue;
+ }
old_cleanup = frame_prepare_for_sniffer (this_frame, entry->unwinder);
if (entry->unwinder->sniffer (entry->unwinder, this_frame,
this_cache))
{
discard_cleanups (old_cleanup);
+ if (inline_unwinder)
+ {
+ /* Pass the complete information about the regular unwinder. */
+ if (inline_unwinder->sniffer (entry->unwinder, this_frame,
+ this_cache))
+
+ return inline_unwinder;
+ }
return entry->unwinder;
}
do_cleanups (old_cleanup);
diff -up -u -X /home/jkratoch/.diffi.list -rupxCVS sources-codesourceryinline-gdb/frame.c sources/gdb/frame.c
--- sources-codesourceryinline-gdb/frame.c 2008-06-20 15:59:09.000000000 +0200
+++ sources/gdb/frame.c 2008-06-22 18:01:02.000000000 +0200
@@ -422,12 +422,12 @@ frame_id_inner (struct gdbarch *gdbarch,
if (!l.stack_addr_p || !r.stack_addr_p)
/* Like NaN, any operation involving an invalid ID always fails. */
inner = 0;
- else if (l.inline_depth > r.inline_depth
+ else if (l.inline_depth < r.inline_depth
&& l.stack_addr == r.stack_addr
&& l.code_addr_p == r.code_addr_p
- && l.code_addr == r.code_addr
+ && (!l.code_addr_p || l.code_addr == r.code_addr)
&& l.special_addr_p == r.special_addr_p
- && l.special_addr == r.special_addr)
+ && (!l.special_addr_p || l.special_addr == r.special_addr))
{
/* Same function, different inlined functions. */
struct block *lb, *rb;
@@ -1697,7 +1697,7 @@ find_frame_sal (struct frame_info *frame
sym = inline_skipped_symbol ();

init_sal (sal);
- if (SYMBOL_LINE (sym) != 0)
+ if (sym && SYMBOL_LINE (sym) != 0)
{
sal->symtab = SYMBOL_SYMTAB (sym);
sal->line = SYMBOL_LINE (sym);
diff -up -u -X /home/jkratoch/.diffi.list -rupxCVS sources-codesourceryinline-gdb/frame.h sources/gdb/frame.h
--- sources-codesourceryinline-gdb/frame.h 2008-06-20 15:59:09.000000000 +0200
+++ sources/gdb/frame.h 2008-06-22 23:54:08.000000000 +0200
@@ -214,8 +214,11 @@ enum frame_type
/* A fake frame, created by GDB when performing an inferior function
call. */
DUMMY_FRAME,
- /* A frame representing an inlined function, associated with an
- upcoming (next, inner, younger) NORMAL_FRAME. */
+ /* A frame representing an inlined function, associated with an upcoming
+ (next, inner, younger) NORMAL_FRAME. Its unwinder execution is delayed
+ after the other unwinders (see FRAME_UNWIND_FIND_BY_FRAME). This
+ unwinder expects the regular unwinder and its existing cache to be passed
+ as the parameters. */
INLINE_FRAME,
/* In a signal handler, various OSs handle this in various ways.
The main thing is that the frame may be far from normal. */
diff -up -u -X /home/jkratoch/.diffi.list -rupxCVS sources-codesourceryinline-gdb/infcmd.c sources/gdb/infcmd.c
--- sources-codesourceryinline-gdb/infcmd.c 2008-06-20 15:59:09.000000000 +0200
+++ sources/gdb/infcmd.c 2008-06-17 20:57:03.000000000 +0200
@@ -758,7 +758,13 @@ step_1 (int skip_subroutines, int single
if (!target_can_async_p ())
{
for (; count > 0; count--)
- step_once (skip_subroutines, single_inst, count, thread);
+ {
+ step_once (skip_subroutines, single_inst, count, thread);
+
+ /* Unexpected breakpoint terminates our multistepping. */
+ if (!stop_step)
+ break;
+ }

do_cleanups (cleanups);
}
diff -up -u -X /home/jkratoch/.diffi.list -rupxCVS sources-codesourceryinline-gdb/inline-frame.c sources/gdb/inline-frame.c
--- sources-codesourceryinline-gdb/inline-frame.c 2008-06-20 15:59:09.000000000 +0200
+++ sources/gdb/inline-frame.c 2008-06-23 00:54:53.000000000 +0200
@@ -39,20 +39,46 @@ static CORE_ADDR inline_skip_pc;
call site of the current frame. */
static struct symbol *inline_skip_symbol;

+/* Transparently wrap the former regular unwinder. Allocated by
+ FRAME_OBSTACK_ZALLOC. */
+struct inline_cache
+ {
+ const struct frame_unwind *unwind_regular;
+ void *cache_regular;
+ };
+
+/* We just must remap wrapped THIS_CACHE. */
+
+static void
+inline_frame_dealloc (struct frame_info *self, void *this_cache)
+{
+ struct inline_cache *inline_cache = this_cache;
+
+ /* The same condition as in REINIT_FRAME_CACHE. */
+ if (inline_cache->unwind_regular->dealloc_cache
+ && inline_cache->cache_regular)
+ inline_cache->unwind_regular->dealloc_cache (self,
+ inline_cache->cache_regular);
+}
+
static void
inline_frame_this_id (struct frame_info *this_frame,
void **this_cache,
struct frame_id *this_id)
{
struct symbol *func;
+ struct frame_info *next_frame;
+ struct inline_cache *inline_cache = *this_cache;

- /* In order to have a stable frame ID for a given inline function,
- we must get the stack / special addresses from the underlying
- real frame's this_id method. So we must call get_prev_frame.
- Because we are inlined into some function, there must be previous
- frames, so this is safe - as long as we're careful not to
- create any cycles. */
- *this_id = get_frame_id (get_prev_frame (this_frame));
+ gdb_assert (get_frame_type (this_frame) == INLINE_FRAME);
+
+ /* In order to have a stable frame ID for a given inline function, we must
+ get the stack / special addresses from the underlying real frame's this_id
+ method. We must not call GET_PREV_FRAME as each frame expects its inner
+ frames to be already fully accessible. This would open a can of worms
+ with unwinding deadlocks. */
+ inline_cache->unwind_regular->this_id (this_frame,
+ &inline_cache->cache_regular, this_id);

/* We need a valid frame ID, so we need to be based on a valid
frame. FSF submission NOTE: this would be a good assertion to
@@ -68,13 +94,27 @@ inline_frame_this_id (struct frame_info
func = get_frame_function (this_frame);
gdb_assert (func != NULL);
(*this_id).block_addr = BLOCK_START (SYMBOL_BLOCK_VALUE (func));
- (*this_id).inline_depth++;
+
+ /* The regular unwinder knows no INLINE_DEPTH. */
+ gdb_assert ((*this_id).inline_depth == 0);
+
+ /* Inlined frames (inlined functions) can never exist behind a normal frame
+ (non-inlined function). Any possible inlined frames (inlined functions)
+ behind such frame will already be at a different code location and not
+ found by BLOCK_FOR_PC at this PC moment. */
+ for (next_frame = this_frame;
+ next_frame && get_frame_type (next_frame) == INLINE_FRAME;
+ next_frame = get_next_frame (next_frame))
+ (*this_id).inline_depth++;
+
+ gdb_assert ((*this_id).inline_depth > 0);
}

static struct value *
inline_frame_prev_register (struct frame_info *this_frame, void **this_cache,
int regnum)
{
+ /* Copy all the registers unchanged. */
return frame_unwind_got_register (this_frame, regnum, regnum);
}

@@ -90,12 +130,22 @@ inline_frame_sniffer (const struct frame
struct block *frame_block, *cur_block;
int depth;
struct frame_info *next_frame;
+ struct inline_cache *inline_cache;
+
+ /* INLINE_FRAME unwinder is an exception, it is passed the regular unwinder
+ detected for this frame. */
+ gdb_assert (self != inline_frame_unwind);

this_pc = get_frame_address_in_block (this_frame);
frame_block = block_for_pc (this_pc);
if (frame_block == NULL)
return 0;

+ /* INLINE_SKIP_SYMBOL does not need to be set for each specific frame. But
+ * it needs to be recalculated after STEP_INTO_INLINE_FRAME according to new
+ * INLINE_SKIP_FRAMES - therefore SET_SKIPPED_INLINE_FRAMES is too early. */
+ inline_skip_symbol = NULL;
+
/* Calculate DEPTH, the number of inlined functions at this
location. */
depth = 0;
@@ -104,13 +154,27 @@ inline_frame_sniffer (const struct frame
{
if (block_inlined_p (cur_block))
depth++;
+ if (depth == inline_skip_frames && inline_skip_symbol == NULL)
+ inline_skip_symbol = BLOCK_FUNCTION (cur_block);

cur_block = BLOCK_SUPERBLOCK (cur_block);
}

+ /* There are inlined functions here. Check how many of them already have
+ frames. Any possibly inlined frames (inlined functions) behind
+ NORMAL_FRAME (non-inlined function) will already be at a different code
+ location and not found by BLOCK_FOR_PC at this PC moment. */
+ for (next_frame = get_next_frame (this_frame);
+ next_frame && get_frame_type (next_frame) == INLINE_FRAME;
+ next_frame = get_next_frame (next_frame))
+ {
+ gdb_assert (depth > 0);
+ depth--;
+ }
+
/* Check whether we were requested to skip some frames, so they
can be stepped into later. */
- if (inline_skip_frames > 0 && frame_relative_level (this_frame) == 0)
+ if (inline_skip_frames > 0 && next_frame == NULL)
{
if (this_pc != inline_skip_pc)
inline_skip_frames = 0;
@@ -121,36 +185,30 @@ inline_frame_sniffer (const struct frame
}
}

- if (depth == 0)
- return 0;
-
- /* There are inlined functions here. Check how many of them already
- have frames. */
- for (next_frame = get_next_frame (this_frame);
- next_frame && get_frame_type (next_frame) == INLINE_FRAME;
- next_frame = get_next_frame (next_frame))
- {
- gdb_assert (depth > 0);
- depth--;
- }
-
/* If all the inlined functions already have frames, then pass to the
normal unwinder for this PC. */
if (depth == 0)
return 0;

+ /* Transparently wrap the former regular unwinder. */
+ inline_cache = frame_obstack_zalloc (sizeof (*inline_cache));
+ inline_cache->unwind_regular = self;
+ inline_cache->cache_regular = *this_cache;
+ *this_cache = inline_cache;
+
/* If the next frame is an inlined function, but not the outermost, then
we are the next outer. If it is not an inlined function, then we
are the innermost inlined function of a different real frame. */
return 1;
}

-const struct frame_unwind inline_frame_unwinder = {
+static const struct frame_unwind inline_frame_unwinder = {
INLINE_FRAME,
inline_frame_this_id,
inline_frame_prev_register,
NULL,
- inline_frame_sniffer
+ inline_frame_sniffer,
+ inline_frame_dealloc
};

const struct frame_unwind *const inline_frame_unwind = {
@@ -189,7 +247,6 @@ set_skipped_inline_frames (int skip_ok)
{
CORE_ADDR this_pc;
struct block *frame_block, *cur_block;
- struct symbol *last_sym = NULL;
int skip_count = 0;

if (!skip_ok)
@@ -219,19 +276,23 @@ set_skipped_inline_frames (int skip_ok)
of BLOCK_START. */
if (BLOCK_START (cur_block) == this_pc
|| block_starting_point_at (this_pc, cur_block))
+ skip_count++;
+ else
{
- skip_count++;
- last_sym = BLOCK_FUNCTION (cur_block);
+ /* Here we will stop at any possible inlining after we
+ already crossed any first non-inlined function. We could
+ possibly detect such functions generating NORMAL_FRAME
+ by `!block_inlined_p && BLOCK_FUNCTION' (as
+ `!block_inlined_p && !BLOCK_FUNCTION' are just anonymous
+ local { ... } blocks). */
+ break;
}
- else
- break;
}
cur_block = BLOCK_SUPERBLOCK (cur_block);
}
}

inline_skip_pc = this_pc;
- inline_skip_symbol = last_sym;

if (inline_skip_frames != skip_count)
{
diff -up -u -X /home/jkratoch/.diffi.list -rupxCVS sources-codesourceryinline-gdb/testsuite/gdb.cp/annota2.exp sources/gdb/testsuite/gdb.cp/annota2.exp
--- sources-codesourceryinline-gdb/testsuite/gdb.cp/annota2.exp 2008-06-20 15:58:04.000000000 +0200
+++ sources/gdb/testsuite/gdb.cp/annota2.exp 2008-06-18 18:44:11.000000000 +0200
@@ -119,13 +119,14 @@ gdb_expect {
# continue until exit
# this will test:
# annotate-exited
+# `a.x is 1' is asynchronous regarding to `frames-invalid'.
#
send_gdb "continue\n"
gdb_expect {
- -re "\r\n\032\032post-prompt\r\nContinuing.\r\n\r\n\032\032starting\r\n\r\n\032\032frames-invalid\r\na.x is 1\r\n\r\n\032\032exited 0\r\n\r\nProgram exited normally.\r\n\r\n\032\032stopped\r\n$gdb_prompt$" \
+ -re "\r\n\032\032post-prompt\r\nContinuing.\r\n\r\n\032\032starting\r\n(\r\n\032\032frames-invalid\r\n)*a.x is 1\r\n(\r\n\032\032frames-invalid\r\n)*\r\n\032\032exited 0\r\n\r\nProgram exited normally.\r\n\r\n\032\032stopped\r\n$gdb_prompt$" \
{ pass "continue until exit" }
- -re ".*$gdb_prompt$" { fail "continue to exit" }
- timeout { fail "continue to exit (timeout)" }
+ -re ".*$gdb_prompt$" { fail "continue until exit" }
+ timeout { fail "continue until exit (timeout)" }
}

#
diff -up -u -X /home/jkratoch/.diffi.list -rupxCVS sources-codesourceryinline-gdb/testsuite/gdb.opt/inline-bt.c sources/gdb/testsuite/gdb.opt/inline-bt.c
--- sources-codesourceryinline-gdb/testsuite/gdb.opt/inline-bt.c 2008-06-20 15:59:09.000000000 +0200
+++ sources/gdb/testsuite/gdb.opt/inline-bt.c 2008-06-22 23:56:29.000000000 +0200
@@ -15,7 +15,9 @@
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA. */

-int x, y;
+/* VOLATILE forces all the inlining to happen as otherwise the whole program
+ gets optimized by CSE to just simple assignments of the results. */
+volatile int x, y;
volatile int result;

void bar(void)
diff -up -u -X /home/jkratoch/.diffi.list -rupxCVS sources-codesourceryinline-gdb/testsuite/gdb.opt/inline-bt.exp sources/gdb/testsuite/gdb.opt/inline-bt.exp
--- sources-codesourceryinline-gdb/testsuite/gdb.opt/inline-bt.exp 2008-06-20 15:59:09.000000000 +0200
+++ sources/gdb/testsuite/gdb.opt/inline-bt.exp 2008-06-21 18:05:40.000000000 +0200
@@ -43,13 +43,14 @@ gdb_breakpoint $line1

gdb_test "continue" ".*set breakpoint 1 here.*" "continue to bar (1)"
gdb_test "backtrace" "#0 bar.*#1 .*main.*" "backtrace from bar (1)"
-gdb_test "info frame" ".*called by frame.*" "bar not inlined"
+gdb_test "info frame" ".*inlined into frame.*" "bar inlined"

-gdb_test "continue" ".*set breakpoint 1 here.*" "continue to bar (2)"
-gdb_test "backtrace" "#0 bar.*#1 .*func1.*#2 .*main.*" \
- "backtrace from bar (2)"
-gdb_test "up" "#1 .*func1.*" "up from bar (2)"
-gdb_test "info frame" ".*inlined into frame.*" "func1 inlined (2)"
+# gcc-4.3.1 omits the line number information for (2).
+#gdb_test "continue" ".*set breakpoint 1 here.*" "continue to bar (2)"
+#gdb_test "backtrace" "#0 bar.*#1 .*func1.*#2 .*main.*" \
+# "backtrace from bar (2)"
+#gdb_test "up" "#1 .*func1.*" "up from bar (2)"
+#gdb_test "info frame" ".*inlined into frame.*" "func1 inlined (2)"

gdb_test "continue" ".*set breakpoint 1 here.*" "continue to bar (3)"
gdb_test "backtrace" "#0 bar.*#1 .*func1.*#2 .*func2.*#3 .*main.*" \
diff -up -u -X /home/jkratoch/.diffi.list -rupxCVS sources-codesourceryinline-gdb/testsuite/gdb.opt/inline-cmds.c sources/gdb/testsuite/gdb.opt/inline-cmds.c
--- sources-codesourceryinline-gdb/testsuite/gdb.opt/inline-cmds.c 2008-06-20 15:59:09.000000000 +0200
+++ sources/gdb/testsuite/gdb.opt/inline-cmds.c 2008-06-23 00:18:26.000000000 +0200
@@ -15,7 +15,9 @@
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA. */

-int x, y;
+/* VOLATILE forces all the inlining to happen as otherwise the whole program
+ gets optimized by CSE to just simple assignments of the results. */
+volatile int x, y;
volatile int result;

void bar(void)
@@ -23,6 +25,9 @@ void bar(void)
x += y; /* set breakpoint 1 here */
}

+#ifdef __GNUC__
+__attribute__((__noinline__))
+#endif
void marker(void)
{
x += y; /* set breakpoint 2 here */
@@ -44,6 +49,25 @@ inline void func3(void)
bar ();
}

+#ifdef __GNUC__
+__attribute__((__noinline__))
+#endif
+void noinline(void)
+{
+ bar (); /* inlined */
+}
+
+
+inline void outer_inline1(void)
+{
+ noinline ();
+}
+
+inline void outer_inline2(void)
+{
+ outer_inline1 ();
+}
+
int main (void)
{ /* start of main */
int val;
@@ -75,7 +99,9 @@ int main (void)
marker ();
func1 ();
func3 ();
- marker ();
+ marker (); /* set breakpoint 6 here */
+
+ outer_inline2 ();

return 0;
}
diff -up -u -X /home/jkratoch/.diffi.list -rupxCVS sources-codesourceryinline-gdb/testsuite/gdb.opt/inline-cmds.exp sources/gdb/testsuite/gdb.opt/inline-cmds.exp
--- sources-codesourceryinline-gdb/testsuite/gdb.opt/inline-cmds.exp 2008-06-20 15:59:09.000000000 +0200
+++ sources/gdb/testsuite/gdb.opt/inline-cmds.exp 2008-06-23 00:37:02.000000000 +0200
@@ -63,7 +63,7 @@ gdb_test "info frame" ".*inlined into fr

gdb_test "continue" ".*set breakpoint 2 here.*" "continue to marker"
gdb_test "backtrace" "#0 marker.*#1 .*main.*" "backtrace from marker"
-gdb_test "info frame" ".*called by frame.*" "marker not inlined"
+gdb_test "info frame" ".*\n called by frame.*" "marker not inlined"

# Next, check that we can next over inlined functions. We should not end up
# inside any of them.
@@ -235,3 +235,27 @@ gdb_test "finish" "func1 \\\(\\\);" "fin

gdb_test "step" "bar \\\(\\\);" "step into func1 for finish"
gdb_test "finish" "func3 \\\(\\\);" "finish from func1 to func3"
+
+set line6 [gdb_get_line_number "set breakpoint 6 here"]
+gdb_breakpoint $line6
+gdb_continue_to_breakpoint "before the outer_inline call"
+gdb_test "step" "marker \\\(\\\) at .*" "reach 1 the outer_inline call"
+gdb_test "finish" "main \\\(\\\) at .*outer_inline2 \\\(\\\);" "reach outer_inline2"
+gdb_test "bt" "#0 main.*" "backtrace at main of outer_inline"
+gdb_test "step" "outer_inline2 \\\(\\\) at .*" "enter outer_inline2"
+gdb_test "bt" "#0 outer_inline2.*#1 main.*" "backtrace at outer_inline2"
+gdb_test "step" "outer_inline1 \\\(\\\) at .*" "enter outer_inline1 from outer_inline2"
+gdb_test "bt" "#0 outer_inline1.*#1 outer_inline2.*#2 main.*" "backtrace at outer_inline1"
+gdb_test "step" "noinline \\\(\\\) at .*" "enter noinline from outer_inline1"
+gdb_test "bt" "#0 noinline.*#1 .*outer_inline1.*#2 .*outer_inline2.*#3 main.*" "backtrace at noinline from outer_inline1"
+gdb_test "step" "bar \\\(\\\) at .*" "enter bar from noinline"
+gdb_test "bt" "#0 bar.*#1 noinline.*#2 .*outer_inline1.*#3 .*outer_inline2.*#4 main.*" "backtrace at bar from noinline"
+gdb_test "info frame" ".*inlined into frame.*" "bar from noinline inlined"
+gdb_test "up" "#1 noinline.*" "up to noinline"
+gdb_test "info frame" ".*\n called by frame.*" "noinline from outer_inline1 not inlined"
+gdb_test "up" "#2 .*outer_inline1.*" "up to outer_inline1"
+gdb_test "info frame" ".*inlined into frame.*" "outer_inline1 inlined"
+gdb_test "up" "#3 .*outer_inline2.*" "up to outer_inline2"
+gdb_test "info frame" ".*inlined into frame.*" "outer_inline2 inlined"
+gdb_test "up" "#4 main.*" "up from outer_inline2"
+gdb_test "info frame" ".*\n caller of frame.*" "main not inlined"
diff -up -u -X /home/jkratoch/.diffi.list -rupxCVS sources-codesourceryinline-gdb/testsuite/gdb.opt/inline-locals.c sources/gdb/testsuite/gdb.opt/inline-locals.c
--- sources-codesourceryinline-gdb/testsuite/gdb.opt/inline-locals.c 2008-06-20 15:59:09.000000000 +0200
+++ sources/gdb/testsuite/gdb.opt/inline-locals.c 2008-06-22 23:56:42.000000000 +0200
@@ -15,7 +15,9 @@
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA. */

-int x, y;
+/* VOLATILE forces all the inlining to happen as otherwise the whole program
+ gets optimized by CSE to just simple assignments of the results. */
+volatile int x, y;
volatile int result;
volatile int *array_p;

diff -up -u -X /home/jkratoch/.diffi.list -rupxCVS sources-codesourceryinline-gdb/testsuite/gdb.opt/inline-locals.exp sources/gdb/testsuite/gdb.opt/inline-locals.exp
--- sources-codesourceryinline-gdb/testsuite/gdb.opt/inline-locals.exp 2008-06-20 15:59:09.000000000 +0200
+++ sources/gdb/testsuite/gdb.opt/inline-locals.exp 2008-06-23 00:07:43.000000000 +0200
@@ -53,7 +53,7 @@ if { ! $no_frames } {
gdb_test "up" "#1 .*func1 .* at .*" "up from bar (2)"
gdb_test "info frame" ".*inlined into frame.*" "func1 inlined (2)"
gdb_test "info locals" "array = {.*}" "info locals above bar (2)"
- gdb_test "info args" "arg1 = $decimal" "info args above bar (2)"
+ gdb_test "info args" "arg1 = .*" "info args above bar (2)"
} else {
gdb_test "up" "#1 .*main .* at .*" "up from bar (2)"
gdb_test "info locals" ".*arg1 = 0.*" "info locals above bar (2)"
@@ -61,6 +61,9 @@ if { ! $no_frames } {

# Make sure that locals on the stack are found. This is an array to
# prevent it from living in a register.
+if [test_compiler_info "gcc-4-3-*"] {
+ setup_kfail *-*-* "gcc/debug.optimization"
+}
gdb_test "print array\[0\]" "\\\$$decimal = 0" "print local (2)"

if { ! $no_frames } {
@@ -79,11 +82,14 @@ if { ! $no_frames } {
gdb_test "up" "#1 .*func1 .* at .*" "up from bar (3)"
gdb_test "info frame" ".*inlined into frame.*" "func1 inlined (3)"
gdb_test "info locals" "array = {.*}" "info locals above bar (3)"
- gdb_test "info args" "arg1 = $decimal" "info args above bar (3)"
+ gdb_test "info args" "arg1 = .*" "info args above bar (3)"
} else {
gdb_test "up" "#1 .*main .* at .*" "up from bar (3)"
gdb_test "info locals" ".*arg1 = 1.*" "info locals above bar (3a)"
gdb_test "info locals" ".*arg2 = 184.*" "info locals above bar (3b)"
}
-
+
+if [test_compiler_info "gcc-4-3-*"] {
+ setup_kfail *-*-* "gcc/debug.optimization"
+}
gdb_test "print array\[0\]" "\\\$$decimal = 184" "print local (3)"