diff --git a/gdb-6.8-inlining-addon.patch b/gdb-6.8-inlining-addon.patch new file mode 100644 index 0000000..ebceae0 --- /dev/null +++ b/gdb-6.8-inlining-addon.patch @@ -0,0 +1,677 @@ +infcall.c : +Revert the change of: gdb-6.8-inlining.patch +causing: FAIL: gdb.base/unwindonsignal.exp: unwindonsignal, stack unwound + +resume() -> target_resume() move of clear_inline_frame_state() is for: +gdb.mi/mi-nsmoribund.exp + +Index: gdb-6.8.50.20081214/gdb/testsuite/gdb.opt/inline-bt.c +=================================================================== +--- gdb-6.8.50.20081214.orig/gdb/testsuite/gdb.opt/inline-bt.c 2009-02-09 13:28:48.000000000 +0100 ++++ gdb-6.8.50.20081214/gdb/testsuite/gdb.opt/inline-bt.c 2009-02-09 13:28:49.000000000 +0100 +@@ -13,10 +13,16 @@ + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +-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); ++inline void bar(void) ++{ ++ x += y; /* set breakpoint 1 here */ ++} ++ + + inline int func1(void) + { +Index: gdb-6.8.50.20081214/gdb/testsuite/gdb.opt/inline-bt.exp +=================================================================== +--- gdb-6.8.50.20081214.orig/gdb/testsuite/gdb.opt/inline-bt.exp 2009-02-09 13:28:48.000000000 +0100 ++++ gdb-6.8.50.20081214/gdb/testsuite/gdb.opt/inline-bt.exp 2009-02-09 13:28:49.000000000 +0100 +@@ -41,18 +41,19 @@ if { [skip_inline_frame_tests] } { + return + } + +-set line1 [gdb_get_line_number "set breakpoint 1 here" ${fullsrcfile2}] +-gdb_breakpoint $srcfile2:$line1 ++set line1 [gdb_get_line_number "set breakpoint 1 here" ${srcfile}] ++gdb_breakpoint $srcfile:$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.*" \ +Index: gdb-6.8.50.20081214/gdb/testsuite/gdb.opt/inline-cmds.c +=================================================================== +--- gdb-6.8.50.20081214.orig/gdb/testsuite/gdb.opt/inline-cmds.c 2009-02-09 13:28:48.000000000 +0100 ++++ gdb-6.8.50.20081214/gdb/testsuite/gdb.opt/inline-cmds.c 2009-02-09 13:28:49.000000000 +0100 +@@ -13,13 +13,19 @@ + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +-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); + void marker(void); + void noinline(void); + ++inline void bar(void) ++{ ++ x += y; /* set breakpoint 1 here */ ++} ++ + inline int func1(void) + { + bar (); +Index: gdb-6.8.50.20081214/gdb/testsuite/gdb.opt/inline-cmds.exp +=================================================================== +--- gdb-6.8.50.20081214.orig/gdb/testsuite/gdb.opt/inline-cmds.exp 2009-02-09 13:28:48.000000000 +0100 ++++ gdb-6.8.50.20081214/gdb/testsuite/gdb.opt/inline-cmds.exp 2009-02-09 13:30:16.000000000 +0100 +@@ -45,28 +45,28 @@ if { [skip_inline_frame_tests] } { + + # First, check that the things we expected to be inlined really were, + # and those that shouldn't be weren't. +-set line1 [gdb_get_line_number "set breakpoint 1 here" ${fullsrcfile2}] ++set line1 [gdb_get_line_number "set breakpoint 1 here" ${srcfile2}] + gdb_breakpoint $srcfile2:$line1 +-set line2 [gdb_get_line_number "set breakpoint 2 here" ${fullsrcfile2}] ++set line2 [gdb_get_line_number "set breakpoint 2 here" ${srcfile2}] + gdb_breakpoint $srcfile2:$line2 + +-gdb_test "continue" ".*set breakpoint 1 here.*" "continue to bar (1)" ++gdb_test "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 "info frame" "inlined into frame.*" "func1 inlined (1)" + +-gdb_test "continue" ".*set breakpoint 1 here.*" "continue to bar (2)" ++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 "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 "info frame" "inlined into frame.*" "func2 inlined (2)" + +-gdb_test "continue" ".*set breakpoint 2 here.*" "continue to marker" ++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. +@@ -201,7 +201,7 @@ set line3 [gdb_get_line_number "set brea + gdb_breakpoint $line3 + gdb_continue_to_breakpoint "consecutive func1" + +-gdb_test "next" ".*func1 .*first call.*" "next to first 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 $" { +@@ -224,16 +224,16 @@ set line4 [gdb_get_line_number "set brea + 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" ++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 "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" +@@ -268,12 +268,12 @@ gdb_test "step" "noinline \\\(\\\) at .* + gdb_test "bt" "#0 noinline.*#1 .*outer_inline1.*#2 .*outer_inline2.*#3 main.*" "backtrace at noinline from outer_inline1" + gdb_test "step" "inlined_fn \\\(\\\) at .*" "enter inlined_fn from noinline" + gdb_test "bt" "#0 inlined_fn.*#1 noinline.*#2 .*outer_inline1.*#3 .*outer_inline2.*#4 main.*" "backtrace at inlined_fn from noinline" +-gdb_test "info frame" ".*inlined into frame.*" "inlined_fn from noinline inlined" +-gdb_test "up" "#1 noinline.*" "up to noinline" +-gdb_test "info frame" ".*\n called by frame.*" "noinline from outer_inline1 not inlined" +-gdb_test "up" "#2 .*outer_inline1.*" "up to outer_inline1" +-gdb_test "info frame" ".*inlined into frame.*" "outer_inline1 inlined" +-gdb_test "up" "#3 .*outer_inline2.*" "up to outer_inline2" +-gdb_test "info frame" ".*inlined into frame.*" "outer_inline2 inlined" +-gdb_test "up" "#4 main.*" "up from outer_inline2" +-gdb_test "info frame" ".*\n caller of frame.*" "main not inlined" ++gdb_test "info frame" "inlined into frame.*" "inlined_fn from noinline inlined" ++gdb_test "fini" "" "up to noinline" ++gdb_test "info frame" "\n called by frame.*" "noinline from outer_inline1 not inlined" ++gdb_test "fini" "" "up to outer_inline1" ++gdb_test "info frame" "inlined into frame.*" "outer_inline1 inlined" ++gdb_test "fini" "" "up to outer_inline2" ++gdb_test "info frame" "inlined into frame.*" "outer_inline2 inlined" ++gdb_test "fini" "" "up from outer_inline2" ++gdb_test "info frame" " in main \[^\n\]*\n source language.*" "main not inlined" +Index: gdb-6.8.50.20081214/gdb/testsuite/gdb.opt/inline-locals.c +=================================================================== +--- gdb-6.8.50.20081214.orig/gdb/testsuite/gdb.opt/inline-locals.c 2009-02-09 13:28:48.000000000 +0100 ++++ gdb-6.8.50.20081214/gdb/testsuite/gdb.opt/inline-locals.c 2009-02-09 13:28:49.000000000 +0100 +@@ -13,11 +13,16 @@ + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +-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; + +-void bar(void); ++inline void bar(void) ++{ ++ x += y; /* set breakpoint 1 here */ ++} + + inline int func1(int arg1) + { +Index: gdb-6.8.50.20081214/gdb/testsuite/gdb.opt/inline-locals.exp +=================================================================== +--- gdb-6.8.50.20081214.orig/gdb/testsuite/gdb.opt/inline-locals.exp 2009-02-09 13:28:48.000000000 +0100 ++++ gdb-6.8.50.20081214/gdb/testsuite/gdb.opt/inline-locals.exp 2009-02-09 13:28:49.000000000 +0100 +@@ -43,8 +43,8 @@ if { [skip_inline_var_tests] } { + + set no_frames [skip_inline_frame_tests] + +-set line1 [gdb_get_line_number "set breakpoint 1 here" ${fullsrcfile2}] +-gdb_breakpoint $srcfile2:$line1 ++set line1 [gdb_get_line_number "set breakpoint 1 here" ${srcfile}] ++gdb_breakpoint $srcfile:$line1 + + gdb_test "continue" ".*set breakpoint 1 here.*" "continue to bar (1)" + +@@ -77,6 +77,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 } { +@@ -115,4 +118,7 @@ if { ! $no_frames } { + 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)" +Index: gdb-6.8.50.20081214/gdb/frame.c +=================================================================== +--- gdb-6.8.50.20081214.orig/gdb/frame.c 2009-02-09 13:28:48.000000000 +0100 ++++ gdb-6.8.50.20081214/gdb/frame.c 2009-02-09 13:30:16.000000000 +0100 +@@ -269,7 +269,7 @@ fprint_frame (struct ui_file *file, stru + static struct frame_info * + skip_inlined_frames (struct frame_info *frame) + { +- while (get_frame_type (frame) == INLINE_FRAME) ++ while (frame && get_frame_type (frame) == INLINE_FRAME) + frame = get_prev_frame (frame); + + return frame; +@@ -1697,6 +1697,7 @@ get_frame_address_in_block (struct frame + { + /* A draft address. */ + CORE_ADDR pc = get_frame_pc (this_frame); ++ struct thread_info *tp = inferior_thread (); + + struct frame_info *next_frame = this_frame->next; + +@@ -1739,6 +1740,9 @@ get_frame_address_in_block (struct frame + while in an inlined function, then the code address of the + "calling" normal function should not be adjusted either. */ + ++ if (tp->current_pc_is_notcurrent) ++ return pc - 1; ++ + while (get_frame_type (next_frame) == INLINE_FRAME) + next_frame = next_frame->next; + +@@ -1770,7 +1774,7 @@ find_frame_sal (struct frame_info *frame + sym = inline_skipped_symbol (inferior_ptid); + + init_sal (sal); +- if (SYMBOL_LINE (sym) != 0) ++ if (sym != NULL && SYMBOL_LINE (sym) != 0) + { + sal->symtab = SYMBOL_SYMTAB (sym); + sal->line = SYMBOL_LINE (sym); +Index: gdb-6.8.50.20081214/gdb/breakpoint.c +=================================================================== +--- gdb-6.8.50.20081214.orig/gdb/breakpoint.c 2009-02-09 13:28:48.000000000 +0100 ++++ gdb-6.8.50.20081214/gdb/breakpoint.c 2009-02-09 13:30:16.000000000 +0100 +@@ -57,6 +57,7 @@ + #include "top.h" + #include "wrapper.h" + #include "valprint.h" ++#include "inline-frame.h" + + #include "mi/mi-common.h" + +@@ -2833,10 +2834,24 @@ bpstat_check_breakpoint_conditions (bpst + const struct bp_location *bl = bs->breakpoint_at; + struct breakpoint *b = bl->owner; + +- if (frame_id_p (b->frame_id) +- && !frame_id_eq (b->frame_id, get_stack_frame_id (get_current_frame ()))) +- bs->stop = 0; +- else if (bs->stop) ++ if (frame_id_p (b->frame_id)) ++ { ++ struct frame_info *b_frame, *frame; ++ struct frame_id b_frame_id, current_frame_id; ++ ++ b_frame = frame_find_by_id (b->frame_id); ++ ++ /* get_stack_frame_id normalizes the id to the real non-inlined function ++ by skip_inlined_frames. */ ++ b_frame_id = get_stack_frame_id (b_frame); ++ current_frame_id = get_stack_frame_id (get_current_frame ()); ++ ++ /* Completely different (inlining notwithstanding) frames? */ ++ if (!frame_id_eq (b_frame_id, current_frame_id)) ++ bs->stop = 0; ++ } ++ ++ if (bs->stop) + { + int value_is_zero = 0; + +@@ -2975,6 +2990,12 @@ bpstat_stop_status (CORE_ADDR bp_addr, p + bs->print = 0; + } + bs->commands = copy_command_lines (bs->commands); ++ ++ /* 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) ++ while (inline_skipped_frames (ptid)) ++ step_into_inline_frame (ptid); + } + + /* Print nothing for this entry if we dont stop or if we dont print. */ +@@ -4826,9 +4847,9 @@ set_momentary_breakpoint (struct symtab_ + { + struct breakpoint *b; + +- /* If FRAME_ID is valid, it should be a real frame, not an inlined +- one. */ +- gdb_assert (!frame_id_inlined_p (frame_id)); ++ /* We can be returning even into an inline frame. While finish_command will ++ shortcut the case of returning _from_ an inline frame we still may be ++ returning from non-inlined frame _to_ an inlined frame. */ + + b = set_raw_breakpoint (sal, type); + b->enable_state = bp_enabled; +Index: gdb-6.8.50.20081214/gdb/inline-frame.c +=================================================================== +--- gdb-6.8.50.20081214.orig/gdb/inline-frame.c 2009-02-09 13:28:48.000000000 +0100 ++++ gdb-6.8.50.20081214/gdb/inline-frame.c 2009-02-09 13:30:16.000000000 +0100 +@@ -183,6 +183,12 @@ inline_frame_sniffer (const struct frame + if (frame_block == NULL) + return 0; + ++ /* For >=2 inlined functions SKIPPED_SYMBOL needs to be different after each ++ step_into_inline_frame call. But skip_inline_frames is called only once ++ and thus SKIPPED_SYMBOL needs to be calculated by INLINE_FRAME_SNIFFER. */ ++ if (state) ++ state->skipped_symbol = NULL; ++ + /* Calculate DEPTH, the number of inlined functions at this + location. */ + depth = 0; +@@ -192,6 +198,10 @@ inline_frame_sniffer (const struct frame + if (block_inlined_p (cur_block)) + depth++; + ++ if (state && depth == state->skipped_frames ++ && state->skipped_symbol == NULL) ++ state->skipped_symbol = BLOCK_FUNCTION (cur_block); ++ + cur_block = BLOCK_SUPERBLOCK (cur_block); + } + +@@ -275,7 +285,6 @@ skip_inline_frames (ptid_t ptid) + { + CORE_ADDR this_pc; + struct block *frame_block, *cur_block; +- struct symbol *last_sym = NULL; + int skip_count = 0; + struct inline_state *state; + +@@ -296,10 +305,7 @@ skip_inline_frames (ptid_t ptid) + 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); +- } ++ skip_count++; + else + break; + } +@@ -311,7 +317,6 @@ skip_inline_frames (ptid_t ptid) + state = allocate_inline_frame_state (ptid); + state->skipped_frames = skip_count; + state->saved_pc = this_pc; +- state->skipped_symbol = last_sym; + + if (skip_count != 0) + reinit_frame_cache (); +@@ -329,6 +334,23 @@ step_into_inline_frame (ptid_t ptid) + reinit_frame_cache (); + } + ++/* Step out of an inlined function by hiding it. */ ++ ++void ++step_out_of_inline_frame (ptid_t ptid) ++{ ++ struct inline_state *state = find_inline_frame_state (ptid); ++ ++ gdb_assert (state != NULL); ++ ++ /* Simulate the caller adjustment. */ ++ if (state->skipped_frames == 0) ++ state->saved_pc--; ++ ++ state->skipped_frames++; ++ reinit_frame_cache (); ++} ++ + /* Return the number of hidden functions inlined into the current + frame. */ + +Index: gdb-6.8.50.20081214/gdb/testsuite/gdb.opt/inline-markers.c +=================================================================== +--- gdb-6.8.50.20081214.orig/gdb/testsuite/gdb.opt/inline-markers.c 2009-02-09 13:28:48.000000000 +0100 ++++ gdb-6.8.50.20081214/gdb/testsuite/gdb.opt/inline-markers.c 2009-02-09 13:28:49.000000000 +0100 +@@ -15,11 +15,6 @@ + + extern int x, y; + +-void bar(void) +-{ +- x += y; /* set breakpoint 1 here */ +-} +- + void marker(void) + { + x += y; /* set breakpoint 2 here */ +Index: gdb-6.8.50.20081214/gdb/gdbthread.h +=================================================================== +--- gdb-6.8.50.20081214.orig/gdb/gdbthread.h 2009-02-09 13:28:48.000000000 +0100 ++++ gdb-6.8.50.20081214/gdb/gdbthread.h 2009-02-09 13:30:16.000000000 +0100 +@@ -180,6 +180,12 @@ struct thread_info + + /* Private data used by the target vector implementation. */ + struct private_thread_info *private; ++ ++ /* Nonzero if the current frame PC should be unwound as the caller. It is ++ used to keep the backtrace upper levels existing after finish_command into ++ an inlined frame if the current inlined function/block was ending at the ++ current PC. */ ++ int current_pc_is_notcurrent; + }; + + /* Create an empty thread list, or empty the existing one. */ +Index: gdb-6.8.50.20081214/gdb/infcmd.c +=================================================================== +--- gdb-6.8.50.20081214.orig/gdb/infcmd.c 2009-02-09 13:28:48.000000000 +0100 ++++ gdb-6.8.50.20081214/gdb/infcmd.c 2009-02-09 13:30:16.000000000 +0100 +@@ -1373,11 +1373,11 @@ finish_command_continuation (void *arg) + struct type *value_type; + + value_type = TYPE_TARGET_TYPE (SYMBOL_TYPE (a->function)); +- if (!value_type) ++ if (!SYMBOL_INLINED (a->function) && !value_type) + internal_error (__FILE__, __LINE__, + _("finish_command: function has no target type")); + +- if (TYPE_CODE (value_type) != TYPE_CODE_VOID) ++ if (value_type && TYPE_CODE (value_type) != TYPE_CODE_VOID) + print_return_value (SYMBOL_TYPE (a->function), value_type); + } + +@@ -1481,6 +1481,16 @@ finish_forward (struct symbol *function, + + old_chain = make_cleanup_delete_breakpoint (breakpoint); + ++ /* We should _always_ set CURRENT_PC_IS_NOTCURRENT here to always see the ++ calling line with the message `Value returned is ...'. Currently it is ++ seen only if at least one instruction is on that source line after the ++ call instruction. We would also need to hook step_once and only clear ++ CURRENT_PC_IS_NOTCURRENT on the first step. But it would be a change of ++ general non-inlining behavior against upstream. */ ++ ++ if (get_frame_type (frame) == INLINE_FRAME) ++ tp->current_pc_is_notcurrent = 1; ++ + tp->proceed_to_finish = 1; /* We want stop_registers, please... */ + make_cleanup_restore_integer (&suppress_stop_observer); + suppress_stop_observer = 1; +@@ -1504,7 +1514,9 @@ finish_forward (struct symbol *function, + static void + finish_command (char *arg, int from_tty) + { +- struct frame_info *frame; ++ /* FIXME: Rename `current_frame' to `frame' upon a merge. */ ++ struct frame_info *current_frame, *prev_frame; ++ CORE_ADDR frame_pc; + struct symbol *function; + + int async_exec = 0; +@@ -1535,46 +1547,63 @@ finish_command (char *arg, int from_tty) + if (!target_has_execution) + error (_("The program is not running.")); + +- frame = get_prev_frame (get_selected_frame (_("No selected frame."))); +- if (frame == 0) ++ current_frame = get_selected_frame (_("No selected frame.")); ++ frame_pc = get_frame_pc (current_frame); ++ prev_frame = get_prev_frame (current_frame); ++ if (prev_frame == 0) + error (_("\"finish\" not meaningful in the outermost frame.")); + +- 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) ++ if (get_frame_type (current_frame) == INLINE_FRAME) + { + struct thread_info *tp = inferior_thread (); +- +- /* Claim we are stepping in the calling frame. An empty step +- range means that we will stop once we aren't in a function +- called by that frame. We don't use the magic "1" value for +- step_range_end, because then infrun will think this is nexti, +- and not step over the rest of this inlined function call. */ + struct symtab_and_line empty_sal; +- init_sal (&empty_sal); +- set_step_info (tp, frame, empty_sal); +- tp->step_range_start = tp->step_range_end = get_frame_pc (frame); +- tp->step_over_calls = STEP_OVER_ALL; ++ struct block *frame_block; + + /* 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); ++ print_stack_frame (current_frame, 1, LOCATION); + } + ++ /* Even just a single stepi would get us out of the caller function PC ++ range. */ ++ ++ frame_block = get_frame_block (current_frame, NULL); ++ ++ /* FRAME_BLOCK must be initialized and also the frame printing above must ++ be done still with the original CURRENT_PC_IS_NOTCURRENT setting. */ ++ clear_proceed_status (); ++ ++ if (frame_block && BLOCK_END (frame_block) == frame_pc) ++ { ++ step_out_of_inline_frame (tp->ptid); ++ tp->current_pc_is_notcurrent = 1; ++ normal_stop (); ++ return; ++ } ++ ++ /* 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. */ ++ init_sal (&empty_sal); ++ set_step_info (tp, prev_frame, empty_sal); ++ tp->step_range_start = tp->step_range_end = get_frame_pc (prev_frame); ++ tp->step_over_calls = STEP_OVER_ALL; ++ + proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 1); + return; + } + + /* Find the function we will return from. */ + +- function = find_pc_function (get_frame_pc (get_selected_frame (NULL))); ++ function = find_pc_function (frame_pc); + + /* Print info on the selected frame, including level number but not + source. */ +@@ -1588,10 +1617,14 @@ finish_command (char *arg, int from_tty) + print_stack_frame (get_selected_frame (NULL), 1, LOCATION); + } + ++ /* Frames printing above must be done still with the original ++ CURRENT_PC_IS_NOTCURRENT setting. */ ++ clear_proceed_status (); ++ + if (execution_direction == EXEC_REVERSE) + finish_backward (function); + else +- finish_forward (function, frame); ++ finish_forward (function, prev_frame); + } + + +Index: gdb-6.8.50.20081214/gdb/infrun.c +=================================================================== +--- gdb-6.8.50.20081214.orig/gdb/infrun.c 2009-02-09 13:29:51.000000000 +0100 ++++ gdb-6.8.50.20081214/gdb/infrun.c 2009-02-09 13:30:16.000000000 +0100 +@@ -1147,8 +1150,6 @@ a command like `return' or `jump' to con + step = 0; + } + +- clear_inline_frame_state (resume_ptid); +- + if (debug_displaced + && use_displaced_stepping (gdbarch) + && tp->trap_expected) +@@ -1201,6 +1201,8 @@ clear_proceed_status_thread (struct thre + + /* Discard any remaining commands or status from previous stop. */ + bpstat_clear (&tp->stop_bpstat); ++ ++ tp->current_pc_is_notcurrent = 0; + } + + static int +--- ./gdb/target.c 2009-02-10 23:45:59.000000000 +0100 ++++ ./gdb/target.c 2009-02-10 23:43:29.000000000 +0100 +@@ -41,6 +41,7 @@ + #include "target-descriptions.h" + #include "gdbthread.h" + #include "solib.h" ++#include "inline-frame.h" + + static void target_info (char *, int); + +@@ -1938,6 +1939,7 @@ target_pid_to_str (ptid_t ptid) + void + target_resume (ptid_t ptid, int step, enum target_signal signal) + { ++ clear_inline_frame_state (ptid); + dcache_invalidate (target_dcache); + (*current_target.to_resume) (ptid, step, signal); + set_executing (ptid, 1); +Index: gdb-6.8.50.20081214/gdb/inline-frame.h +=================================================================== +--- gdb-6.8.50.20081214.orig/gdb/inline-frame.h 2009-02-09 13:28:48.000000000 +0100 ++++ gdb-6.8.50.20081214/gdb/inline-frame.h 2009-02-09 13:30:16.000000000 +0100 +@@ -43,6 +43,10 @@ void clear_inline_frame_state (ptid_t pt + + void step_into_inline_frame (ptid_t ptid); + ++/* Step out of an inlined function by hiding it. */ ++ ++void step_out_of_inline_frame (ptid_t ptid); ++ + /* Return the number of hidden functions inlined into the current + frame. */ + +--- gdb-6.8.50.20090209/gdb/infcall.c 2009-02-10 00:16:10.000000000 +0100 ++++ gdb-6.8.50.20090209/gdb/infcall.c-removed 2009-02-10 00:15:58.000000000 +0100 +@@ -841,8 +841,15 @@ When the function is done executing, GDB + + if (unwind_on_signal_p) + { +- /* The user wants the context restored. Calling error will +- run inf_status_cleanup, which does all the work. */ ++ /* The user wants the context restored. */ ++ ++ /* We must get back to the frame we were before the ++ dummy call. */ ++ dummy_frame_pop (dummy_id); ++ ++ /* We also need to restore inferior status to that before the ++ dummy call. */ ++ restore_inferior_status (inf_status); + + /* FIXME: Insert a bunch of wrap_here; name can be very + long if it's a C++ name with arguments and stuff. */ diff --git a/gdb-6.8-inlining-by-name.patch b/gdb-6.8-inlining-by-name.patch new file mode 100644 index 0000000..bdb954b --- /dev/null +++ b/gdb-6.8-inlining-by-name.patch @@ -0,0 +1,104 @@ +Implement `b ' for with concete inlined instances by +a multiple-PC breakpoint. + +Index: gdb-6.8.50.20081128/gdb/ada-lang.c +=================================================================== +--- gdb-6.8.50.20081128.orig/gdb/ada-lang.c 2008-11-25 00:21:15.000000000 +0100 ++++ gdb-6.8.50.20081128/gdb/ada-lang.c 2008-12-06 21:39:56.000000000 +0100 +@@ -4614,7 +4614,7 @@ remove_irrelevant_renamings (struct ada_ + if (current_block == NULL) + return nsyms; + +- current_function = block_linkage_function (current_block); ++ current_function = block_function (current_block); + if (current_function == NULL) + return nsyms; + +@@ -6625,7 +6625,7 @@ ada_find_renaming_symbol (const char *na + static struct symbol * + find_old_style_renaming_symbol (const char *name, struct block *block) + { +- const struct symbol *function_sym = block_linkage_function (block); ++ const struct symbol *function_sym = block_function (block); + char *rename; + + if (function_sym != NULL) +Index: gdb-6.8.50.20081128/gdb/block.c +=================================================================== +--- gdb-6.8.50.20081128.orig/gdb/block.c 2008-12-06 14:06:16.000000000 +0100 ++++ gdb-6.8.50.20081128/gdb/block.c 2008-12-06 21:40:29.000000000 +0100 +@@ -75,6 +75,19 @@ block_linkage_function (const struct blo + return BLOCK_FUNCTION (bl); + } + ++/* Return the symbol for the function which contains a specified ++ lexical block, described by a struct block BL. Inlined functions ++ can be returned. */ ++ ++struct symbol * ++block_function (const struct block *bl) ++{ ++ while (BLOCK_FUNCTION (bl) == NULL && BLOCK_SUPERBLOCK (bl) != NULL) ++ bl = BLOCK_SUPERBLOCK (bl); ++ ++ return BLOCK_FUNCTION (bl); ++} ++ + /* Return one if BL represents an inlined function. */ + + int +Index: gdb-6.8.50.20081128/gdb/block.h +=================================================================== +--- gdb-6.8.50.20081128.orig/gdb/block.h 2008-12-06 14:06:16.000000000 +0100 ++++ gdb-6.8.50.20081128/gdb/block.h 2008-12-06 21:39:56.000000000 +0100 +@@ -133,6 +133,7 @@ struct blockvector + enum { GLOBAL_BLOCK = 0, STATIC_BLOCK = 1, FIRST_LOCAL_BLOCK = 2 }; + + extern struct symbol *block_linkage_function (const struct block *); ++extern struct symbol *block_function (const struct block *bl); + + extern int block_inlined_p (const struct block *block); + +Index: gdb-6.8.50.20081128/gdb/blockframe.c +=================================================================== +--- gdb-6.8.50.20081128.orig/gdb/blockframe.c 2008-12-06 14:06:16.000000000 +0100 ++++ gdb-6.8.50.20081128/gdb/blockframe.c 2008-12-06 21:39:56.000000000 +0100 +@@ -143,7 +143,7 @@ find_pc_sect_function (CORE_ADDR pc, str + struct block *b = block_for_pc_sect (pc, section); + if (b == 0) + return 0; +- return block_linkage_function (b); ++ return block_function (b); + } + + /* Return the function containing pc value PC. +Index: gdb-6.8.50.20081128/gdb/breakpoint.c +=================================================================== +--- gdb-6.8.50.20081128.orig/gdb/breakpoint.c 2008-12-06 14:06:17.000000000 +0100 ++++ gdb-6.8.50.20081128/gdb/breakpoint.c 2008-12-06 21:39:56.000000000 +0100 +@@ -5712,7 +5712,7 @@ resolve_sal_pc (struct symtab_and_line * + bv = blockvector_for_pc_sect (sal->pc, 0, &b, sal->symtab); + if (bv != NULL) + { +- sym = block_linkage_function (b); ++ sym = block_function (b); + if (sym != NULL) + { + fixup_symbol_section (sym, sal->symtab->objfile); +Index: gdb-6.8.50.20081128/gdb/testsuite/gdb.opt/inline-cmds.exp +=================================================================== +--- gdb-6.8.50.20081128.orig/gdb/testsuite/gdb.opt/inline-cmds.exp 2008-12-06 21:37:27.000000000 +0100 ++++ gdb-6.8.50.20081128/gdb/testsuite/gdb.opt/inline-cmds.exp 2008-12-06 21:41:37.000000000 +0100 +@@ -45,8 +45,10 @@ if { [skip_inline_frame_tests] } { + + # First, check that the things we expected to be inlined really were, + # and those that shouldn't be weren't. +-set line1 [gdb_get_line_number "set breakpoint 1 here" ${srcfile2}] +-gdb_breakpoint $srcfile2:$line1 ++# We test also inlining by the function name, otherwise we would use: ++# set line1 [gdb_get_line_number "set breakpoint 1 here" ${srcfile2}] ++# gdb_breakpoint $srcfile2:$line1 ++gdb_breakpoint "bar" + set line2 [gdb_get_line_number "set breakpoint 2 here" ${srcfile2}] + gdb_breakpoint $srcfile2:$line2 + diff --git a/gdb-6.8-inlining.patch b/gdb-6.8-inlining.patch new file mode 100644 index 0000000..7ef8b1c --- /dev/null +++ b/gdb-6.8-inlining.patch @@ -0,0 +1,3271 @@ +http://sourceware.org/ml/gdb-patches/2008-07/msg00442.html +with pre-applied and post-unapplied (the patch is currently 2008-12-06 not +applied upstream) +http://sourceware.org/ml/gdb-patches/2008-07/msg00317.html + +Removed dwarf_expr_frame_base NULL check duplicity with *-vla.patch. + +Index: gdb-6.8.50.20090216-cvs/gdb/NEWS +=================================================================== +--- gdb-6.8.50.20090216-cvs.orig/gdb/NEWS 2009-02-09 10:56:02.000000000 +0100 ++++ gdb-6.8.50.20090216-cvs/gdb/NEWS 2009-03-02 11:38:29.000000000 +0100 +@@ -1,6 +1,11 @@ + What has changed in GDB? + (Organized release by release) + ++*** Fedora changes ++ ++* Inlined functions are now supported. They show up in backtraces, and ++the "step", "next", and "finish" commands handle them automatically. ++ + *** Changes since GDB 6.8 + + * GDB now supports automatic retrieval of shared library files from +Index: gdb-6.8.50.20090216-cvs/gdb/block.c +=================================================================== +--- gdb-6.8.50.20090216-cvs.orig/gdb/block.c 2009-01-07 09:44:56.000000000 +0100 ++++ gdb-6.8.50.20090216-cvs/gdb/block.c 2009-03-02 11:38:29.000000000 +0100 +@@ -47,8 +47,16 @@ contained_in (const struct block *a, con + { + if (!a || !b) + return 0; +- return BLOCK_START (a) >= BLOCK_START (b) +- && BLOCK_END (a) <= BLOCK_END (b); ++ ++ do ++ { ++ if (a == b) ++ return 1; ++ a = BLOCK_SUPERBLOCK (a); ++ } ++ while (a != NULL); ++ ++ return 0; + } + + +@@ -60,12 +68,21 @@ contained_in (const struct block *a, con + struct symbol * + block_linkage_function (const struct block *bl) + { +- while (BLOCK_FUNCTION (bl) == 0 && BLOCK_SUPERBLOCK (bl) != 0) ++ while ((BLOCK_FUNCTION (bl) == NULL || block_inlined_p (bl)) ++ && BLOCK_SUPERBLOCK (bl) != NULL) + bl = BLOCK_SUPERBLOCK (bl); + + return BLOCK_FUNCTION (bl); + } + ++/* Return one if BL represents an inlined function. */ ++ ++int ++block_inlined_p (const struct block *bl) ++{ ++ return BLOCK_FUNCTION (bl) != NULL && SYMBOL_INLINED (BLOCK_FUNCTION (bl)); ++} ++ + /* Return the blockvector immediately containing the innermost lexical + block containing the specified pc value and section, or 0 if there + is none. PBLOCK is a pointer to the block. If PBLOCK is NULL, we +Index: gdb-6.8.50.20090216-cvs/gdb/block.h +=================================================================== +--- gdb-6.8.50.20090216-cvs.orig/gdb/block.h 2009-01-07 09:44:56.000000000 +0100 ++++ gdb-6.8.50.20090216-cvs/gdb/block.h 2009-03-02 11:38:29.000000000 +0100 +@@ -65,7 +65,7 @@ struct block + CORE_ADDR endaddr; + + /* The symbol that names this block, if the block is the body of a +- function; otherwise, zero. */ ++ function (real or inlined); otherwise, zero. */ + + struct symbol *function; + +@@ -134,6 +134,8 @@ enum { GLOBAL_BLOCK = 0, STATIC_BLOCK = + + extern struct symbol *block_linkage_function (const struct block *); + ++extern int block_inlined_p (const struct block *block); ++ + extern int contained_in (const struct block *, const struct block *); + + extern struct blockvector *blockvector_for_pc (CORE_ADDR, struct block **); +Index: gdb-6.8.50.20090216-cvs/gdb/blockframe.c +=================================================================== +--- gdb-6.8.50.20090216-cvs.orig/gdb/blockframe.c 2009-01-07 09:44:56.000000000 +0100 ++++ gdb-6.8.50.20090216-cvs/gdb/blockframe.c 2009-03-02 11:38:29.000000000 +0100 +@@ -36,6 +36,7 @@ + #include "command.h" + #include "gdbcmd.h" + #include "block.h" ++#include "inline-frame.h" + + /* Prototypes for exported functions. */ + +@@ -61,11 +62,29 @@ struct block * + get_frame_block (struct frame_info *frame, CORE_ADDR *addr_in_block) + { + const CORE_ADDR pc = get_frame_address_in_block (frame); ++ struct frame_info *next_frame; ++ struct block *bl; ++ int inline_count; + + if (addr_in_block) + *addr_in_block = pc; + +- return block_for_pc (pc); ++ bl = block_for_pc (pc); ++ if (bl == NULL) ++ return NULL; ++ ++ inline_count = frame_inlined_callees (frame); ++ ++ while (inline_count > 0) ++ { ++ if (block_inlined_p (bl)) ++ inline_count--; ++ ++ bl = BLOCK_SUPERBLOCK (bl); ++ gdb_assert (bl != NULL); ++ } ++ ++ return bl; + } + + CORE_ADDR +@@ -104,9 +123,14 @@ struct symbol * + get_frame_function (struct frame_info *frame) + { + struct block *bl = get_frame_block (frame, 0); +- if (bl == 0) +- return 0; +- return block_linkage_function (bl); ++ ++ if (bl == NULL) ++ return NULL; ++ ++ while (BLOCK_FUNCTION (bl) == NULL && BLOCK_SUPERBLOCK (bl) != NULL) ++ bl = BLOCK_SUPERBLOCK (bl); ++ ++ return BLOCK_FUNCTION (bl); + } + + +@@ -350,8 +374,8 @@ block_innermost_frame (struct block *blo + frame = get_current_frame (); + while (frame != NULL) + { +- calling_pc = get_frame_address_in_block (frame); +- if (calling_pc >= start && calling_pc < end) ++ struct block *frame_block = get_frame_block (frame, NULL); ++ if (frame_block != NULL && contained_in (frame_block, block)) + return frame; + + frame = get_prev_frame (frame); +Index: gdb-6.8.50.20090216-cvs/gdb/breakpoint.c +=================================================================== +--- gdb-6.8.50.20090216-cvs.orig/gdb/breakpoint.c 2009-03-02 11:37:58.000000000 +0100 ++++ gdb-6.8.50.20090216-cvs/gdb/breakpoint.c 2009-03-02 11:38:29.000000000 +0100 +@@ -2622,19 +2622,21 @@ watchpoint_check (void *p) + within_current_scope = 1; + else + { +- /* There is no current frame at this moment. If we're going to have +- any chance of handling watchpoints on local variables, we'll need +- the frame chain (so we can determine if we're in scope). */ +- reinit_frame_cache (); + fr = frame_find_by_id (b->watchpoint_frame); + within_current_scope = (fr != NULL); + + /* If we've gotten confused in the unwinder, we might have + returned a frame that can't describe this variable. */ +- if (within_current_scope +- && (block_linkage_function (b->exp_valid_block) +- != get_frame_function (fr))) +- within_current_scope = 0; ++ if (within_current_scope) ++ { ++ struct symbol *function; ++ ++ function = get_frame_function (fr); ++ if (function == NULL ++ || !contained_in (b->exp_valid_block, ++ SYMBOL_BLOCK_VALUE (function))) ++ within_current_scope = 0; ++ } + + /* in_function_epilogue_p() returns a non-zero value if we're still + in the function but the stack frame has already been invalidated. +@@ -2646,10 +2648,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. */ +@@ -2883,7 +2884,7 @@ bpstat_check_breakpoint_conditions (bpst + struct breakpoint *b = bl->owner; + + if (frame_id_p (b->frame_id) +- && !frame_id_eq (b->frame_id, get_frame_id (get_current_frame ()))) ++ && !frame_id_eq (b->frame_id, get_stack_frame_id (get_current_frame ()))) + bs->stop = 0; + else if (bs->stop) + { +@@ -2898,8 +2899,12 @@ bpstat_check_breakpoint_conditions (bpst + + if (bl->cond && bl->owner->disposition != disp_del_at_next_stop) + { +- /* Need to select the frame, with all that implies +- so that the conditions will have the right context. */ ++ /* Need to select the frame, with all that implies so that ++ the conditions will have the right context. Because we ++ use the frame, we will not see an inlined function's ++ variables when we arrive at a breakpoint at the start ++ of the inlined function; the current frame will be the ++ call site. */ + select_frame (get_current_frame ()); + value_is_zero + = catch_errors (breakpoint_cond_eval, (bl->cond), +@@ -4926,6 +4931,11 @@ set_momentary_breakpoint (struct symtab_ + enum bptype type) + { + struct breakpoint *b; ++ ++ /* If FRAME_ID is valid, it should be a real frame, not an inlined ++ one. */ ++ gdb_assert (!frame_id_inlined_p (frame_id)); ++ + b = set_raw_breakpoint (sal, type); + b->enable_state = bp_enabled; + b->disposition = disp_donttouch; +@@ -5924,7 +5934,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; +@@ -6064,34 +6073,34 @@ watch_command_1 (char *arg, int accessfl + bp_type = bp_watchpoint; + + frame = block_innermost_frame (exp_valid_block); +- if (frame) +- prev_frame = get_prev_frame (frame); +- else +- prev_frame = NULL; + + /* If the expression is "local", then set up a "watchpoint scope" + breakpoint at the point where we've left the scope of the watchpoint + expression. Create the scope breakpoint before the watchpoint, so + that we will encounter it first in bpstat_stop_status. */ +- if (innermost_block && prev_frame) ++ if (innermost_block && frame) + { +- scope_breakpoint = create_internal_breakpoint (get_frame_pc (prev_frame), +- bp_watchpoint_scope); ++ if (frame_id_p (frame_unwind_id (frame))) ++ { ++ scope_breakpoint ++ = create_internal_breakpoint (frame_pc_unwind (frame), ++ bp_watchpoint_scope); + +- scope_breakpoint->enable_state = bp_enabled; ++ scope_breakpoint->enable_state = bp_enabled; + +- /* Automatically delete the breakpoint when it hits. */ +- scope_breakpoint->disposition = disp_del; ++ /* Automatically delete the breakpoint when it hits. */ ++ scope_breakpoint->disposition = disp_del; + +- /* Only break in the proper frame (help with recursion). */ +- scope_breakpoint->frame_id = get_frame_id (prev_frame); ++ /* Only break in the proper frame (help with recursion). */ ++ scope_breakpoint->frame_id = frame_unwind_id (frame); + +- /* Set the address at which we will stop. */ +- scope_breakpoint->loc->requested_address +- = get_frame_pc (prev_frame); +- scope_breakpoint->loc->address +- = adjust_breakpoint_address (scope_breakpoint->loc->requested_address, +- scope_breakpoint->type); ++ /* Set the address at which we will stop. */ ++ scope_breakpoint->loc->requested_address ++ = frame_pc_unwind (frame); ++ scope_breakpoint->loc->address ++ = adjust_breakpoint_address (scope_breakpoint->loc->requested_address, ++ scope_breakpoint->type); ++ } + } + + /* Now set up the breakpoint. */ +@@ -6272,7 +6281,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; +@@ -6305,20 +6313,22 @@ until_break_command (char *arg, int from + we don't specify a frame at which we need to stop. */ + breakpoint = set_momentary_breakpoint (sal, null_frame_id, bp_until); + else +- /* Otherwise, specify the current frame, because we want to stop only ++ /* Otherwise, specify the selected frame, because we want to stop only + at the very same frame. */ +- breakpoint = set_momentary_breakpoint (sal, get_frame_id (frame), ++ breakpoint = set_momentary_breakpoint (sal, get_stack_frame_id (frame), + bp_until); + + old_chain = make_cleanup_delete_breakpoint (breakpoint); + + /* Keep within the current frame, or in frames called by the current + one. */ +- if (prev_frame) ++ ++ if (frame_id_p (frame_unwind_id (frame))) + { +- sal = find_pc_line (get_frame_pc (prev_frame), 0); +- sal.pc = get_frame_pc (prev_frame); +- breakpoint2 = set_momentary_breakpoint (sal, get_frame_id (prev_frame), ++ sal = find_pc_line (frame_pc_unwind (frame), 0); ++ sal.pc = frame_pc_unwind (frame); ++ breakpoint2 = set_momentary_breakpoint (sal, ++ frame_unwind_id (frame), + bp_until); + make_cleanup_delete_breakpoint (breakpoint2); + } +Index: gdb-6.8.50.20090216-cvs/gdb/buildsym.c +=================================================================== +--- gdb-6.8.50.20090216-cvs.orig/gdb/buildsym.c 2009-01-07 09:44:56.000000000 +0100 ++++ gdb-6.8.50.20090216-cvs/gdb/buildsym.c 2009-03-02 11:38:29.000000000 +0100 +@@ -1159,6 +1159,12 @@ end_symtab (CORE_ADDR end_addr, struct o + struct symbol *sym; + struct dict_iterator iter; + ++ /* Inlined functions may have symbols not in the global or static ++ symbol lists. */ ++ if (BLOCK_FUNCTION (block) != NULL) ++ if (SYMBOL_SYMTAB (BLOCK_FUNCTION (block)) == NULL) ++ SYMBOL_SYMTAB (BLOCK_FUNCTION (block)) = symtab; ++ + for (sym = dict_iterator_first (BLOCK_DICT (block), &iter); + sym != NULL; + sym = dict_iterator_next (&iter)) +Index: gdb-6.8.50.20090216-cvs/gdb/doc/gdb.texinfo +=================================================================== +--- gdb-6.8.50.20090216-cvs.orig/gdb/doc/gdb.texinfo 2009-03-02 11:37:57.000000000 +0100 ++++ gdb-6.8.50.20090216-cvs/gdb/doc/gdb.texinfo 2009-03-02 11:38:29.000000000 +0100 +@@ -137,6 +137,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 +@@ -1806,7 +1807,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. +@@ -1815,22 +1816,7 @@ executables containing debugging informa + without @samp{-O}, making it possible to debug optimized code. We + recommend that you @emph{always} use @samp{-g} whenever you compile a + program. You may think your program is correct, but there is no sense +-in pushing your luck. +- +-@cindex optimized code, debugging +-@cindex debugging optimized code +-When you debug a program compiled with @samp{-g -O}, remember that the +-optimizer is rearranging your code; the debugger shows you what is +-really there. Do not be too surprised when the execution path does not +-exactly match your source file! An extreme example: if you define a +-variable, but never use it, @value{GDBN} never sees that +-variable---because the compiler optimizes it out of existence. +- +-Some things do not work as well with @samp{-g -O} as with just +-@samp{-g}, particularly on machines with instruction scheduling. If in +-doubt, recompile with @samp{-g} alone, and if this fixes the problem, +-please report it to us as a bug (including a test case!). +-@xref{Variables}, for more information about debugging optimized code. ++in pushing your luck. For more information, see @ref{Optimized Code}. + + Older versions of the @sc{gnu} C compiler permitted a variant option + @w{@samp{-gg}} for debugging information. @value{GDBN} no longer supports this +@@ -8271,6 +8257,107 @@ $1 = 1 + $2 = (void *) 0x8049560 + @end smallexample + ++@node Optimized Code ++@chapter Debugging Optimized Code ++@cindex optimized code, debugging ++@cindex debugging optimized code ++ ++Almost all compilers support optimization. With optimization ++disabled, the compiler generates assembly code that corresponds ++directly to your source code, in a simplistic way. As the compiler ++applies more powerful optimizations, the generated assembly code ++diverges from your original source code. With help from debugging ++information generated by the compiler, @value{GDBN} can map from ++the running program back to constructs from your original source. ++ ++@value{GDBN} is more accurate with optimization disabled. If you ++can recompile without optimization, it is easier to follow the ++progress of your program during debugging. But, there are many cases ++where you may need to debug an optimized version. ++ ++When you debug a program compiled with @samp{-g -O}, remember that the ++optimizer has rearranged your code; the debugger shows you what is ++really there. Do not be too surprised when the execution path does not ++exactly match your source file! An extreme example: if you define a ++variable, but never use it, @value{GDBN} never sees that ++variable---because the compiler optimizes it out of existence. ++ ++Some things do not work as well with @samp{-g -O} as with just ++@samp{-g}, particularly on machines with instruction scheduling. If in ++doubt, recompile with @samp{-g} alone, and if this fixes the problem, ++please report it to us as a bug (including a test case!). ++@xref{Variables}, for more information about debugging optimized code. ++ ++@menu ++* Inline Functions:: How @value{GDBN} presents inlining ++@end menu ++ ++@node Inline Functions ++@section Inline Functions ++@cindex inline functions, debugging ++ ++@dfn{Inlining} is an optimization that inserts a copy of the function ++body directly at each call site, instead of jumping to a shared ++routine. @value{GDBN} displays inlined functions just like ++non-inlined functions. They appear in backtraces. You can view their ++arguments and local variables, step into them with @code{step}, skip ++them with @code{next}, and escape from them with @code{finish}. ++You can check whether a function was inlined by using the ++@code{info frame} command. ++ ++For @value{GDBN} to support inlined functions, the compiler must ++record information about inlining in the debug information --- ++@value{NGCC} using the @sc{dwarf 2} format does this, and several ++other compilers do also. @value{GDBN} only supports inlined functions ++when using @sc{dwarf 2}. Versions of @value{NGCC} before 4.1 ++do not emit two required attributes (@samp{DW_AT_call_file} and ++@samp{DW_AT_call_line}); @value{GDBN} does not display inlined ++function calls with earlier versions of @value{NGCC}. It instead ++displays the arguments and local variables of inlined functions as ++local variables in the caller. ++ ++The body of an inlined function is directly included at its call site; ++unlike a non-inlined function, there are no instructions devoted to ++the call. @value{GDBN} still pretends that the call site and the ++start of the inlined function are different instructions. Stepping to ++the call site shows the call site, and then stepping again shows ++the first line of the inlined function, even though no additional ++instructions are executed. ++ ++This makes source-level debugging much clearer; you can see both the ++context of the call and then the effect of the call. Only stepping by ++a single instruction using @code{stepi} or @code{nexti} does not do ++this; single instruction steps always show the inlined body. ++ ++There are some ways that @value{GDBN} does not pretend that inlined ++function calls are the same as normal calls: ++ ++@itemize @bullet ++@item ++You cannot set breakpoints on inlined functions. @value{GDBN} ++either reports that there is no symbol with that name, or else sets the ++breakpoint only on non-inlined copies of the function. This limitation ++will be removed in a future version of @value{GDBN}; until then, ++set a breakpoint by line number on the first line of the inlined ++function instead. ++ ++@item ++Setting breakpoints at the call site of an inlined function may not ++work, because the call site does not contain any code. @value{GDBN} ++may incorrectly move the breakpoint to the next line of the enclosing ++function, after the call. This limitation will be removed in a future ++version of @value{GDBN}; until then, set a breakpoint on an earlier line ++or inside the inlined function instead. ++ ++@item ++@value{GDBN} cannot locate the return value of inlined calls after ++using the @code{finish} command. This is a limitation of compiler-generated ++debugging information; after @code{finish}, you can step to the next line ++and print a variable where your program stored the return value. ++ ++@end itemize ++ ++ + @node Macros + @chapter C Preprocessor Macros + +Index: gdb-6.8.50.20090216-cvs/gdb/dwarf2loc.c +=================================================================== +--- gdb-6.8.50.20090216-cvs.orig/gdb/dwarf2loc.c 2009-01-14 10:23:13.000000000 +0100 ++++ gdb-6.8.50.20090216-cvs/gdb/dwarf2loc.c 2009-03-02 11:38:29.000000000 +0100 +@@ -31,6 +31,7 @@ + #include "regcache.h" + #include "objfiles.h" + #include "exceptions.h" ++#include "block.h" + + #include "elf/dwarf2.h" + #include "dwarf2expr.h" +@@ -147,7 +148,10 @@ dwarf_expr_frame_base (void *baton, gdb_ + struct symbol *framefunc; + struct dwarf_expr_baton *debaton = (struct dwarf_expr_baton *) baton; + +- framefunc = get_frame_function (debaton->frame); ++ /* Use block_linkage_function, which returns a real (not inlined) ++ function, instead of get_frame_function, which may return an ++ inlined function. */ ++ framefunc = block_linkage_function (get_frame_block (debaton->frame, NULL)); + + /* If we found a frame-relative symbol then it was certainly within + some function associated with a frame. If we can't find the frame, +Index: gdb-6.8.50.20090216-cvs/gdb/dwarf2read.c +=================================================================== +--- gdb-6.8.50.20090216-cvs.orig/gdb/dwarf2read.c 2009-03-02 11:37:57.000000000 +0100 ++++ gdb-6.8.50.20090216-cvs/gdb/dwarf2read.c 2009-03-02 11:38:29.000000000 +0100 +@@ -46,6 +46,7 @@ + #include "hashtab.h" + #include "command.h" + #include "gdbcmd.h" ++#include "block.h" + #include "addrmap.h" + #include "top.h" + +@@ -2818,12 +2819,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: +@@ -3089,10 +3086,25 @@ read_func_scope (struct die_info *die, s + CORE_ADDR lowpc; + CORE_ADDR highpc; + struct die_info *child_die; +- struct attribute *attr; ++ struct attribute *attr, *call_line, *call_file; + char *name; + CORE_ADDR baseaddr; + struct block *block; ++ int inlined_func = (die->tag == DW_TAG_inlined_subroutine); ++ ++ if (inlined_func) ++ { ++ /* If we do not have call site information, we can't show the ++ caller of this inlined function. That's too confusing, so ++ only use the scope for local variables. */ ++ call_line = dwarf2_attr (die, DW_AT_call_line, cu); ++ call_file = dwarf2_attr (die, DW_AT_call_file, cu); ++ if (call_line == NULL || call_file == NULL) ++ { ++ read_lexical_block_scope (die, cu); ++ return; ++ } ++ } + + baseaddr = ANOFFSET (objfile->section_offsets, SECT_OFF_TEXT (objfile)); + +@@ -6858,6 +6870,9 @@ die_specification (struct die_info *die, + *spec_cu); + + if (spec_attr == NULL) ++ spec_attr = dwarf2_attr (die, DW_AT_abstract_origin, *spec_cu); ++ ++ if (spec_attr == NULL) + return NULL; + else + return follow_die_ref (die, spec_attr, spec_cu); +@@ -7539,6 +7554,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)); + +@@ -7566,13 +7582,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); +@@ -7619,6 +7639,14 @@ new_symbol (struct die_info *die, struct + add_symbol_to_list (sym, cu->list_in_scope); + } + break; ++ case DW_TAG_inlined_subroutine: ++ /* SYMBOL_BLOCK_VALUE (sym) will be filled in later by ++ finish_block. */ ++ SYMBOL_CLASS (sym) = LOC_BLOCK; ++ SYMBOL_INLINED (sym) = 1; ++ /* Do not add the symbol to any lists. It will be found via ++ BLOCK_FUNCTION from the blockvector. */ ++ break; + case DW_TAG_variable: + /* Compilation with minimal debug info may result in variables + with missing type entries. Change the misleading `void' type +@@ -7666,7 +7694,14 @@ new_symbol (struct die_info *die, struct + } + break; + case DW_TAG_formal_parameter: +- SYMBOL_IS_ARGUMENT (sym) = 1; ++ /* If we are inside a function, mark this as an argument. If ++ not, we might be looking at an argument to an inlined function ++ when we do not have enough information to show inlined frames; ++ pretend it's a local variable in that case so that the user can ++ still see it. */ ++ if (context_stack_depth > 0 ++ && context_stack[context_stack_depth - 1].name != NULL) ++ SYMBOL_IS_ARGUMENT (sym) = 1; + attr = dwarf2_attr (die, DW_AT_location, cu); + if (attr) + { +Index: gdb-6.8.50.20090216-cvs/gdb/frame-unwind.c +=================================================================== +--- gdb-6.8.50.20090216-cvs.orig/gdb/frame-unwind.c 2009-03-02 11:37:57.000000000 +0100 ++++ gdb-6.8.50.20090216-cvs/gdb/frame-unwind.c 2009-03-02 11:38:29.000000000 +0100 +@@ -21,6 +21,7 @@ + #include "frame.h" + #include "frame-unwind.h" + #include "dummy-frame.h" ++#include "inline-frame.h" + #include "value.h" + #include "regcache.h" + +@@ -51,8 +52,10 @@ frame_unwind_init (struct obstack *obsta + can't override this. */ + table->list = OBSTACK_ZALLOC (obstack, struct frame_unwind_table_entry); + table->list->unwinder = dummy_frame_unwind; ++ table->list->next = OBSTACK_ZALLOC (obstack, struct frame_unwind_table_entry); ++ table->list->next->unwinder = inline_frame_unwind; + /* The insertion point for OSABI sniffers. */ +- table->osabi_head = &table->list->next; ++ table->osabi_head = &table->list->next->next; + return table; + } + +Index: gdb-6.8.50.20090216-cvs/gdb/frame.c +=================================================================== +--- gdb-6.8.50.20090216-cvs.orig/gdb/frame.c 2009-03-02 11:37:57.000000000 +0100 ++++ gdb-6.8.50.20090216-cvs/gdb/frame.c 2009-03-02 11:39:34.000000000 +0100 +@@ -42,8 +42,14 @@ + #include "exceptions.h" + #include "gdbthread.h" + #include "arch-utils.h" ++#include "block.h" ++#include "inline-frame.h" + + static struct frame_info *get_prev_frame_1 (struct frame_info *this_frame); ++static struct frame_info *get_prev_frame_raw (struct frame_info *this_frame); ++ ++static void deprecated_update_frame_pc_hack (struct frame_info *frame, ++ CORE_ADDR pc); + + /* We keep a cache of stack frames, each of which is a "struct + frame_info". The innermost one gets allocated (in +@@ -181,6 +187,8 @@ fprint_frame_id (struct ui_file *file, s + fprint_field (file, "code", id.code_addr_p, id.code_addr); + fprintf_unfiltered (file, ","); + fprint_field (file, "special", id.special_addr_p, id.special_addr); ++ if (id.inline_depth) ++ fprintf_unfiltered (file, ",inlined=%d", id.inline_depth); + fprintf_unfiltered (file, "}"); + } + +@@ -195,6 +203,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; +@@ -250,6 +264,18 @@ fprint_frame (struct ui_file *file, stru + fprintf_unfiltered (file, "}"); + } + ++/* Given FRAME, return the enclosing normal frame for inlined ++ function frames. Otherwise return the original frame. */ ++ ++static struct frame_info * ++skip_inlined_frames (struct frame_info *frame) ++{ ++ while (get_frame_type (frame) == INLINE_FRAME) ++ frame = get_prev_frame (frame); ++ ++ return frame; ++} ++ + /* Return a frame uniq ID that can be used to, later, re-find the + frame. */ + +@@ -287,13 +313,21 @@ get_frame_id (struct frame_info *fi) + } + + struct frame_id ++get_stack_frame_id (struct frame_info *next_frame) ++{ ++ return get_frame_id (skip_inlined_frames (next_frame)); ++} ++ ++struct frame_id + frame_unwind_id (struct frame_info *next_frame) + { + /* Use prev_frame, and not get_prev_frame. The latter will truncate + the frame chain, leading to this function unintentionally + returning a null_frame_id (e.g., when a caller requests the frame + ID of "main()"s caller. */ +- return get_frame_id (get_prev_frame_1 (next_frame)); ++ ++ next_frame = skip_inlined_frames (next_frame); ++ return get_frame_id (skip_inlined_frames (get_prev_frame_1 (next_frame))); + } + + const struct frame_id null_frame_id; /* All zeros. */ +@@ -348,6 +382,15 @@ frame_id_p (struct frame_id l) + } + + int ++frame_id_inlined_p (struct frame_id l) ++{ ++ if (!frame_id_p (l)) ++ return 0; ++ ++ return (l.inline_depth != 0); ++} ++ ++int + frame_id_eq (struct frame_id l, struct frame_id r) + { + int eq; +@@ -358,21 +401,22 @@ frame_id_eq (struct frame_id l, struct f + else if (l.stack_addr != r.stack_addr) + /* If .stack addresses are different, the frames are different. */ + eq = 0; +- else if (!l.code_addr_p || !r.code_addr_p) +- /* An invalid code addr is a wild card, always succeed. */ +- eq = 1; +- else if (l.code_addr != r.code_addr) +- /* If .code addresses are different, the frames are different. */ ++ else if (l.code_addr_p && r.code_addr_p && l.code_addr != r.code_addr) ++ /* An invalid code addr is a wild card. If .code addresses are ++ different, the frames are different. */ + eq = 0; +- else if (!l.special_addr_p || !r.special_addr_p) +- /* An invalid special addr is a wild card (or unused), always succeed. */ +- eq = 1; +- else if (l.special_addr == r.special_addr) ++ else if (l.special_addr_p && r.special_addr_p ++ && l.special_addr != r.special_addr) ++ /* An invalid special addr is a wild card (or unused). Otherwise ++ if special addresses are different, the frames are different. */ ++ eq = 0; ++ else if (l.inline_depth != r.inline_depth) ++ /* If inline depths are different, the frames must be different. */ ++ eq = 0; ++ else + /* Frames are equal. */ + eq = 1; +- else +- /* No luck. */ +- eq = 0; ++ + if (frame_debug) + { + fprintf_unfiltered (gdb_stdlog, "{ frame_id_eq (l="); +@@ -417,6 +461,29 @@ frame_id_inner (struct gdbarch *gdbarch, + if (!l.stack_addr_p || !r.stack_addr_p) + /* Like NaN, any operation involving an invalid ID always fails. */ + inner = 0; ++ else if (l.inline_depth > r.inline_depth ++ && l.stack_addr == r.stack_addr ++ && l.code_addr_p == r.code_addr_p ++ && l.special_addr_p == r.special_addr_p ++ && l.special_addr == r.special_addr) ++ { ++ /* Same function, different inlined functions. */ ++ struct block *lb, *rb; ++ ++ gdb_assert (l.code_addr_p && r.code_addr_p); ++ ++ lb = block_for_pc (l.code_addr); ++ rb = block_for_pc (r.code_addr); ++ ++ if (lb == NULL || rb == NULL) ++ /* Something's gone wrong. */ ++ inner = 0; ++ else ++ /* This will return true if LB and RB are the same block, or ++ if the block with the smaller depth lexically encloses the ++ block with the greater depth. */ ++ inner = contained_in (lb, rb); ++ } + else + /* Only return non-zero when strictly inner than. Note that, per + comment in "frame.h", there is some fuzz here. Frameless +@@ -469,8 +536,8 @@ frame_find_by_id (struct frame_id id) + return NULL; + } + +-CORE_ADDR +-frame_pc_unwind (struct frame_info *this_frame) ++static CORE_ADDR ++frame_unwind_pc (struct frame_info *this_frame) + { + if (!this_frame->prev_pc.p) + { +@@ -509,6 +576,12 @@ frame_pc_unwind (struct frame_info *this + } + + CORE_ADDR ++frame_pc_unwind (struct frame_info *this_frame) ++{ ++ return frame_unwind_pc (skip_inlined_frames (this_frame)); ++} ++ ++CORE_ADDR + get_frame_func (struct frame_info *this_frame) + { + struct frame_info *next_frame = this_frame->next; +@@ -1240,7 +1313,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; + +@@ -1280,6 +1352,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. */ +@@ -1347,7 +1427,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; +@@ -1376,6 +1457,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 +@@ -1498,7 +1590,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 +@@ -1543,8 +1635,9 @@ get_prev_frame (struct frame_info *this_ + from main returns directly to the caller of main. Since we don't + stop at main, we should at least stop at the entry point of the + application. */ +- if (!backtrace_past_entry +- && get_frame_type (this_frame) != DUMMY_FRAME && this_frame->level >= 0 ++ if (this_frame->level >= 0 ++ && get_frame_type (this_frame) == NORMAL_FRAME ++ && !backtrace_past_entry + && inside_entry_func (this_frame)) + { + frame_debug_got_null_frame (this_frame, "inside entry func"); +@@ -1555,7 +1648,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) + { +@@ -1570,7 +1664,7 @@ CORE_ADDR + get_frame_pc (struct frame_info *frame) + { + gdb_assert (frame->next != NULL); +- return frame_pc_unwind (frame->next); ++ return frame_unwind_pc (frame->next); + } + + /* Return an address that falls within THIS_FRAME's code block. */ +@@ -1615,17 +1709,58 @@ get_frame_address_in_block (struct frame + We check the type of NEXT_FRAME first, since it is already + known; frame type is determined by the unwinder, and since + we have THIS_FRAME we've already selected an unwinder for +- NEXT_FRAME. */ ++ NEXT_FRAME. ++ ++ If the next frame is inlined, we need to keep going until we find ++ the real function - for instance, if a signal handler is invoked ++ while in an inlined function, then the code address of the ++ "calling" normal function should not be adjusted either. */ ++ ++ while (get_frame_type (next_frame) == INLINE_FRAME) ++ next_frame = next_frame->next; ++ + if (get_frame_type (next_frame) == NORMAL_FRAME +- && get_frame_type (this_frame) == NORMAL_FRAME) ++ && (get_frame_type (this_frame) == NORMAL_FRAME ++ || get_frame_type (this_frame) == INLINE_FRAME)) + return pc - 1; + + return pc; + } + +-static int +-pc_notcurrent (struct frame_info *frame) ++void ++find_frame_sal (struct frame_info *frame, struct symtab_and_line *sal) + { ++ struct frame_info *next_frame; ++ int notcurrent; ++ ++ /* If the next frame represents an inlined function call, this frame's ++ sal is the "call site" of that inlined function, which can not ++ be inferred from get_frame_pc. */ ++ next_frame = get_next_frame (frame); ++ if (frame_inlined_callees (frame) > 0) ++ { ++ struct symbol *sym; ++ ++ if (next_frame) ++ sym = get_frame_function (next_frame); ++ else ++ sym = inline_skipped_symbol (inferior_ptid); ++ ++ init_sal (sal); ++ if (SYMBOL_LINE (sym) != 0) ++ { ++ sal->symtab = SYMBOL_SYMTAB (sym); ++ sal->line = SYMBOL_LINE (sym); ++ } ++ else ++ /* If the symbol does not have a location, we don't know where ++ the call site is. Do not pretend to. This is jarring, but ++ we can't do much better. */ ++ sal->pc = get_frame_pc (frame); ++ ++ return; ++ } ++ + /* If FRAME is not the innermost frame, that normally means that + FRAME->pc points at the return instruction (which is *after* the + call instruction), and we want to get the line containing the +@@ -1635,15 +1770,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 +Index: gdb-6.8.50.20090216-cvs/gdb/frame.h +=================================================================== +--- gdb-6.8.50.20090216-cvs.orig/gdb/frame.h 2009-03-02 11:37:57.000000000 +0100 ++++ gdb-6.8.50.20090216-cvs/gdb/frame.h 2009-03-02 11:38:29.000000000 +0100 +@@ -34,6 +34,9 @@ + frame_unwind_WHAT...(): Unwind THIS frame's WHAT from the NEXT + frame. + ++ get_stack_frame_WHAT...(): Get WHAT for THIS frame, but if THIS is ++ inlined, skip to the containing stack frame. ++ + put_frame_WHAT...(): Put a value into this frame (unsafe, need to + invalidate the frame / regcache afterwards) (better name more + strongly hinting at its unsafeness) +@@ -101,6 +104,10 @@ struct frame_id + Typically, it is set to the address of the entry point of the + frame's function (as returned by get_frame_func). + ++ For inlined functions (INLINE_DEPTH != 0), this is the address of ++ the first executed instruction in the block corresponding to the ++ inlined function. ++ + This field is valid only if code_addr_p is true. Otherwise, this + frame is considered to have a wildcard code address, i.e. one that + matches every address value in frame comparisons. */ +@@ -122,6 +129,10 @@ struct frame_id + unsigned int stack_addr_p : 1; + unsigned int code_addr_p : 1; + unsigned int special_addr_p : 1; ++ ++ /* The inline depth of this frame. A frame representing a "called" ++ inlined function will have this set to a nonzero value. */ ++ int inline_depth; + }; + + /* Methods for constructing and comparing Frame IDs. */ +@@ -157,6 +168,10 @@ extern struct frame_id frame_id_build_wi + non-zero .base). */ + extern int frame_id_p (struct frame_id l); + ++/* Returns non-zero when L is a valid frame representing an inlined ++ function. */ ++extern int frame_id_inlined_p (struct frame_id l); ++ + /* Returns non-zero when L and R identify the same frame, or, if + either L or R have a zero .func, then the same frame base. */ + extern int frame_id_eq (struct frame_id l, struct frame_id r); +@@ -177,6 +192,9 @@ enum frame_type + /* A fake frame, created by GDB when performing an inferior function + call. */ + DUMMY_FRAME, ++ /* A frame representing an inlined function, associated with an ++ upcoming (next, inner, younger) NORMAL_FRAME. */ ++ INLINE_FRAME, + /* In a signal handler, various OSs handle this in various ways. + The main thing is that the frame may be far from normal. */ + SIGTRAMP_FRAME, +@@ -347,6 +365,7 @@ extern CORE_ADDR get_frame_base (struct + + instead, since that avoids the bug. */ + extern struct frame_id get_frame_id (struct frame_info *fi); ++extern struct frame_id get_stack_frame_id (struct frame_info *fi); + extern struct frame_id frame_unwind_id (struct frame_info *next_frame); + + /* Assuming that a frame is `normal', return its base-address, or 0 if +Index: gdb-6.8.50.20090216-cvs/gdb/gdbthread.h +=================================================================== +--- gdb-6.8.50.20090216-cvs.orig/gdb/gdbthread.h 2009-01-19 10:30:36.000000000 +0100 ++++ gdb-6.8.50.20090216-cvs/gdb/gdbthread.h 2009-03-02 11:38:29.000000000 +0100 +@@ -83,6 +83,13 @@ struct thread_info + This is how we know when we step into a subroutine call, and how + to set the frame for the breakpoint used to step out. */ + struct frame_id step_frame_id; ++ ++ /* Similarly, the frame ID of the underlying stack frame (skipping any ++ inlined frames). */ ++ struct frame_id step_stack_frame_id; ++ ++ /* The source file and line at the beginning of the current step ++ operation. Only valid when step_frame_id is set. */ + int current_line; + struct symtab *current_symtab; + +Index: gdb-6.8.50.20090216-cvs/gdb/infcall.c +=================================================================== +--- gdb-6.8.50.20090216-cvs.orig/gdb/infcall.c 2009-02-16 11:31:31.000000000 +0100 ++++ gdb-6.8.50.20090216-cvs/gdb/infcall.c 2009-03-02 11:38:29.000000000 +0100 +@@ -841,15 +841,8 @@ When the function is done executing, GDB + + if (unwind_on_signal_p) + { +- /* The user wants the context restored. */ +- +- /* We must get back to the frame we were before the +- dummy call. */ +- dummy_frame_pop (dummy_id); +- +- /* We also need to restore inferior status to that before the +- dummy call. */ +- restore_inferior_status (inf_status); ++ /* The user wants the context restored. Calling error will ++ run inf_status_cleanup, which does all the work. */ + + /* FIXME: Insert a bunch of wrap_here; name can be very + long if it's a C++ name with arguments and stuff. */ +Index: gdb-6.8.50.20090216-cvs/gdb/infcmd.c +=================================================================== +--- gdb-6.8.50.20090216-cvs.orig/gdb/infcmd.c 2009-02-16 11:04:44.000000000 +0100 ++++ gdb-6.8.50.20090216-cvs/gdb/infcmd.c 2009-03-02 11:38:29.000000000 +0100 +@@ -52,6 +52,7 @@ + #include "cli/cli-decode.h" + #include "gdbthread.h" + #include "valprint.h" ++#include "inline-frame.h" + + /* Functions exported for general use, in inferior.h: */ + +@@ -739,6 +740,17 @@ Can't resume all threads and specify pro + continue_1 (all_threads); + } + ++/* Record the starting point of a "step" or "next" command. */ ++ ++static void ++set_step_frame (struct thread_info *tp) ++{ ++ struct symtab_and_line sal; ++ ++ find_frame_sal (get_current_frame (), &sal); ++ set_step_info (tp, get_current_frame (), sal); ++} ++ + /* Step until outside of current statement. */ + + static void +@@ -916,6 +928,20 @@ step_once (int skip_subroutines, int sin + THREAD is set. */ + struct thread_info *tp = inferior_thread (); + clear_proceed_status (); ++ set_step_frame (tp); ++ ++ /* Step at an inlined function behaves like "down". */ ++ if (!skip_subroutines && !single_inst ++ && inline_skipped_frames (inferior_ptid)) ++ { ++ step_into_inline_frame (inferior_ptid); ++ if (count > 1) ++ step_once (skip_subroutines, single_inst, count - 1, thread); ++ else ++ /* Pretend that we've stopped. */ ++ normal_stop (); ++ return; ++ } + + frame = get_current_frame (); + tp->step_frame_id = get_frame_id (frame); +@@ -1168,6 +1194,7 @@ until_next_command (int from_tty) + clear_proceed_status (); + + frame = get_current_frame (); ++ set_step_frame (tp); + + /* Step until either exited from this function or greater + than the current line (if in symbolic section) or pc (if +@@ -1195,7 +1222,6 @@ until_next_command (int from_tty) + } + + tp->step_over_calls = STEP_OVER_ALL; +- tp->step_frame_id = get_frame_id (frame); + + tp->step_multi = 0; /* Only one call to proceed */ + +@@ -1528,6 +1554,37 @@ finish_command (char *arg, int from_tty) + + clear_proceed_status (); + ++ /* Finishing from an inline frame is completely different. We don't ++ try to show the "return value" - no way to locate it. So we do ++ not need a completion. */ ++ if (get_frame_type (get_selected_frame (_("No selected frame."))) ++ == INLINE_FRAME) ++ { ++ struct thread_info *tp = inferior_thread (); ++ ++ /* Claim we are stepping in the calling frame. An empty step ++ range means that we will stop once we aren't in a function ++ called by that frame. We don't use the magic "1" value for ++ step_range_end, because then infrun will think this is nexti, ++ and not step over the rest of this inlined function call. */ ++ struct symtab_and_line empty_sal; ++ init_sal (&empty_sal); ++ set_step_info (tp, frame, empty_sal); ++ tp->step_range_start = tp->step_range_end = get_frame_pc (frame); ++ tp->step_over_calls = STEP_OVER_ALL; ++ ++ /* Print info on the selected frame, including level number but not ++ source. */ ++ if (from_tty) ++ { ++ printf_filtered (_("Run till exit from ")); ++ print_stack_frame (get_selected_frame (NULL), 1, LOCATION); ++ } ++ ++ proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 1); ++ return; ++ } ++ + /* Find the function we will return from. */ + + function = find_pc_function (get_frame_pc (get_selected_frame (NULL))); +Index: gdb-6.8.50.20090216-cvs/gdb/inferior.h +=================================================================== +--- gdb-6.8.50.20090216-cvs.orig/gdb/inferior.h 2009-03-02 11:37:57.000000000 +0100 ++++ gdb-6.8.50.20090216-cvs/gdb/inferior.h 2009-03-02 11:38:29.000000000 +0100 +@@ -255,6 +255,9 @@ extern void error_is_running (void); + /* Calls error_is_running if the current thread is running. */ + extern void ensure_not_running (void); + ++void set_step_info (struct thread_info *tp, struct frame_info *frame, ++ struct symtab_and_line sal); ++ + /* From infcmd.c */ + + extern void tty_command (char *, int); +Index: gdb-6.8.50.20090216-cvs/gdb/infrun.c +=================================================================== +--- gdb-6.8.50.20090216-cvs.orig/gdb/infrun.c 2009-03-02 11:37:57.000000000 +0100 ++++ gdb-6.8.50.20090216-cvs/gdb/infrun.c 2009-03-02 11:38:29.000000000 +0100 +@@ -48,6 +48,7 @@ + #include "gdb_assert.h" + #include "mi/mi-common.h" + #include "event-top.h" ++#include "inline-frame.h" + + /* Prototypes for local functions */ + +@@ -205,7 +206,7 @@ static unsigned char *signal_program; + + /* Value to pass to target_resume() to cause all threads to resume */ + +-#define RESUME_ALL (pid_to_ptid (-1)) ++#define RESUME_ALL minus_one_ptid + + /* Command list pointer for the "stop" placeholder. */ + +@@ -1143,6 +1144,8 @@ a command like `return' or `jump' to con + step = 0; + } + ++ clear_inline_frame_state (resume_ptid); ++ + if (debug_displaced + && use_displaced_stepping (gdbarch) + && tp->trap_expected) +@@ -1184,6 +1187,7 @@ clear_proceed_status_thread (struct thre + tp->step_range_start = 0; + tp->step_range_end = 0; + tp->step_frame_id = null_frame_id; ++ tp->step_stack_frame_id = null_frame_id; + tp->step_over_calls = STEP_OVER_UNDEBUGGABLE; + tp->stop_requested = 0; + +@@ -1523,6 +1527,9 @@ init_wait_for_inferior (void) + init_infwait_state (); + + displaced_step_clear (); ++ ++ /* Discard any skipped inlined frames. */ ++ clear_inline_frame_state (minus_one_ptid); + } + + +@@ -1578,7 +1585,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); + +@@ -1936,10 +1943,21 @@ fetch_inferior_event (void *client_data) + display_gdb_prompt (0); + } + ++/* Record the frame and location we're currently stepping through. */ ++void ++set_step_info (struct thread_info *tp, struct frame_info *frame, ++ struct symtab_and_line sal) ++{ ++ tp->step_frame_id = get_frame_id (frame); ++ tp->step_stack_frame_id = get_stack_frame_id (frame); ++ tp->current_symtab = sal.symtab; ++ tp->current_line = sal.line; ++} ++ + /* Prepare an execution control state for looping through a + wait_for_inferior-type loop. */ + +-void ++static void + init_execution_control_state (struct execution_control_state *ecs) + { + ecs->random_signal = 0; +@@ -1950,16 +1968,10 @@ init_execution_control_state (struct exe + void + init_thread_stepping_state (struct thread_info *tss) + { +- struct symtab_and_line sal; +- + tss->stepping_over_breakpoint = 0; + tss->step_after_step_resume_breakpoint = 0; + tss->stepping_through_solib_after_catch = 0; + tss->stepping_through_solib_catchpoints = NULL; +- +- sal = find_pc_line (tss->prev_pc, 0); +- tss->current_line = sal.line; +- tss->current_symtab = sal.symtab; + } + + /* Return the cached copy of the last pid/waitstatus returned by +@@ -2129,6 +2141,22 @@ ensure_not_running (void) + error_is_running (); + } + ++static int ++stepped_in_from (struct frame_info *frame, struct frame_id step_frame_id) ++{ ++ for (frame = get_prev_frame (frame); ++ frame != NULL; ++ frame = get_prev_frame (frame)) ++ { ++ if (frame_id_eq (get_frame_id (frame), step_frame_id)) ++ return 1; ++ if (get_frame_type (frame) != INLINE_FRAME) ++ break; ++ } ++ ++ return 0; ++} ++ + /* Given an execution control state that has been freshly filled in + by an event from the inferior, figure out what it means and take + appropriate action. */ +@@ -2825,6 +2853,12 @@ targets should add new threads to the th + ecs->random_signal = 0; + stopped_by_random_signal = 0; + ++ /* Hide inlined functions starting here, unless we just performed stepi or ++ nexti. After stepi and nexti, always show the innermost frame (not any ++ inline function call sites). */ ++ if (ecs->event_thread->step_range_end != 1) ++ skip_inline_frames (ecs->ptid); ++ + if (ecs->event_thread->stop_signal == TARGET_SIGNAL_TRAP + && ecs->event_thread->trap_expected + && gdbarch_single_step_through_delay_p (current_gdbarch) +@@ -3057,8 +3091,8 @@ process_event_stop_test: + && ecs->event_thread->stop_signal != TARGET_SIGNAL_0 + && (ecs->event_thread->step_range_start <= stop_pc + && stop_pc < ecs->event_thread->step_range_end) +- && frame_id_eq (get_frame_id (get_current_frame ()), +- ecs->event_thread->step_frame_id) ++ && frame_id_eq (get_stack_frame_id (get_current_frame ()), ++ ecs->event_thread->step_stack_frame_id) + && ecs->event_thread->step_resume_breakpoint == NULL) + { + /* The inferior is about to take a signal that will take it +@@ -3449,10 +3483,10 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME ( + NOTE: frame_id_eq will never report two invalid frame IDs as + being equal, so to get into this block, both the current and + previous frame must have valid frame IDs. */ +- if (!frame_id_eq (get_frame_id (get_current_frame ()), +- ecs->event_thread->step_frame_id) ++ if (!frame_id_eq (get_stack_frame_id (get_current_frame ()), ++ ecs->event_thread->step_stack_frame_id) + && (frame_id_eq (frame_unwind_id (get_current_frame ()), +- ecs->event_thread->step_frame_id) ++ ecs->event_thread->step_stack_frame_id) + || execution_direction == EXEC_REVERSE)) + { + CORE_ADDR real_stop_pc; +@@ -3695,6 +3729,82 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME ( + return; + } + ++ /* Look for "calls" to inlined functions, part one. If the inline ++ frame machinery detected some skipped call sites, we have entered ++ a new inline function. */ ++ ++ if (frame_id_eq (get_frame_id (get_current_frame ()), ++ ecs->event_thread->step_frame_id) ++ && inline_skipped_frames (ecs->ptid)) ++ { ++ struct symtab_and_line call_sal; ++ ++ if (debug_infrun) ++ fprintf_unfiltered (gdb_stdlog, ++ "infrun: stepped into inlined function\n"); ++ ++ find_frame_sal (get_current_frame (), &call_sal); ++ ++ if (ecs->event_thread->step_over_calls != STEP_OVER_ALL) ++ { ++ /* For "step", we're going to stop. But if the call site ++ for this inlined function is on the same source line as ++ we were previously stepping, go down into the function ++ first. Otherwise stop at the call site. */ ++ ++ if (call_sal.line == ecs->event_thread->current_line ++ && call_sal.symtab == ecs->event_thread->current_symtab) ++ step_into_inline_frame (ecs->ptid); ++ ++ ecs->event_thread->stop_step = 1; ++ print_stop_reason (END_STEPPING_RANGE, 0); ++ stop_stepping (ecs); ++ return; ++ } ++ else ++ { ++ /* For "next", we should stop at the call site if it is on a ++ different source line. Otherwise continue through the ++ inlined function. */ ++ if (call_sal.line == ecs->event_thread->current_line ++ && call_sal.symtab == ecs->event_thread->current_symtab) ++ keep_going (ecs); ++ else ++ { ++ ecs->event_thread->stop_step = 1; ++ print_stop_reason (END_STEPPING_RANGE, 0); ++ stop_stepping (ecs); ++ } ++ return; ++ } ++ } ++ ++ /* Look for "calls" to inlined functions, part two. If we are still ++ in the same real function we were stepping through, but we have ++ to go further up to find the exact frame ID, we are stepping ++ through a more inlined call beyond its call site. */ ++ ++ if (get_frame_type (get_current_frame ()) == INLINE_FRAME ++ && !frame_id_eq (get_frame_id (get_current_frame ()), ++ ecs->event_thread->step_frame_id) ++ && stepped_in_from (get_current_frame (), ++ ecs->event_thread->step_frame_id)) ++ { ++ if (debug_infrun) ++ fprintf_unfiltered (gdb_stdlog, ++ "infrun: stepping through inlined function\n"); ++ ++ if (ecs->event_thread->step_over_calls == STEP_OVER_ALL) ++ keep_going (ecs); ++ else ++ { ++ ecs->event_thread->stop_step = 1; ++ print_stop_reason (END_STEPPING_RANGE, 0); ++ stop_stepping (ecs); ++ } ++ return; ++ } ++ + if ((stop_pc == stop_pc_sal.pc) + && (ecs->event_thread->current_line != stop_pc_sal.line + || ecs->event_thread->current_symtab != stop_pc_sal.symtab)) +@@ -3720,9 +3830,7 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME ( + + ecs->event_thread->step_range_start = stop_pc_sal.pc; + ecs->event_thread->step_range_end = stop_pc_sal.end; +- ecs->event_thread->step_frame_id = get_frame_id (get_current_frame ()); +- ecs->event_thread->current_line = stop_pc_sal.line; +- ecs->event_thread->current_symtab = stop_pc_sal.symtab; ++ set_step_info (ecs->event_thread, get_current_frame (), stop_pc_sal); + + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, "infrun: keep going\n"); +@@ -4968,6 +5076,7 @@ struct inferior_status + CORE_ADDR step_range_start; + CORE_ADDR step_range_end; + struct frame_id step_frame_id; ++ struct frame_id step_stack_frame_id; + enum step_over_calls_kind step_over_calls; + CORE_ADDR step_resume_break_address; + int stop_after_trap; +@@ -4997,6 +5106,7 @@ save_inferior_status (void) + inf_status->step_range_start = tp->step_range_start; + inf_status->step_range_end = tp->step_range_end; + inf_status->step_frame_id = tp->step_frame_id; ++ inf_status->step_stack_frame_id = tp->step_stack_frame_id; + inf_status->step_over_calls = tp->step_over_calls; + inf_status->stop_after_trap = stop_after_trap; + inf_status->stop_soon = inf->stop_soon; +@@ -5050,6 +5160,7 @@ restore_inferior_status (struct inferior + tp->step_range_start = inf_status->step_range_start; + tp->step_range_end = inf_status->step_range_end; + tp->step_frame_id = inf_status->step_frame_id; ++ tp->step_stack_frame_id = inf_status->step_stack_frame_id; + tp->step_over_calls = inf_status->step_over_calls; + stop_after_trap = inf_status->stop_after_trap; + inf->stop_soon = inf_status->stop_soon; +Index: gdb-6.8.50.20090216-cvs/gdb/inline-frame.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ gdb-6.8.50.20090216-cvs/gdb/inline-frame.c 2009-03-02 11:38:29.000000000 +0100 +@@ -0,0 +1,382 @@ ++/* Inline frame unwinder for GDB. ++ ++ Copyright (C) 2008 Free Software Foundation, Inc. ++ ++ This file is part of GDB. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++#include "defs.h" ++#include "addrmap.h" ++#include "block.h" ++#include "frame-unwind.h" ++#include "inferior.h" ++#include "symtab.h" ++#include "vec.h" ++ ++#include "gdb_assert.h" ++ ++/* We need to save a few variables for every thread stopped at the ++ virtual call site of an inlined function. If there was always a ++ "struct thread_info", we could hang it off that; in the mean time, ++ keep our own list. */ ++struct inline_state ++{ ++ /* The thread this data relates to. It should be a currently ++ stopped thread; we assume thread IDs never change while the ++ thread is stopped. */ ++ ptid_t ptid; ++ ++ /* The number of inlined functions we are skipping. Each of these ++ functions can be stepped in to. */ ++ int skipped_frames; ++ ++ /* Only valid if SKIPPED_FRAMES is non-zero. This is the PC used ++ when calculating SKIPPED_FRAMES; used to check whether we have ++ moved to a new location by user request. If so, we invalidate ++ any skipped frames. */ ++ CORE_ADDR saved_pc; ++ ++ /* Only valid if SKIPPED_FRAMES is non-zero. This is the symbol ++ of the outermost skipped inline function. It's used to find the ++ call site of the current frame. */ ++ struct symbol *skipped_symbol; ++}; ++ ++typedef struct inline_state inline_state_s; ++DEF_VEC_O(inline_state_s); ++ ++static VEC(inline_state_s) *inline_states; ++ ++/* Locate saved inlined frame state for PTID, if it exists. */ ++ ++static struct inline_state * ++find_inline_frame_state (ptid_t ptid) ++{ ++ struct inline_state *state; ++ int ix; ++ ++ for (ix = 0; VEC_iterate (inline_state_s, inline_states, ix, state); ix++) ++ { ++ if (ptid_equal (state->ptid, ptid)) ++ return state; ++ } ++ ++ return NULL; ++} ++ ++/* Allocate saved inlined frame state for PTID. */ ++ ++static struct inline_state * ++allocate_inline_frame_state (ptid_t ptid) ++{ ++ struct inline_state *state; ++ ++ state = VEC_safe_push (inline_state_s, inline_states, NULL); ++ memset (state, 0, sizeof (*state)); ++ state->ptid = ptid; ++ ++ return state; ++} ++ ++/* Forget about any hidden inlined functions in PTID, which is new or ++ about to be resumed. If PTID is minus_one_ptid, forget about all ++ hidden inlined functions. */ ++ ++void ++clear_inline_frame_state (ptid_t ptid) ++{ ++ struct inline_state *state; ++ int ix; ++ ++ if (ptid_equal (ptid, minus_one_ptid)) ++ { ++ VEC_free (inline_state_s, inline_states); ++ return; ++ } ++ ++ for (ix = 0; VEC_iterate (inline_state_s, inline_states, ix, state); ix++) ++ if (ptid_equal (state->ptid, ptid)) ++ { ++ VEC_unordered_remove (inline_state_s, inline_states, ix); ++ return; ++ } ++} ++ ++static void ++inline_frame_this_id (struct frame_info *this_frame, ++ void **this_cache, ++ struct frame_id *this_id) ++{ ++ struct symbol *func; ++ ++ /* In order to have a stable frame ID for a given inline function, ++ we must get the stack / special addresses from the underlying ++ real frame's this_id method. So we must call get_prev_frame. ++ Because we are inlined into some function, there must be previous ++ frames, so this is safe - as long as we're careful not to ++ create any cycles. */ ++ *this_id = get_frame_id (get_prev_frame (this_frame)); ++ ++ /* We need a valid frame ID, so we need to be based on a valid ++ frame. FSF submission NOTE: this would be a good assertion to ++ apply to all frames, all the time. That would fix the ambiguity ++ of null_frame_id (between "no/any frame" and "the outermost ++ frame"). This will take work. */ ++ gdb_assert (frame_id_p (*this_id)); ++ ++ /* Future work NOTE: Alexandre Oliva applied a patch to GCC 4.3 ++ which generates DW_AT_entry_pc for inlined functions when ++ possible. If this attribute is available, we should use it ++ in the frame ID (and eventually, to set breakpoints). */ ++ func = get_frame_function (this_frame); ++ gdb_assert (func != NULL); ++ (*this_id).code_addr = BLOCK_START (SYMBOL_BLOCK_VALUE (func)); ++ (*this_id).inline_depth++; ++} ++ ++static struct value * ++inline_frame_prev_register (struct frame_info *this_frame, void **this_cache, ++ int regnum) ++{ ++ /* Use get_frame_register_value instead of ++ frame_unwind_got_register, to avoid requiring this frame's ID. ++ This frame's ID depends on the previous frame's ID (unusual), and ++ the previous frame's ID depends on this frame's unwound ++ registers. If unwinding registers from this frame called ++ get_frame_id, there would be a loop. ++ ++ Do not copy this code into any other unwinder! Inlined functions ++ are special; other unwinders must not have a dependency on the ++ previous frame's ID, and therefore can and should use ++ frame_unwind_got_register instead. */ ++ return get_frame_register_value (this_frame, regnum); ++} ++ ++/* Check whether we are at an inlining site that does not already ++ have an associated frame. */ ++ ++static int ++inline_frame_sniffer (const struct frame_unwind *self, ++ struct frame_info *this_frame, ++ void **this_cache) ++{ ++ CORE_ADDR this_pc; ++ struct block *frame_block, *cur_block; ++ int depth; ++ struct frame_info *next_frame; ++ struct inline_state *state = find_inline_frame_state (inferior_ptid); ++ ++ this_pc = get_frame_address_in_block (this_frame); ++ frame_block = block_for_pc (this_pc); ++ if (frame_block == NULL) ++ return 0; ++ ++ /* Calculate DEPTH, the number of inlined functions at this ++ location. */ ++ depth = 0; ++ cur_block = frame_block; ++ while (BLOCK_SUPERBLOCK (cur_block)) ++ { ++ if (block_inlined_p (cur_block)) ++ depth++; ++ ++ cur_block = BLOCK_SUPERBLOCK (cur_block); ++ } ++ ++ /* Check how many inlined functions already have frames. */ ++ for (next_frame = get_next_frame (this_frame); ++ next_frame && get_frame_type (next_frame) == INLINE_FRAME; ++ next_frame = get_next_frame (next_frame)) ++ { ++ gdb_assert (depth > 0); ++ depth--; ++ } ++ ++ /* If this is the topmost frame, or all frames above us are inlined, ++ then check whether we were requested to skip some frames (so they ++ can be stepped into later). */ ++ if (state != NULL && state->skipped_frames > 0 && next_frame == NULL) ++ { ++ if (this_pc != state->saved_pc) ++ state->skipped_frames = 0; ++ else ++ { ++ gdb_assert (depth >= state->skipped_frames); ++ depth -= state->skipped_frames; ++ } ++ } ++ ++ /* If all the inlined functions here already have frames, then pass ++ to the normal unwinder for this PC. */ ++ if (depth == 0) ++ return 0; ++ ++ /* If the next frame is an inlined function, but not the outermost, then ++ we are the next outer. If it is not an inlined function, then we ++ are the innermost inlined function of a different real frame. */ ++ return 1; ++} ++ ++const struct frame_unwind inline_frame_unwinder = { ++ INLINE_FRAME, ++ inline_frame_this_id, ++ inline_frame_prev_register, ++ NULL, ++ inline_frame_sniffer ++}; ++ ++const struct frame_unwind *const inline_frame_unwind = &inline_frame_unwinder; ++ ++/* Return non-zero if BLOCK, an inlined function block containing PC, ++ has a group of contiguous instructions starting at PC (but not ++ before it). */ ++ ++static int ++block_starting_point_at (CORE_ADDR pc, struct block *block) ++{ ++ struct blockvector *bv; ++ struct block *new_block; ++ ++ bv = blockvector_for_pc (pc, NULL); ++ if (BLOCKVECTOR_MAP (bv) == NULL) ++ return 0; ++ ++ new_block = addrmap_find (BLOCKVECTOR_MAP (bv), pc - 1); ++ if (new_block == NULL) ++ return 1; ++ ++ if (new_block == block || contained_in (new_block, block)) ++ return 0; ++ ++ /* The immediately preceeding address belongs to a different block, ++ which is not a child of this one. Treat this as an entrance into ++ BLOCK. */ ++ return 1; ++} ++ ++/* Skip all inlined functions whose call sites are at the current PC. ++ Frames for the hidden functions will not appear in the backtrace until the ++ user steps into them. */ ++ ++void ++skip_inline_frames (ptid_t ptid) ++{ ++ CORE_ADDR this_pc; ++ struct block *frame_block, *cur_block; ++ struct symbol *last_sym = NULL; ++ int skip_count = 0; ++ struct inline_state *state; ++ ++ /* This function is called right after reinitializing the frame ++ cache. We try not to do more unwinding than absolutely ++ necessary, for performance. */ ++ this_pc = get_frame_pc (get_current_frame ()); ++ frame_block = block_for_pc (this_pc); ++ ++ if (frame_block != NULL) ++ { ++ cur_block = frame_block; ++ while (BLOCK_SUPERBLOCK (cur_block)) ++ { ++ if (block_inlined_p (cur_block)) ++ { ++ /* See comments in inline_frame_this_id about this use ++ of BLOCK_START. */ ++ if (BLOCK_START (cur_block) == this_pc ++ || block_starting_point_at (this_pc, cur_block)) ++ { ++ skip_count++; ++ last_sym = BLOCK_FUNCTION (cur_block); ++ } ++ else ++ break; ++ } ++ cur_block = BLOCK_SUPERBLOCK (cur_block); ++ } ++ } ++ ++ gdb_assert (find_inline_frame_state (ptid) == NULL); ++ state = allocate_inline_frame_state (ptid); ++ state->skipped_frames = skip_count; ++ state->saved_pc = this_pc; ++ state->skipped_symbol = last_sym; ++ ++ if (skip_count != 0) ++ reinit_frame_cache (); ++} ++ ++/* Step into an inlined function by unhiding it. */ ++ ++void ++step_into_inline_frame (ptid_t ptid) ++{ ++ struct inline_state *state = find_inline_frame_state (ptid); ++ ++ gdb_assert (state != NULL && state->skipped_frames > 0); ++ state->skipped_frames--; ++ reinit_frame_cache (); ++} ++ ++/* Return the number of hidden functions inlined into the current ++ frame. */ ++ ++int ++inline_skipped_frames (ptid_t ptid) ++{ ++ struct inline_state *state = find_inline_frame_state (ptid); ++ ++ if (state == NULL) ++ return 0; ++ else ++ return state->skipped_frames; ++} ++ ++/* If one or more inlined functions are hidden, return the symbol for ++ the function inlined into the current frame. */ ++ ++struct symbol * ++inline_skipped_symbol (ptid_t ptid) ++{ ++ struct inline_state *state = find_inline_frame_state (ptid); ++ ++ gdb_assert (state != NULL); ++ return state->skipped_symbol; ++} ++ ++/* Return the number of functions inlined into THIS_FRAME. Some of ++ the callees may not have associated frames (see ++ skip_inline_frames). */ ++ ++int ++frame_inlined_callees (struct frame_info *this_frame) ++{ ++ struct frame_info *next_frame; ++ int inline_count = 0; ++ ++ /* First count how many inlined functions at this PC have frames ++ above FRAME (are inlined into FRAME). */ ++ for (next_frame = get_next_frame (this_frame); ++ next_frame && get_frame_type (next_frame) == INLINE_FRAME; ++ next_frame = get_next_frame (next_frame)) ++ inline_count++; ++ ++ /* Simulate some most-inner inlined frames which were suppressed, so ++ they can be stepped into later. If we are unwinding already ++ outer frames from some non-inlined frame this does not apply. */ ++ if (next_frame == NULL) ++ inline_count += inline_skipped_frames (inferior_ptid); ++ ++ return inline_count; ++} +Index: gdb-6.8.50.20090216-cvs/gdb/inline-frame.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ gdb-6.8.50.20090216-cvs/gdb/inline-frame.h 2009-03-02 11:38:29.000000000 +0100 +@@ -0,0 +1,62 @@ ++/* Definitions for inline frame support. ++ ++ Copyright (C) 2008 Free Software Foundation, Inc. ++ ++ This file is part of GDB. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++#if !defined (INLINE_FRAME_H) ++#define INLINE_FRAME_H 1 ++ ++struct frame_info; ++struct frame_unwind; ++ ++/* The inline frame unwinder. */ ++ ++extern const struct frame_unwind *const inline_frame_unwind; ++ ++/* Skip all inlined functions whose call sites are at the current PC. ++ Frames for the hidden functions will not appear in the backtrace until the ++ user steps into them. */ ++ ++void skip_inline_frames (ptid_t ptid); ++ ++/* Forget about any hidden inlined functions in PTID, which is new or ++ about to be resumed. If PTID is minus_one_ptid, forget about all ++ hidden inlined functions. */ ++ ++void clear_inline_frame_state (ptid_t ptid); ++ ++/* Step into an inlined function by unhiding it. */ ++ ++void step_into_inline_frame (ptid_t ptid); ++ ++/* Return the number of hidden functions inlined into the current ++ frame. */ ++ ++int inline_skipped_frames (ptid_t ptid); ++ ++/* If one or more inlined functions are hidden, return the symbol for ++ the function inlined into the current frame. */ ++ ++struct symbol *inline_skipped_symbol (ptid_t ptid); ++ ++/* Return the number of functions inlined into THIS_FRAME. Some of ++ the callees may not have associated frames (see ++ skip_inline_frames). */ ++ ++int frame_inlined_callees (struct frame_info *this_frame); ++ ++#endif /* !defined (INLINE_FRAME_H) */ +Index: gdb-6.8.50.20090216-cvs/gdb/minsyms.c +=================================================================== +--- gdb-6.8.50.20090216-cvs.orig/gdb/minsyms.c 2009-02-16 11:31:31.000000000 +0100 ++++ gdb-6.8.50.20090216-cvs/gdb/minsyms.c 2009-03-02 11:38:29.000000000 +0100 +@@ -762,7 +762,7 @@ prim_record_minimal_symbol_and_info (con + + if (msym_bunch_index == BUNCH_SIZE) + { +- new = (struct msym_bunch *) xmalloc (sizeof (struct msym_bunch)); ++ new = XCALLOC (1, struct msym_bunch); + msym_bunch_index = 0; + new->next = msym_bunch; + msym_bunch = new; +Index: gdb-6.8.50.20090216-cvs/gdb/s390-tdep.c +=================================================================== +--- gdb-6.8.50.20090216-cvs.orig/gdb/s390-tdep.c 2009-01-07 09:45:04.000000000 +0100 ++++ gdb-6.8.50.20090216-cvs/gdb/s390-tdep.c 2009-03-02 11:38:29.000000000 +0100 +@@ -1182,6 +1182,7 @@ s390_prologue_frame_unwind_cache (struct + CORE_ADDR prev_sp; + int frame_pointer; + int size; ++ struct frame_info *next_frame; + + /* Try to find the function start address. If we can't find it, we don't + bother searching for it -- with modern compilers this would be mostly +@@ -1215,7 +1216,10 @@ s390_prologue_frame_unwind_cache (struct + /* FIXME: cagney/2004-05-01: This sanity check shouldn't be + needed, instead the code should simpliy rely on its + analysis. */ +- if (get_next_frame (this_frame) ++ next_frame = get_next_frame (this_frame); ++ while (next_frame && get_frame_type (next_frame) == INLINE_FRAME) ++ next_frame = get_next_frame (next_frame); ++ if (next_frame + && get_frame_type (get_next_frame (this_frame)) == NORMAL_FRAME) + return 0; + +@@ -1261,8 +1265,11 @@ s390_prologue_frame_unwind_cache (struct + This can only happen in an innermost frame. */ + /* FIXME: cagney/2004-05-01: This sanity check shouldn't be needed, + instead the code should simpliy rely on its analysis. */ ++ next_frame = get_next_frame (this_frame); ++ while (next_frame && get_frame_type (next_frame) == INLINE_FRAME) ++ next_frame = get_next_frame (next_frame); + if (size > 0 +- && (!get_next_frame (this_frame) ++ && (next_frame == NULL + || get_frame_type (get_next_frame (this_frame)) != NORMAL_FRAME)) + { + /* See the comment in s390_in_function_epilogue_p on why this is +Index: gdb-6.8.50.20090216-cvs/gdb/stack.c +=================================================================== +--- gdb-6.8.50.20090216-cvs.orig/gdb/stack.c 2009-03-02 11:37:57.000000000 +0100 ++++ gdb-6.8.50.20090216-cvs/gdb/stack.c 2009-03-02 11:40:00.000000000 +0100 +@@ -45,6 +45,7 @@ + #include "valprint.h" + #include "gdbthread.h" + #include "cp-support.h" ++#include "inline-frame.h" + #include "arch-utils.h" + + #include "gdb_assert.h" +@@ -99,6 +100,30 @@ print_stack_frame_stub (void *args) + return 0; + } + ++/* Return 1 if we should display the address in addition to the location, ++ because we are in the middle of a statement. */ ++ ++static int ++frame_show_address (struct frame_info *frame, ++ struct symtab_and_line sal) ++{ ++ /* If there is a line number, but no PC, then there is no location ++ information associated with this sal. The only way that should ++ happen is for the call sites of inlined functions (SAL comes from ++ find_frame_sal). Otherwise, we would have some PC range if the ++ SAL came from a line table. */ ++ if (sal.line != 0 && sal.pc == 0 && sal.end == 0) ++ { ++ if (get_next_frame (frame) == NULL) ++ gdb_assert (inline_skipped_frames (inferior_ptid) > 0); ++ else ++ gdb_assert (get_frame_type (get_next_frame (frame)) == INLINE_FRAME); ++ return 0; ++ } ++ ++ return get_frame_pc (frame) != sal.pc; ++} ++ + /* Show or print a stack frame FRAME briefly. The output is format + according to PRINT_LEVEL and PRINT_WHAT printing the frame's + relative level, function name, argument list, and file name and +@@ -554,7 +579,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, +@@ -612,7 +637,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 +@@ -633,8 +658,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) +@@ -691,7 +721,7 @@ print_frame (struct frame_info *frame, i + } + get_user_print_options (&opts); + if (opts.addressprint) +- if (get_frame_pc (frame) != sal.pc || !sal.symtab ++ if (frame_show_address (frame, sal) || !sal.symtab + || print_what == LOC_AND_ADDRESS) + { + annotate_frame_address (); +@@ -871,8 +901,16 @@ parse_frame_specification_1 (const char + { + if (frame_id_eq (id, get_frame_id (fid))) + { +- while (frame_id_eq (id, frame_unwind_id (fid))) +- fid = get_prev_frame (fid); ++ struct frame_info *prev_frame; ++ ++ while (1) ++ { ++ prev_frame = get_prev_frame (fid); ++ if (!prev_frame ++ || !frame_id_eq (id, get_frame_id (prev_frame))) ++ break; ++ fid = prev_frame; ++ } + return fid; + } + } +@@ -1006,8 +1044,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)), +@@ -1471,7 +1511,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); +@@ -1544,7 +1586,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); +@@ -1814,6 +1858,9 @@ return_command (char *retval_exp, int fr + thisframe = get_selected_frame ("No selected frame."); + thisfun = get_frame_function (thisframe); + ++ if (get_frame_type (get_current_frame ()) == INLINE_FRAME) ++ error (_("Can not force return from an inlined function.")); ++ + /* Compute the return value. If the computation triggers an error, + let it bail. If the return type can't be handled, set + RETURN_VALUE to NULL, and QUERY_PREFIX to an informational +Index: gdb-6.8.50.20090216-cvs/gdb/symtab.c +=================================================================== +--- gdb-6.8.50.20090216-cvs.orig/gdb/symtab.c 2009-03-02 11:37:57.000000000 +0100 ++++ gdb-6.8.50.20090216-cvs/gdb/symtab.c 2009-03-02 11:38:29.000000000 +0100 +@@ -1373,10 +1373,13 @@ lookup_symbol_aux_local (const char *nam + sym = lookup_symbol_aux_block (name, linkage_name, block, domain); + if (sym != NULL) + return sym; ++ ++ if (BLOCK_FUNCTION (block) != NULL && block_inlined_p (block)) ++ break; + block = BLOCK_SUPERBLOCK (block); + } + +- /* We've reached the static block without finding a result. */ ++ /* We've reached the edge of the function without finding a result. */ + + return NULL; + } +@@ -2605,6 +2608,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); +@@ -2643,6 +2647,25 @@ find_function_start_sal (struct symbol * + + sal.pc = pc; + ++ /* Check if we are now inside an inlined function. If we can, ++ use the call site of the function instead. */ ++ b = block_for_pc_sect (sal.pc, SYMBOL_OBJ_SECTION (sym)); ++ function_block = NULL; ++ while (b != NULL) ++ { ++ if (BLOCK_FUNCTION (b) != NULL && block_inlined_p (b)) ++ function_block = b; ++ else if (BLOCK_FUNCTION (b) != NULL) ++ break; ++ b = BLOCK_SUPERBLOCK (b); ++ } ++ if (function_block != NULL ++ && SYMBOL_LINE (BLOCK_FUNCTION (function_block)) != 0) ++ { ++ sal.line = SYMBOL_LINE (BLOCK_FUNCTION (function_block)); ++ sal.symtab = SYMBOL_SYMTAB (BLOCK_FUNCTION (function_block)); ++ } ++ + return sal; + } + +@@ -3665,6 +3688,24 @@ add_macro_name (const char *name, const + datum->text, datum->word); + } + ++static void ++completion_list_add_fields (struct symbol *sym, char *sym_text, ++ int sym_text_len, char *text, char *word) ++{ ++ if (SYMBOL_CLASS (sym) == LOC_TYPEDEF) ++ { ++ struct type *t = SYMBOL_TYPE (sym); ++ enum type_code c = TYPE_CODE (t); ++ int j; ++ ++ if (c == TYPE_CODE_UNION || c == TYPE_CODE_STRUCT) ++ for (j = TYPE_N_BASECLASSES (t); j < TYPE_NFIELDS (t); j++) ++ if (TYPE_FIELD_NAME (t, j)) ++ completion_list_add_name (TYPE_FIELD_NAME (t, j), ++ sym_text, sym_text_len, text, word); ++ } ++} ++ + char ** + default_make_symbol_completion_list (char *text, char *word) + { +@@ -3677,9 +3718,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; +@@ -3789,41 +3830,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. */ +@@ -3842,9 +3885,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); +@@ -4311,6 +4351,25 @@ skip_prologue_using_sal (CORE_ADDR func_ + line mark the prologue -> body transition. */ + if (sal.line >= prologue_sal.line) + break; ++ ++ /* The line number is smaller. Check that it's from the ++ same function, not something inlined. If it's inlined, ++ then there is no point comparing the line numbers. */ ++ bl = block_for_pc (prologue_sal.end); ++ while (bl) ++ { ++ if (block_inlined_p (bl)) ++ break; ++ if (BLOCK_FUNCTION (bl)) ++ { ++ bl = NULL; ++ break; ++ } ++ bl = BLOCK_SUPERBLOCK (bl); ++ } ++ if (bl != NULL) ++ break; ++ + /* The case in which compiler's optimizer/scheduler has + moved instructions into the prologue. We look ahead in + the function looking for address ranges whose +Index: gdb-6.8.50.20090216-cvs/gdb/symtab.h +=================================================================== +--- gdb-6.8.50.20090216-cvs.orig/gdb/symtab.h 2009-02-09 10:56:03.000000000 +0100 ++++ gdb-6.8.50.20090216-cvs/gdb/symtab.h 2009-03-02 11:38:29.000000000 +0100 +@@ -556,9 +556,18 @@ struct symbol + + unsigned is_argument : 1; + +- /* Line number of definition. FIXME: Should we really make the assumption +- that nobody will try to debug files longer than 64K lines? What about +- machine generated programs? */ ++ /* Whether this is an inlined function (class LOC_BLOCK only). */ ++ unsigned is_inlined : 1; ++ ++ /* Line number of this symbol's definition, except for inlined ++ functions. For an inlined function (class LOC_BLOCK and ++ SYMBOL_INLINED set) this is the line number of the function's call ++ site. Inlined function symbols are not definitions, and they are ++ never found by symbol table lookup. ++ ++ FIXME: Should we really make the assumption that nobody will try ++ to debug files longer than 64K lines? What about machine ++ generated programs? */ + + unsigned short line; + +@@ -589,6 +598,7 @@ struct symbol + #define SYMBOL_DOMAIN(symbol) (symbol)->domain + #define SYMBOL_CLASS(symbol) (symbol)->aclass + #define SYMBOL_IS_ARGUMENT(symbol) (symbol)->is_argument ++#define SYMBOL_INLINED(symbol) (symbol)->is_inlined + #define SYMBOL_TYPE(symbol) (symbol)->type + #define SYMBOL_LINE(symbol) (symbol)->line + #define SYMBOL_SYMTAB(symbol) (symbol)->symtab +Index: gdb-6.8.50.20090216-cvs/gdb/testsuite/gdb.base/break.exp +=================================================================== +--- gdb-6.8.50.20090216-cvs.orig/gdb/testsuite/gdb.base/break.exp 2009-01-20 10:17:27.000000000 +0100 ++++ gdb-6.8.50.20090216-cvs/gdb/testsuite/gdb.base/break.exp 2009-03-02 11:38:29.000000000 +0100 +@@ -880,6 +880,13 @@ gdb_expect { + # marker4() is defined at line 46 when compiled with -DPROTOTYPES + pass "run until breakpoint set at small function, optimized file (line bp_location14)" + } ++ -re "Breakpoint $decimal, factorial \\(.*\\) .*\{\r\n$gdb_prompt" { ++ # GCC 4.3 emits bad line number information - see gcc/36748. ++ if { [test_compiler_info "gcc-4-3-*"] } { ++ setup_xfail *-*-* ++ } ++ fail "run until breakpoint set at small function, optimized file" ++ } + -re ".*$gdb_prompt " { + fail "run until breakpoint set at small function, optimized file" + } +Index: gdb-6.8.50.20090216-cvs/gdb/testsuite/gdb.cp/annota2.exp +=================================================================== +--- gdb-6.8.50.20090216-cvs.orig/gdb/testsuite/gdb.cp/annota2.exp 2009-01-07 09:45:17.000000000 +0100 ++++ gdb-6.8.50.20090216-cvs/gdb/testsuite/gdb.cp/annota2.exp 2009-03-02 11:38:29.000000000 +0100 +@@ -119,10 +119,11 @@ gdb_expect { + # continue until exit + # this will test: + # annotate-exited ++# `a.x is 1' is asynchronous regarding to `frames-invalid'. + # + send_gdb "continue\n" + gdb_expect { +- -re "\r\n\032\032post-prompt\r\nContinuing.\r\n\r\n\032\032starting\r\n\r\n\032\032frames-invalid\r\na.x is 1\r\n\r\n\032\032exited 0\r\n\r\nProgram exited normally.\r\n\r\n\032\032stopped\r\n$gdb_prompt$" \ ++ -re "\r\n\032\032post-prompt\r\nContinuing.\r\n\r\n\032\032starting\r\n(\r\n\032\032frames-invalid\r\n)*a.x is 1\r\n(\r\n\032\032frames-invalid\r\n)*\r\n\032\032exited 0\r\n\r\nProgram exited normally.\r\n\r\n\032\032stopped\r\n$gdb_prompt$" \ + { pass "continue until exit" } + -re ".*$gdb_prompt$" { fail "continue to exit" } + timeout { fail "continue to exit (timeout)" } +Index: gdb-6.8.50.20090216-cvs/gdb/testsuite/gdb.opt/inline-bt.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ gdb-6.8.50.20090216-cvs/gdb/testsuite/gdb.opt/inline-bt.c 2009-03-02 11:38:29.000000000 +0100 +@@ -0,0 +1,47 @@ ++/* Copyright (C) 2008 Free Software Foundation, Inc. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++int x, y; ++volatile int result; ++ ++void bar(void); ++ ++inline int func1(void) ++{ ++ bar (); ++ return x * y; ++} ++ ++inline int func2(void) ++{ ++ return x * func1 (); ++} ++ ++int main (void) ++{ ++ int val; ++ ++ x = 7; ++ y = 8; ++ bar (); ++ ++ val = func1 (); ++ result = val; ++ ++ val = func2 (); ++ result = val; ++ ++ return 0; ++} +Index: gdb-6.8.50.20090216-cvs/gdb/testsuite/gdb.opt/inline-bt.exp +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ gdb-6.8.50.20090216-cvs/gdb/testsuite/gdb.opt/inline-bt.exp 2009-03-02 11:38:29.000000000 +0100 +@@ -0,0 +1,63 @@ ++# Copyright 2008 Free Software Foundation, Inc. ++ ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation; either version 3 of the License, or ++# (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see . ++ ++set testfile "inline-bt" ++set srcfile ${testfile}.c ++set srcfile2 "inline-markers.c" ++set fullsrcfile "${srcdir}/${subdir}/${srcfile}" ++set fullsrcfile2 "${srcdir}/${subdir}/${srcfile2}" ++set sources [list ${fullsrcfile} ${fullsrcfile2}] ++set binfile ${objdir}/${subdir}/${testfile} ++ ++if { [gdb_compile ${sources} ${binfile} \ ++ executable {debug optimize=-O2}] != "" } { ++ untested inline-bt.exp ++ return -1 ++} ++ ++gdb_exit ++gdb_start ++gdb_reinitialize_dir $srcdir/$subdir ++gdb_load ${binfile} ++ ++runto_main ++ ++get_compiler_info $binfile ++get_debug_format ++if { [skip_inline_frame_tests] } { ++ untested inline-bt.exp ++ return ++} ++ ++set line1 [gdb_get_line_number "set breakpoint 1 here" ${fullsrcfile2}] ++gdb_breakpoint $srcfile2:$line1 ++ ++gdb_test "continue" ".*set breakpoint 1 here.*" "continue to bar (1)" ++gdb_test "backtrace" "#0 bar.*#1 .*main.*" "backtrace from bar (1)" ++gdb_test "info frame" ".*called by frame.*" "bar not inlined" ++ ++gdb_test "continue" ".*set breakpoint 1 here.*" "continue to bar (2)" ++gdb_test "backtrace" "#0 bar.*#1 .*func1.*#2 .*main.*" \ ++ "backtrace from bar (2)" ++gdb_test "up" "#1 .*func1.*" "up from bar (2)" ++gdb_test "info frame" ".*inlined into frame.*" "func1 inlined (2)" ++ ++gdb_test "continue" ".*set breakpoint 1 here.*" "continue to bar (3)" ++gdb_test "backtrace" "#0 bar.*#1 .*func1.*#2 .*func2.*#3 .*main.*" \ ++ "backtrace from bar (3)" ++gdb_test "up" "#1 .*func1.*" "up from bar (3)" ++gdb_test "info frame" ".*inlined into frame.*" "func1 inlined (3)" ++gdb_test "up" "#2 .*func2.*" "up from func1 (3)" ++gdb_test "info frame" ".*inlined into frame.*" "func2 inlined (3)" +Index: gdb-6.8.50.20090216-cvs/gdb/testsuite/gdb.opt/inline-cmds.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ gdb-6.8.50.20090216-cvs/gdb/testsuite/gdb.opt/inline-cmds.c 2009-03-02 11:38:29.000000000 +0100 +@@ -0,0 +1,85 @@ ++/* Copyright (C) 2008 Free Software Foundation, Inc. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++int x, y; ++volatile int result; ++ ++void bar(void); ++void marker(void); ++void noinline(void); ++ ++inline int func1(void) ++{ ++ bar (); ++ return x * y; ++} ++ ++inline int func2(void) ++{ ++ return x * func1 (); ++} ++ ++inline void func3(void) ++{ ++ bar (); ++} ++ ++inline void outer_inline1(void) ++{ ++ noinline (); ++} ++ ++inline void outer_inline2(void) ++{ ++ outer_inline1 (); ++} ++ ++int main (void) ++{ /* start of main */ ++ int val; ++ ++ x = 7; ++ y = 8; ++ ++ result = func1 (); ++ result = func2 (); ++ marker (); ++ ++ result = 0; ++ result = 0; /* set breakpoint 3 here */ ++ ++ func1 (); /* first call */ ++ func1 (); /* second call */ ++ marker (); ++ ++ result = 0; ++ result = 0; /* set breakpoint 4 here */ ++ ++ func1 (); ++ func3 (); ++ marker (); ++ ++ result = 0; ++ result = 0; /* set breakpoint 5 here */ ++ ++ marker (); ++ func1 (); ++ func3 (); ++ marker (); /* set breakpoint 6 here */ ++ ++ outer_inline2 (); ++ ++ return 0; ++} +Index: gdb-6.8.50.20090216-cvs/gdb/testsuite/gdb.opt/inline-cmds.exp +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ gdb-6.8.50.20090216-cvs/gdb/testsuite/gdb.opt/inline-cmds.exp 2009-03-02 11:38:29.000000000 +0100 +@@ -0,0 +1,279 @@ ++# Copyright 2008 Free Software Foundation, Inc. ++ ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation; either version 3 of the License, or ++# (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see . ++ ++set testfile "inline-cmds" ++set srcfile "${testfile}.c" ++set srcfile2 "inline-markers.c" ++set fullsrcfile "${srcdir}/${subdir}/${srcfile}" ++set fullsrcfile2 "${srcdir}/${subdir}/${srcfile2}" ++set sources [list ${fullsrcfile} ${fullsrcfile2}] ++set binfile ${objdir}/${subdir}/${testfile} ++ ++if { [gdb_compile $sources ${binfile} \ ++ executable {debug optimize=-O2}] != "" } { ++ untested inline-cmds.exp ++ return -1 ++} ++ ++gdb_exit ++gdb_start ++gdb_reinitialize_dir $srcdir/$subdir ++gdb_load ${binfile} ++ ++gdb_test "set listsize 1" "" ++ ++runto_main ++ ++get_compiler_info $binfile ++get_debug_format ++if { [skip_inline_frame_tests] } { ++ untested inline-cmds.exp ++ return ++} ++ ++# First, check that the things we expected to be inlined really were, ++# and those that shouldn't be weren't. ++set line1 [gdb_get_line_number "set breakpoint 1 here" ${fullsrcfile2}] ++gdb_breakpoint $srcfile2:$line1 ++set line2 [gdb_get_line_number "set breakpoint 2 here" ${fullsrcfile2}] ++gdb_breakpoint $srcfile2:$line2 ++ ++gdb_test "continue" ".*set breakpoint 1 here.*" "continue to bar (1)" ++gdb_test "backtrace" "#0 bar.*#1 .*func1.*#2 .*main.*" \ ++ "backtrace from bar (1)" ++gdb_test "up" "#1 .*func1.*" "up from bar (1)" ++gdb_test "info frame" ".*inlined into frame.*" "func1 inlined (1)" ++ ++gdb_test "continue" ".*set breakpoint 1 here.*" "continue to bar (2)" ++gdb_test "backtrace" "#0 bar.*#1 .*func1.*#2 .*func2.*#3 .*main.*" \ ++ "backtrace from bar (2)" ++gdb_test "up" "#1 .*func1.*" "up from bar (2)" ++gdb_test "info frame" ".*inlined into frame.*" "func1 inlined (2)" ++gdb_test "up" "#2 .*func2.*" "up from func1 (2)" ++gdb_test "info frame" ".*inlined into frame.*" "func2 inlined (2)" ++ ++gdb_test "continue" ".*set breakpoint 2 here.*" "continue to marker" ++gdb_test "backtrace" "#0 marker.*#1 .*main.*" "backtrace from marker" ++gdb_test "info frame" ".*called by frame.*" "marker not inlined" ++ ++# Next, check that we can next over inlined functions. We should not end up ++# inside any of them. ++delete_breakpoints ++runto_main ++ ++# The lines before the first inlined call. ++set first "x = 7|y = 8" ++ ++# Some extra lines that end up in our stepping due to code motion. ++set opt "start of main|result = 0" ++ ++# We start this test with a "list" instead of a "next", in case the ++# first non-prologue instruction in main comes from the inlined function. ++set msg "next over inlined functions" ++gdb_test_multiple "list" $msg { ++ -re "($first|result = func1|result = func2|$opt).*$gdb_prompt $" { ++ send_gdb "next\r" ++ exp_continue ++ } ++ -re "marker \\\(\\\);\r\n$gdb_prompt $" { ++ pass $msg ++ } ++} ++ ++# Check that when next shows the call of func1, it has not happened yet. ++runto_main ++ ++# Like the return value of gdb_test: -1 something is wrong, 0 passed, 1 failed. ++set bt_test -1 ++set x_test -1 ++ ++set msg "next past inlined func1" ++gdb_test_multiple "list" $msg { ++ -re "($first|$opt).*$gdb_prompt $" { ++ send_gdb "next\r" ++ exp_continue ++ } ++ -re "result = func1 \\\(\\\);\r\n$gdb_prompt $" { ++ # Check whether x has been set. If 0, we may be doing something ++ # else associated with this line besides the inlined call - e.g. ++ # loading the address of result. If 7, we may be at the call site. ++ # If 15, though, we are past the call and back at the store to ++ # result - that's bad, we should have stepped out of func1 and ++ # kept stepping until the line changed. ++ set x_val -1 ++ gdb_test_multiple "print x" "" { ++ -re "\\\$$decimal = (\[0-9\]*)\r\n$gdb_prompt $" { ++ set x_val $expect_out(1,string) ++ } ++ -re "$gdb_prompt $" { } ++ } ++ if { $x_val == 0 || $x_val == 7 } { ++ if { $x_test != 1 } { ++ set x_test 0 ++ } ++ } else { ++ set x_test 1 ++ } ++ ++ # func1 should not show up on backtraces yet. ++ if { $bt_test != 1 } { ++ set bt_test [gdb_test "backtrace" "#0 \[^#]*main.*" ""] ++ } ++ ++ send_gdb "next\r" ++ exp_continue ++ } ++ ++ -re "result = func2 \\\(\\\);\r\n$gdb_prompt $" { ++ pass $msg ++ } ++} ++ ++if { $x_test == 0 } { ++ pass "print x before func1" ++} else { ++ fail "print x before func1" ++} ++ ++if { $bt_test == 0 } { ++ pass "backtrace does not include func1" ++} else { ++ fail "backtrace does not include func1" ++} ++ ++# Next, check that we can single step into inlined functions. We should always ++# "stop" at the call sites before entering them. ++runto_main ++ ++set msg "step into func1" ++set saw_call_site 0 ++gdb_test_multiple "list" $msg { ++ -re "($first|$opt).*$gdb_prompt $" { ++ send_gdb "step\r" ++ exp_continue ++ } ++ -re "result = func1.*$gdb_prompt $" { ++ set saw_call_site 1 ++ send_gdb "step\r" ++ exp_continue ++ } ++ -re "func1 \\\(\\\) at .*\r\n$decimal.*bar \\\(\\\);\r\n$gdb_prompt $" { ++ if { $saw_call_site } { ++ pass $msg ++ } else { ++ fail $msg ++ } ++ } ++} ++ ++# Check finish out of an inlined function. ++set msg "finish from func1" ++gdb_test_multiple "finish" $msg { ++ -re "result = func1 \\\(\\\);\r\n$gdb_prompt $" { ++ pass $msg ++ } ++ -re "($first|$opt).*$gdb_prompt $" { ++ # Whoops. We finished, but ended up back at an earlier line. Keep ++ # trying. ++ send_gdb "step\r" ++ exp_continue ++ } ++ -re "func1 \\\(\\\) at .*\r\n$decimal.*bar \\\(\\\);\r\n$gdb_prompt $" { ++ send_gdb "finish\r" ++ exp_continue ++ } ++} ++ ++# Test some corner cases involving consecutive inlined functions. ++set line3 [gdb_get_line_number "set breakpoint 3 here"] ++gdb_breakpoint $line3 ++gdb_continue_to_breakpoint "consecutive func1" ++ ++gdb_test "next" ".*func1 .*first call.*" "next to first func1" ++set msg "next to second func1" ++gdb_test_multiple "next" $msg { ++ -re ".*func1 .*second call.*$gdb_prompt $" { ++ pass $msg ++ } ++ -re ".*marker .*$gdb_prompt $" { ++ # This assembles to two consecutive call instructions. ++ # Both appear to be at the same line, because they're ++ # in the body of the same inlined function. This is ++ # reasonable for the line table. GDB should take the ++ # containing block and/or function into account when ++ # deciding how far to step. The single line table entry ++ # is actually two consecutive instances of the same line. ++ kfail gdb/NNNN $msg ++ } ++} ++ ++# It is easier when the two inlined functions are not on the same line. ++set line4 [gdb_get_line_number "set breakpoint 4 here"] ++gdb_breakpoint $line4 ++gdb_continue_to_breakpoint "func1 then func3" ++ ++gdb_test "next" ".*func1 \\\(\\\);" "next to func1 before func3" ++gdb_test "next" ".*func3 \\\(\\\);" "next to func3" ++ ++# Test finishing out of one thing and into another. ++set line5 [gdb_get_line_number "set breakpoint 5 here"] ++gdb_breakpoint $line5 ++gdb_continue_to_breakpoint "finish into func1" ++ ++gdb_test "next" ".*marker \\\(\\\);" "next to finish marker" ++gdb_test "step" ".*set breakpoint 2 here.*" "step into finish marker" ++gdb_test "finish" "func1 \\\(\\\);" "finish from marker to func1" ++ ++gdb_test "step" "bar \\\(\\\);" "step into func1 for finish" ++gdb_test "finish" "func3 \\\(\\\);" "finish from func1 to func3" ++ ++# Test a deeper call stack. ++set line6 [gdb_get_line_number "set breakpoint 6 here"] ++gdb_breakpoint $line6 ++gdb_continue_to_breakpoint "before the outer_inline call" ++gdb_test "step" "marker \\\(\\\) at .*" "reach 1 the outer_inline call" ++gdb_test "finish" "main \\\(\\\) at .*outer_inline2 \\\(\\\);" "reach outer_inline2" ++gdb_test "bt" "#0 main.*" "backtrace at main of outer_inline" ++gdb_test "step" "outer_inline2 \\\(\\\) at .*" "enter outer_inline2" ++gdb_test "bt" "#0 outer_inline2.*#1 main.*" "backtrace at outer_inline2" ++gdb_test "step" "outer_inline1 \\\(\\\) at .*" "enter outer_inline1 from outer_inline2" ++ ++set msg "backtrace at outer_inline1" ++gdb_test_multiple "bt" $msg { ++ -re "#0 outer_inline1.*#1 outer_inline2.*#2 main.*$gdb_prompt $" { ++ pass $msg ++ } ++ -re "#0 $hex in outer_inline1.*#1 outer_inline2.*#2 main.*$gdb_prompt $" { ++ # Binutils PR gas/6717. Gas moves .loc past .p2align and the ++ # leading nop of the inlined call appears to be on the same line ++ # as main's call to marker. ++ xfail $msg ++ gdb_test "step" "noinline \\\(\\\);" "step to call of noinline" ++ } ++} ++ ++gdb_test "step" "noinline \\\(\\\) at .*" "enter noinline from outer_inline1" ++gdb_test "bt" "#0 noinline.*#1 .*outer_inline1.*#2 .*outer_inline2.*#3 main.*" "backtrace at noinline from outer_inline1" ++gdb_test "step" "inlined_fn \\\(\\\) at .*" "enter inlined_fn from noinline" ++gdb_test "bt" "#0 inlined_fn.*#1 noinline.*#2 .*outer_inline1.*#3 .*outer_inline2.*#4 main.*" "backtrace at inlined_fn from noinline" ++gdb_test "info frame" ".*inlined into frame.*" "inlined_fn from noinline inlined" ++gdb_test "up" "#1 noinline.*" "up to noinline" ++gdb_test "info frame" ".*\n called by frame.*" "noinline from outer_inline1 not inlined" ++gdb_test "up" "#2 .*outer_inline1.*" "up to outer_inline1" ++gdb_test "info frame" ".*inlined into frame.*" "outer_inline1 inlined" ++gdb_test "up" "#3 .*outer_inline2.*" "up to outer_inline2" ++gdb_test "info frame" ".*inlined into frame.*" "outer_inline2 inlined" ++gdb_test "up" "#4 main.*" "up from outer_inline2" ++gdb_test "info frame" ".*\n caller of frame.*" "main not inlined" +Index: gdb-6.8.50.20090216-cvs/gdb/testsuite/gdb.opt/inline-locals.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ gdb-6.8.50.20090216-cvs/gdb/testsuite/gdb.opt/inline-locals.c 2009-03-02 11:38:29.000000000 +0100 +@@ -0,0 +1,52 @@ ++/* Copyright (C) 2008 Free Software Foundation, Inc. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++int x, y; ++volatile int result; ++volatile int *array_p; ++ ++void bar(void); ++ ++inline int func1(int arg1) ++{ ++ int array[64]; ++ array_p = array; ++ array[0] = result; ++ array[1] = arg1; ++ bar (); ++ return x * y + array_p[0] * arg1; ++} ++ ++inline int func2(int arg2) ++{ ++ return x * func1 (arg2); ++} ++ ++int main (void) ++{ ++ int val; ++ ++ x = 7; ++ y = 8; ++ bar (); ++ ++ val = func1 (result); ++ result = val; ++ ++ val = func2 (result); ++ result = val; ++ ++ return 0; ++} +Index: gdb-6.8.50.20090216-cvs/gdb/testsuite/gdb.opt/inline-locals.exp +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ gdb-6.8.50.20090216-cvs/gdb/testsuite/gdb.opt/inline-locals.exp 2009-03-02 11:38:29.000000000 +0100 +@@ -0,0 +1,118 @@ ++# Copyright 2008 Free Software Foundation, Inc. ++ ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation; either version 3 of the License, or ++# (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see . ++ ++set testfile "inline-locals" ++set srcfile ${testfile}.c ++set srcfile2 "inline-markers.c" ++set fullsrcfile "${srcdir}/${subdir}/${srcfile}" ++set fullsrcfile2 "${srcdir}/${subdir}/${srcfile2}" ++set sources [list ${fullsrcfile} ${fullsrcfile2}] ++set binfile ${objdir}/${subdir}/${testfile} ++ ++if { [gdb_compile ${sources} ${binfile} \ ++ executable {debug optimize=-O2}] != "" } { ++ untested inline-locals.exp ++ return -1 ++} ++ ++gdb_exit ++gdb_start ++gdb_reinitialize_dir $srcdir/$subdir ++gdb_load ${binfile} ++ ++runto_main ++ ++get_compiler_info $binfile ++get_debug_format ++if { [skip_inline_var_tests] } { ++ untested inline-bt.exp ++ return ++} ++ ++set no_frames [skip_inline_frame_tests] ++ ++set line1 [gdb_get_line_number "set breakpoint 1 here" ${fullsrcfile2}] ++gdb_breakpoint $srcfile2:$line1 ++ ++gdb_test "continue" ".*set breakpoint 1 here.*" "continue to bar (1)" ++ ++gdb_test "continue" ".*set breakpoint 1 here.*" "continue to bar (2)" ++ ++if { ! $no_frames } { ++ gdb_test "backtrace" "#0 bar.*#1 .*func1.*#2 .*main.*" \ ++ "backtrace from bar (2)" ++ gdb_test "up" "#1 .*func1 .* at .*" "up from bar (2)" ++ gdb_test "info frame" ".*inlined into frame.*" "func1 inlined (2)" ++ gdb_test "info locals" "array = {.*}" "info locals above bar (2)" ++ ++ set msg "info args above bar (2)" ++ gdb_test_multiple "info args" $msg { ++ -re "arg1 = $decimal\r\n$gdb_prompt $" { ++ pass $msg ++ } ++ -re "arg1 = \r\n$gdb_prompt $" { ++ # GCC 4.3 loses location information for arg1. GCC 4.2 is OK. ++ if { [test_compiler_info "gcc-4-3-*"] } { ++ setup_xfail *-*-* ++ } ++ fail $msg ++ } ++ } ++} else { ++ gdb_test "up" "#1 .*main .* at .*" "up from bar (2)" ++ gdb_test "info locals" ".*arg1 = 0.*" "info locals above bar (2)" ++} ++ ++# Make sure that locals on the stack are found. This is an array to ++# prevent it from living in a register. ++gdb_test "print array\[0\]" "\\\$$decimal = 0" "print local (2)" ++ ++if { ! $no_frames } { ++ # Verify that we do not print out variables from the inlined ++ # function's caller. ++ gdb_test "print val" "No symbol \"val\" in current context\\." \ ++ "print out of scope local" ++} ++ ++# Repeat the tests from a depth of two inlined functions, and with a ++# more interesting value in the local array. ++gdb_test "continue" ".*set breakpoint 1 here.*" "continue to bar (3)" ++if { ! $no_frames } { ++ gdb_test "backtrace" "#0 bar.*#1 .*func1.*#2 .*func2.*#3 .*main.*" \ ++ "backtrace from bar (3)" ++ gdb_test "up" "#1 .*func1 .* at .*" "up from bar (3)" ++ gdb_test "info frame" ".*inlined into frame.*" "func1 inlined (3)" ++ gdb_test "info locals" "array = {.*}" "info locals above bar (3)" ++ ++ set msg "info args above bar (3)" ++ gdb_test_multiple "info args" $msg { ++ -re "arg1 = $decimal\r\n$gdb_prompt $" { ++ pass $msg ++ } ++ -re "arg1 = \r\n$gdb_prompt $" { ++ # GCC 4.3 loses location information for arg1. GCC 4.2 is OK. ++ if { [test_compiler_info "gcc-4-3-*"] } { ++ setup_xfail *-*-* ++ } ++ fail $msg ++ } ++ } ++} else { ++ gdb_test "up" "#1 .*main .* at .*" "up from bar (3)" ++ gdb_test "info locals" ".*arg1 = 1.*" "info locals above bar (3a)" ++ gdb_test "info locals" ".*arg2 = 184.*" "info locals above bar (3b)" ++} ++ ++gdb_test "print array\[0\]" "\\\$$decimal = 184" "print local (3)" +Index: gdb-6.8.50.20090216-cvs/gdb/testsuite/gdb.opt/inline-markers.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ gdb-6.8.50.20090216-cvs/gdb/testsuite/gdb.opt/inline-markers.c 2009-03-02 11:38:29.000000000 +0100 +@@ -0,0 +1,36 @@ ++/* Copyright (C) 2008 Free Software Foundation, Inc. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++extern int x, y; ++ ++void bar(void) ++{ ++ x += y; /* set breakpoint 1 here */ ++} ++ ++void marker(void) ++{ ++ x += y; /* set breakpoint 2 here */ ++} ++ ++inline void inlined_fn(void) ++{ ++ x += y; ++} ++ ++void noinline(void) ++{ ++ inlined_fn (); /* inlined */ ++} +Index: gdb-6.8.50.20090216-cvs/gdb/testsuite/lib/gdb.exp +=================================================================== +--- gdb-6.8.50.20090216-cvs.orig/gdb/testsuite/lib/gdb.exp 2009-02-16 11:04:46.000000000 +0100 ++++ gdb-6.8.50.20090216-cvs/gdb/testsuite/lib/gdb.exp 2009-03-02 11:38:29.000000000 +0100 +@@ -1452,6 +1452,37 @@ proc skip_hp_tests {} { + return $skip_hp + } + ++# Return whether we should skip tests for showing inlined functions in ++# backtraces. Requires get_compiler_info and get_debug_format. ++ ++proc skip_inline_frame_tests {} { ++ # GDB only recognizes inlining information in DWARF 2 (DWARF 3). ++ if { ! [test_debug_format "DWARF 2"] } { ++ return 1 ++ } ++ ++ # GCC before 4.1 does not emit DW_AT_call_file / DW_AT_call_line. ++ if { ([test_compiler_info "gcc-2-*"] ++ || [test_compiler_info "gcc-3-*"] ++ || [test_compiler_info "gcc-4-0-*"]) } { ++ return 1 ++ } ++ ++ return 0 ++} ++ ++# Return whether we should skip tests for showing variables from ++# inlined functions. Requires get_compiler_info and get_debug_format. ++ ++proc skip_inline_var_tests {} { ++ # GDB only recognizes inlining information in DWARF 2 (DWARF 3). ++ if { ! [test_debug_format "DWARF 2"] } { ++ return 1 ++ } ++ ++ return 0 ++} ++ + set compiler_info "unknown" + set gcc_compiled 0 + set hp_cc_compiler 0 +Index: gdb-6.8.50.20090216-cvs/gdb/valops.c +=================================================================== +--- gdb-6.8.50.20090216-cvs.orig/gdb/valops.c 2009-02-09 10:56:04.000000000 +0100 ++++ gdb-6.8.50.20090216-cvs/gdb/valops.c 2009-03-02 11:38:29.000000000 +0100 +@@ -1009,7 +1009,7 @@ value_of_variable (struct symbol *var, s + frame = block_innermost_frame (b); + if (!frame) + { +- if (BLOCK_FUNCTION (b) ++ if (BLOCK_FUNCTION (b) && !block_inlined_p (b) + && SYMBOL_PRINT_NAME (BLOCK_FUNCTION (b))) + error (_("No frame is currently executing in block %s."), + SYMBOL_PRINT_NAME (BLOCK_FUNCTION (b))); +Index: gdb-6.8.50.20090216-cvs/gdb/Makefile.in +=================================================================== +--- gdb-6.8.50.20090216-cvs.orig/gdb/Makefile.in 2009-03-02 11:37:57.000000000 +0100 ++++ gdb-6.8.50.20090216-cvs/gdb/Makefile.in 2009-03-02 11:38:29.000000000 +0100 +@@ -634,6 +634,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 \ +@@ -804,6 +805,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $ + user-regs.o \ + frame.o frame-unwind.o doublest.o \ + frame-base.o \ ++ inline-frame.o \ + gnu-v2-abi.o gnu-v3-abi.o cp-abi.o cp-support.o \ + cp-namespace.o \ + reggroups.o regset.o \ +Index: gdb-6.8.50.20090216-cvs/gdb/testsuite/gdb.opt/Makefile.in +=================================================================== +--- gdb-6.8.50.20090216-cvs.orig/gdb/testsuite/gdb.opt/Makefile.in 2008-04-18 11:24:27.000000000 +0200 ++++ gdb-6.8.50.20090216-cvs/gdb/testsuite/gdb.opt/Makefile.in 2009-03-02 11:38:29.000000000 +0100 +@@ -1,7 +1,7 @@ + VPATH = @srcdir@ + srcdir = @srcdir@ + +-EXECUTABLES = hello/hello ++EXECUTABLES = clobbered-registers-O2 inline-bt inline-cmds inline-locals + + MISCELLANEOUS = + diff --git a/gdb.changes b/gdb.changes index 1ea08b9..f174c78 100644 --- a/gdb.changes +++ b/gdb.changes @@ -1,3 +1,8 @@ +------------------------------------------------------------------- +Mon Mar 2 11:42:03 CET 2009 - rguenther@suse.de + +- Integrate support for transparently debugging inline functions. + ------------------------------------------------------------------- Mon Feb 16 11:40:00 CET 2009 - schwab@suse.de diff --git a/gdb.spec b/gdb.spec index c7e9200..b22b085 100644 --- a/gdb.spec +++ b/gdb.spec @@ -20,7 +20,7 @@ Name: gdb Summary: The GNU Debugger Version: 6.8.50.20090216 -Release: 1 +Release: 2 BuildRequires: bison dejagnu flex gcc-c++ gcc-fortran gcc-java gcc-objc python-devel readline-devel zlib-devel %if 0%{?suse_version} > 1020 BuildRequires: libexpat-devel @@ -43,6 +43,7 @@ Obsoletes: gdb-64bit PreReq: %{install_info_prereq} Recommends: python %define sss -cvs +BuildRoot: %{_tmppath}/%{name}-%{version}-build Source: gdb-%{version}%{sss}.tar.bz2 Patch: cell-combined.diff Patch1: find-pc-sect-line.diff @@ -54,7 +55,11 @@ Patch7: pie-relocate.diff Patch8: find-separate-debug-file.diff Patch9: fix-gdb-backtrace.diff Patch10: expand-line-sal-maybe.diff -BuildRoot: %{_tmppath}/%{name}-%{version}-build +# Patches from Fedora +# Support transparent debugging of inlined functions for an optimized code. +Patch327: gdb-6.8-inlining.patch +Patch350: gdb-6.8-inlining-addon.patch +Patch328: gdb-6.8-inlining-by-name.patch %description You can use GDB to debug programs written in C, C++, Ada, and Modula-2. @@ -123,6 +128,9 @@ Authors: %patch8 %patch9 %patch10 +%patch327 -p1 +%patch350 -p1 +%patch328 -p1 %build %ifarch %ix86 ia64 ppc ppc64 s390 s390x x86_64 @@ -199,6 +207,8 @@ rm -rf $RPM_BUILD_ROOT %endif %changelog +* Mon Mar 02 2009 rguenther@suse.de +- Integrate support for transparently debugging inline functions. * Mon Feb 16 2009 schwab@suse.de - Update to head to trunk. * Tue Feb 03 2009 schwab@suse.de