diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index abce84d..6a2a1f1 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,18 @@
 2019-11-12  Andrew Burgess  <andrew.burgess@embecosm.com>
 
+	* frame.c (find_frame_sal): Move call to get_next_frame into more
+	inner scope.
+	* inline-frame.c (inilne_state) <inline_state>: Update argument
+	types.
+	(inilne_state) <skipped_symbol>: Rename to...
+	(inilne_state) <skipped_symbols>: ...this, and change to a vector.
+	(skip_inline_frames): Build vector of skipped symbols and use this
+	to reate the inline_state.
+	(inline_skipped_symbol): Add a comment and some assertions, fetch
+	skipped symbol from the list.
+
+2019-11-12  Andrew Burgess  <andrew.burgess@embecosm.com>
+
 	* buildsym.c (lte_is_less_than): Delete.
 	(buildsym_compunit::end_symtab_with_blockvector): Create local
 	lambda function to sort line table entries, and use
diff --git a/gdb/frame.c b/gdb/frame.c
index 3ebdc99..dcf2ee2 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -2508,14 +2508,15 @@
   int notcurrent;
   CORE_ADDR pc;
 
-  /* 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 the current frame has some inlined callees, and we have a next
+	 frame, then that frame must be an inlined frame.  In this case
+	 this frame's sal is the "call site" of the next frame's inlined
+	 function, which can not be inferred from get_frame_pc.  */
+      next_frame = get_next_frame (frame);
       if (next_frame)
 	sym = get_frame_function (next_frame);
       else
diff --git a/gdb/inline-frame.c b/gdb/inline-frame.c
index 5840e3e..db8d703 100644
--- a/gdb/inline-frame.c
+++ b/gdb/inline-frame.c
@@ -37,9 +37,9 @@
 struct inline_state
 {
   inline_state (thread_info *thread_, int skipped_frames_, CORE_ADDR saved_pc_,
-		symbol *skipped_symbol_)
+		std::vector<symbol *> &&skipped_symbols_)
     : thread (thread_), skipped_frames (skipped_frames_), saved_pc (saved_pc_),
-      skipped_symbol (skipped_symbol_)
+      skipped_symbols (std::move (skipped_symbols_))
   {}
 
   /* The thread this data relates to.  It should be a currently
@@ -56,10 +56,10 @@
      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;
+  /* Only valid if SKIPPED_FRAMES is non-zero.  This is the list of all
+     function symbols that have been skipped, from inner most to outer
+     most.  It is used to find the call site of the current frame.  */
+  std::vector<struct symbol *> skipped_symbols;
 };
 
 static std::vector<inline_state> inline_states;
@@ -324,7 +324,7 @@
 skip_inline_frames (thread_info *thread, bpstat stop_chain)
 {
   const struct block *frame_block, *cur_block;
-  struct symbol *last_sym = NULL;
+  std::vector<struct symbol *> skipped_syms;
   int skip_count = 0;
 
   /* This function is called right after reinitializing the frame
@@ -352,7 +352,7 @@
 		    break;
 
 		  skip_count++;
-		  last_sym = BLOCK_FUNCTION (cur_block);
+		  skipped_syms.push_back (BLOCK_FUNCTION (cur_block));
 		}
 	      else
 		break;
@@ -365,7 +365,8 @@
     }
 
   gdb_assert (find_inline_frame_state (thread) == NULL);
-  inline_states.emplace_back (thread, skip_count, this_pc, last_sym);
+  inline_states.emplace_back (thread, skip_count, this_pc,
+			      std::move (skipped_syms));
 
   if (skip_count != 0)
     reinit_frame_cache ();
@@ -404,9 +405,16 @@
 inline_skipped_symbol (thread_info *thread)
 {
   inline_state *state = find_inline_frame_state (thread);
-
   gdb_assert (state != NULL);
-  return state->skipped_symbol;
+
+  /* This should only be called when we are skipping at least one frame,
+     hence SKIPPED_FRAMES will be greater than zero when we get here.
+     We initialise SKIPPED_FRAMES at the same time as we build
+     SKIPPED_SYMBOLS, hence it should be true that SKIPPED_FRAMES never
+     indexes outside of the SKIPPED_SYMBOLS vector.  */
+  gdb_assert (state->skipped_frames > 0);
+  gdb_assert (state->skipped_frames <= state->skipped_symbols.size ());
+  return state->skipped_symbols[state->skipped_frames - 1];
 }
 
 /* Return the number of functions inlined into THIS_FRAME.  Some of
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index 18ae148..6afb187 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,5 +1,10 @@
 2019-11-12  Andrew Burgess  <andrew.burgess@embecosm.com>
 
+	* gdb.dwarf2/dw2-inline-many-frames.c: New file.
+	* gdb.dwarf2/dw2-inline-many-frames.exp: New file.
+
+2019-11-12  Andrew Burgess  <andrew.burgess@embecosm.com>
+
 	* gdb.dwarf2/dw2-inline-stepping.c: New file.
 	* gdb.dwarf2/dw2-inline-stepping.exp: New file.
 
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.c
new file mode 100644
index 0000000..16493e1
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.c
@@ -0,0 +1,158 @@
+/* Copyright 2019 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* This test sets up a call stack that looks like this:
+
+   #11     #10    #9     #8     #7     #6     #5     #4     #3     #2     #1     #0
+   main -> aaa -> bbb -> ccc -> ddd -> eee -> fff -> ggg -> hhh -> iii -> jjj -> kkk
+   \_______________________/    \________/    \______________________/    \________/
+      Inline sequence #1          Normal          Inline sequence #2        Normal
+
+   We use the 'start' command to move into main, after that we 'step'
+   through each function until we are in kkk.  We then use the 'up' command
+   to look back at each from to main.
+
+   The test checks that we can handle and step through sequences of more
+   than one inline frame (so 'main .... ccc', and 'fff .... iii'), and also
+   that we can move around in a stack that contains more than one disjoint
+   sequence of inline frames.
+
+   The order of the functions in this file is deliberately mixed up so that
+   the line numbers are not "all ascending" or "all descending" in the line
+   table.  */
+
+#define INLINE_FUNCTION __attribute__ ((always_inline)) static inline
+#define NON_INLINE_FUNCTION __attribute__ ((noinline))
+
+volatile int global_var = 0;
+
+INLINE_FUNCTION int aaa ();
+INLINE_FUNCTION int bbb ();
+INLINE_FUNCTION int ccc ();
+
+NON_INLINE_FUNCTION int ddd ();
+NON_INLINE_FUNCTION int eee ();
+NON_INLINE_FUNCTION int fff ();
+
+INLINE_FUNCTION int ggg ();
+INLINE_FUNCTION int hhh ();
+INLINE_FUNCTION int iii ();
+
+NON_INLINE_FUNCTION int jjj ();
+NON_INLINE_FUNCTION int kkk ();
+
+INLINE_FUNCTION int
+aaa ()
+{						/* aaa prologue */
+  asm ("aaa_label: .globl aaa_label");
+  return bbb () + 1;				/* aaa return */
+}						/* aaa end */
+
+NON_INLINE_FUNCTION int
+jjj ()
+{						/* jjj prologue */
+  int ans;
+  asm ("jjj_label: .globl jjj_label");
+  ans = kkk () + 1;				/* jjj return */
+  asm ("jjj_label2: .globl jjj_label2");
+  return ans;
+}						/* jjj end */
+
+INLINE_FUNCTION int
+ggg ()
+{						/* ggg prologue */
+  asm ("ggg_label: .globl ggg_label");
+  return hhh () + 1;				/* ggg return */
+}						/* ggg end */
+
+INLINE_FUNCTION int
+ccc ()
+{						/* ccc prologue */
+  asm ("ccc_label: .globl ccc_label");
+  return ddd () + 1;				/* ccc return */
+}						/* ccc end */
+
+NON_INLINE_FUNCTION int
+fff ()
+{						/* fff prologue */
+  int ans;
+  asm ("fff_label: .globl fff_label");
+  ans = ggg () + 1;				/* fff return */
+  asm ("fff_label2: .globl fff_label2");
+  return ans;
+}						/* fff end */
+
+NON_INLINE_FUNCTION int
+kkk ()
+{						/* kkk prologue */
+  asm ("kkk_label: .globl kkk_label");
+  return global_var;				/* kkk return */
+}						/* kkk end */
+
+INLINE_FUNCTION int
+bbb ()
+{						/* bbb prologue */
+  asm ("bbb_label: .globl bbb_label");
+  return ccc () + 1;				/* bbb return */
+}						/* bbb end */
+
+INLINE_FUNCTION int
+hhh ()
+{						/* hhh prologue */
+  asm ("hh_label: .globl hhh_label");
+  return iii () + 1;				/* hhh return */
+}						/* hhh end */
+
+int
+main ()
+{						/* main prologue */
+  int ans;
+  asm ("main_label: .globl main_label");
+  global_var = 0;				/* main set global_var */
+  asm ("main_label2: .globl main_label2");
+  ans = aaa () + 1;				/* main call aaa */
+  asm ("main_label3: .globl main_label3");
+  return ans;
+}						/* main end */
+
+NON_INLINE_FUNCTION int
+ddd ()
+{						/* ddd prologue */
+  int ans;
+  asm ("ddd_label: .globl ddd_label");
+  ans =  eee () + 1;				/* ddd return */
+  asm ("ddd_label2: .globl ddd_label2");
+  return ans;
+}						/* ddd end */
+
+INLINE_FUNCTION int
+iii ()
+{						/* iii prologue */
+  int ans;
+  asm ("iii_label: .globl iii_label");
+  ans = jjj () + 1;				/* iii return */
+  asm ("iii_label2: .globl iii_label2");
+  return ans;
+}						/* iii end */
+
+NON_INLINE_FUNCTION int
+eee ()
+{						/* eee prologue */
+  int ans;
+  asm ("eee_label: .globl eee_label");
+  ans = fff () + 1;				/* eee return */
+  asm ("eee_label2: .globl eee_label2");
+  return ans;
+}						/* eee end */
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.exp
new file mode 100644
index 0000000..bc93b3b
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.exp
@@ -0,0 +1,379 @@
+# Copyright 2019 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This test shows the importance of not corrupting the order of line
+# table information.  When multiple lines are given for the same
+# address the compiler usually lists these in the order in which we
+# would expect to encounter them.  When stepping through nested inline
+# frames the last line given for an address is assumed by GDB to be
+# the most inner frame, and this is what GDB displays.
+#
+# If we corrupt the order of the line table entries then GDB will
+# display the wrong line as being the inner most frame.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+# The .c files use __attribute__.
+if [get_compiler_info] {
+    return -1
+}
+if !$gcc_compiled {
+    return 0
+}
+
+standard_testfile dw2-inline-many-frames.c dw2-inline-many-frames.S
+
+# Extract the start, length, and end for function called NAME and
+# create suitable variables in the callers scope.
+proc get_func_info { name } {
+    global srcdir subdir srcfile
+
+    upvar 1 "${name}_start" func_start
+    upvar 1 "${name}_len" func_len
+    upvar 1 "${name}_end" func_end
+
+    lassign [function_range ${name} [list ${srcdir}/${subdir}/$srcfile]] \
+	func_start func_len
+    set func_end "$func_start + $func_len"
+}
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global srcdir subdir srcfile srcfile2
+    declare_labels ranges_label lines_label
+    declare_labels aaa_label bbb_label ccc_label
+    declare_labels ggg_label hhh_label iii_label
+
+    get_func_info main
+    get_func_info ddd
+    get_func_info eee
+    get_func_info fff
+    get_func_info jjj
+    get_func_info kkk
+
+    set call_in_main [gdb_get_line_number "main call aaa"]
+    set call_in_aaa [gdb_get_line_number "aaa return"]
+    set call_in_bbb [gdb_get_line_number "bbb return"]
+    set call_in_ccc [gdb_get_line_number "ccc return"]
+    set call_in_fff [gdb_get_line_number "fff return"]
+    set call_in_ggg [gdb_get_line_number "ggg return"]
+    set call_in_hhh [gdb_get_line_number "hhh return"]
+    set call_in_iii [gdb_get_line_number "iii return"]
+
+    cu {} {
+	compile_unit {
+	    {language @DW_LANG_C}
+	    {name dw2-inline-stepping.c}
+	    {low_pc 0 addr}
+	    {stmt_list ${lines_label} DW_FORM_sec_offset}
+	    {ranges ${ranges_label} DW_FORM_sec_offset}
+	} {
+	    subprogram {
+		{external 1 flag}
+		{name ddd}
+		{low_pc $ddd_start addr}
+		{high_pc "$ddd_start + $ddd_len" addr}
+	    }
+	    subprogram {
+		{external 1 flag}
+		{name eee}
+		{low_pc $eee_start addr}
+		{high_pc "$eee_start + $eee_len" addr}
+	    }
+	    subprogram {
+		{external 1 flag}
+		{name jjj}
+		{low_pc $jjj_start addr}
+		{high_pc "$jjj_start + $jjj_len" addr}
+	    }
+	    subprogram {
+		{external 1 flag}
+		{name kkk}
+		{low_pc $kkk_start addr}
+		{high_pc "$kkk_start + $kkk_len" addr}
+	    }
+	    aaa_label: subprogram {
+		{name aaa}
+		{inline 3 data1}
+	    }
+	    bbb_label: subprogram {
+		{name bbb}
+		{inline 3 data1}
+	    }
+	    ccc_label: subprogram {
+		{name ccc}
+		{inline 3 data1}
+	    }
+	    ggg_label: subprogram {
+		{name ggg}
+		{inline 3 data1}
+	    }
+	    hhh_label: subprogram {
+		{name hhh}
+		{inline 3 data1}
+	    }
+	    iii_label: subprogram {
+		{name iii}
+		{inline 3 data1}
+	    }
+	    subprogram {
+		{external 1 flag}
+		{name main}
+		{low_pc $main_start addr}
+		{high_pc "$main_start + $main_len" addr}
+	    } {
+		inlined_subroutine {
+		    {abstract_origin %$aaa_label}
+		    {low_pc main_label2 addr}
+		    {high_pc main_label3 addr}
+		    {call_file 1 data1}
+		    {call_line $call_in_main data1}
+		} {
+		    inlined_subroutine {
+			{abstract_origin %$bbb_label}
+			{low_pc main_label2 addr}
+			{high_pc main_label3 addr}
+			{call_file 1 data1}
+			{call_line $call_in_aaa data1}
+		    }  {
+			inlined_subroutine {
+			    {abstract_origin %$ccc_label}
+			    {low_pc main_label2 addr}
+			    {high_pc main_label3 addr}
+			    {call_file 1 data1}
+			    {call_line $call_in_bbb data1}
+			}
+		    }
+		}
+	    }
+	    subprogram {
+		{external 1 flag}
+		{name fff}
+		{low_pc $fff_start addr}
+		{high_pc "$fff_start + $fff_len" addr}
+	    }  {
+		inlined_subroutine {
+		    {abstract_origin %$ggg_label}
+		    {low_pc fff_label addr}
+		    {high_pc main_label2 addr}
+		    {call_file 1 data1}
+		    {call_line $call_in_fff data1}
+		} {
+		    inlined_subroutine {
+			{abstract_origin %$hhh_label}
+			{low_pc fff_label addr}
+			{high_pc fff_label2 addr}
+			{call_file 1 data1}
+			{call_line $call_in_ggg data1}
+		    }  {
+			inlined_subroutine {
+			    {abstract_origin %$iii_label}
+			    {low_pc fff_label addr}
+			    {high_pc fff_label2 addr}
+			    {call_file 1 data1}
+			    {call_line $call_in_hhh data1}
+			}
+		    }
+		}
+	    }
+	}
+    }
+
+    lines {version 2} lines_label {
+	include_dir "${srcdir}/${subdir}"
+	file_name "$srcfile" 1
+
+	program {
+	    {DW_LNE_set_address $main_start}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "main prologue"] - 1]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address main_label}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "main set global_var"] - [gdb_get_line_number "main prologue"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address main_label2}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "main call aaa"] - [gdb_get_line_number "main set global_var"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address main_label2}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "aaa return"] - [gdb_get_line_number "main call aaa"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address main_label2}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "bbb return"] - [gdb_get_line_number "aaa return"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address main_label2}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "ccc return"] - [gdb_get_line_number "bbb return"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address main_label3}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "main end"] - [gdb_get_line_number "ccc return"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address $main_end}
+	    {DW_LNE_end_sequence}
+
+	    {DW_LNE_set_address $ddd_start}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "ddd prologue"] - 1]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address ddd_label}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "ddd return"] - [gdb_get_line_number "ddd prologue"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address ddd_label2}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "ddd end"] - [gdb_get_line_number "ddd return"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address $ddd_end}
+	    {DW_LNE_end_sequence}
+
+	    {DW_LNE_set_address $eee_start}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "eee prologue"] - 1]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address eee_label}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "eee return"] - [gdb_get_line_number "eee prologue"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address eee_label2}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "eee end"] - [gdb_get_line_number "eee return"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address $eee_end}
+	    {DW_LNE_end_sequence}
+
+	    {DW_LNE_set_address $fff_start}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "fff prologue"] - 1]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address fff_label}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "fff return"] - [gdb_get_line_number "fff prologue"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address fff_label}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "ggg return"] - [gdb_get_line_number "fff return"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address fff_label}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "hhh return"] - [gdb_get_line_number "ggg return"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address fff_label}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "iii return"] - [gdb_get_line_number "hhh return"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address fff_label2}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "fff end"] - [gdb_get_line_number "fff return"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address $fff_end}
+	    {DW_LNE_end_sequence}
+
+	    {DW_LNE_set_address $jjj_start}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "jjj prologue"] - 1]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address jjj_label}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "jjj return"] - [gdb_get_line_number "jjj prologue"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address jjj_label2}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "jjj end"] - [gdb_get_line_number "jjj return"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address $jjj_end}
+	    {DW_LNE_end_sequence}
+
+	    {DW_LNE_set_address $kkk_start}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "kkk prologue"] - 1]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address kkk_label}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "kkk return"] - [gdb_get_line_number "kkk prologue"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address $kkk_end}
+	    {DW_LNE_end_sequence}
+	}
+    }
+
+    ranges {is_64 [is_64_target]} {
+	ranges_label: sequence {
+	    {range {${main_start}} ${main_end}}
+	    {range {${ddd_start}} ${ddd_end}}
+	    {range {${eee_start}} ${eee_end}}
+	    {range {${fff_start}} ${fff_end}}
+	    {range {${jjj_start}} ${jjj_end}}
+	    {range {${kkk_start}} ${kkk_end}}
+	}
+    }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	  [list $srcfile $asm_file] {nodebug}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+# First we step through all of the functions until we get the 'kkk'.
+set patterns [list "main call aaa" \
+		  "aaa return" \
+		  "bbb return" \
+		  "ccc return" \
+		  "ddd return" \
+		  "eee return" \
+		  "fff return" \
+		  "ggg return" \
+		  "hhh return" \
+		  "iii return" \
+		  "jjj return" \
+		  "kkk return" ]
+foreach p $patterns {
+    gdb_test "step" "/\\* $p \\*/" \
+	"step to '$p'"
+}
+
+# Now check the backtrace.
+set line_in_main [gdb_get_line_number "main call aaa"]
+set line_in_aaa [gdb_get_line_number "aaa return"]
+set line_in_bbb [gdb_get_line_number "bbb return"]
+set line_in_ccc [gdb_get_line_number "ccc return"]
+set line_in_ddd [gdb_get_line_number "ddd return"]
+set line_in_eee [gdb_get_line_number "eee return"]
+set line_in_fff [gdb_get_line_number "fff return"]
+set line_in_ggg [gdb_get_line_number "ggg return"]
+set line_in_hhh [gdb_get_line_number "hhh return"]
+set line_in_iii [gdb_get_line_number "iii return"]
+set line_in_jjj [gdb_get_line_number "jjj return"]
+set line_in_kkk [gdb_get_line_number "kkk return"]
+
+gdb_test "bt" [multi_line \
+		   "#0  kkk \\(\\) at \[^\r\n\]+${srcfile}:${line_in_kkk}" \
+		   "#1  $hex in jjj \\(\\) at \[^\r\n\]+${srcfile}:${line_in_jjj}" \
+		   "#2  $hex in iii \\(\\) at \[^\r\n\]+${srcfile}:${line_in_iii}" \
+		   "#3  hhh \\(\\) at \[^\r\n\]+${srcfile}:${line_in_hhh}" \
+		   "#4  ggg \\(\\) at \[^\r\n\]+${srcfile}:${line_in_ggg}" \
+		   "#5  fff \\(\\) at \[^\r\n\]+${srcfile}:${line_in_fff}" \
+		   "#6  $hex in eee \\(\\) at \[^\r\n\]+${srcfile}:${line_in_eee}" \
+		   "#7  $hex in ddd \\(\\) at \[^\r\n\]+${srcfile}:${line_in_ddd}" \
+		   "#8  $hex in ccc \\(\\) at \[^\r\n\]+${srcfile}:${line_in_ccc}" \
+		   "#9  bbb \\(\\) at \[^\r\n\]+${srcfile}:${line_in_bbb}" \
+		   "#10 aaa \\(\\) at \[^\r\n\]+${srcfile}:${line_in_aaa}" \
+		   "#11 main \\(\\) at \[^\r\n\]+${srcfile}:${line_in_main}" ]
+
+# Now check we can use 'up' to inspect each frame correctly.
+set patterns [list  \
+		  "jjj return" \
+		  "iii return" \
+		  "hhh return" \
+		  "ggg return" \
+		  "fff return" \
+		  "eee return" \
+		  "ddd return" \
+		  "ccc return" \
+		  "bbb return" \
+		  "aaa return" \
+		  "main call aaa" ]
+foreach p $patterns {
+    gdb_test "up" "/\\* $p \\*/" \
+	"up to '$p'"
+}
