This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[patch 04/12] entryval#3: Virtual tail call frames


Hi,

[patch 04/12+doc] entryval#2: Virtual tail call frames
http://sourceware.org/ml/gdb-patches/2011-09/msg00226.html

doc updates by Eli, new examples in the doc, more output on `set debug
entry-values', move the debugging output to earlier patch part.


Thanks,
Jan


gdb/
2011-09-13  Jan Kratochvil  <jan.kratochvil@redhat.com>

	Recognize virtual tail call frames.
	* Makefile.in (SFILES): Add dwarf2-frame-tailcall.c.
	(HFILES_NO_SRCDIR): Add dwarf2-frame-tailcall.h.
	(COMMON_OBS): Add dwarf2-frame-tailcall.o.
	* dwarf2-frame-tailcall.c: New file.
	* dwarf2-frame-tailcall.h: New file.
	* dwarf2-frame.c: Include dwarf2-frame-tailcall.h.
	(execute_cfa_program): New function comment.  Return INSN_PTR.  Reset
	REGS.PREV only after CIE execution.
	(struct dwarf2_frame_cache): New field tailcall_cache.
	(dwarf2_frame_cache): New variables entry_pc, entry_cfa_sp_offset,
	entry_cfa_sp_offset_p and instr.  Execute FDE instructions in two
	parts, try to find entry_cfa_sp_offset.  Call
	dwarf2_tailcall_sniffer_first.
	(dwarf2_frame_prev_register): Call dwarf2_tailcall_prev_register_first
	when appropriate.
	(dwarf2_frame_dealloc_cache): New function.
	(dwarf2_frame_sniffer): Preinitialize cache by dwarf2_frame_cache.
	(dwarf2_frame_unwind): Install dwarf2_frame_dealloc_cache.
	(dwarf2_signal_frame_unwind): Do not install dwarf2_frame_dealloc_cache.
	(dwarf2_append_unwinders): Add dwarf2_tailcall_frame_unwind.
	(dwarf2_frame_cfa): Support also dwarf2_tailcall_frame_unwind.
	* dwarf2loc.c (func_addr_to_tail_call_list)
	(tailcall_dump, call_sitep, VEC (call_sitep), chain_candidate)
	(call_site_find_chain_1, call_site_find_chain): New.
	* dwarf2loc.h (struct call_site_chain): New.
	(call_site_find_chain): New declaration.
	* frame.c (get_frame_address_in_block): Support also TAILCALL_FRAME.
	* frame.h (enum frame_type): New entry TAILCALL_FRAME.
	* python/py-frame.c (gdbpy_initialize_frames): Add TAILCALL_FRAME.
	* stack.c (frame_info): Support also TAILCALL_FRAME.

gdb/doc/
2011-07-18  Jan Kratochvil  <jan.kratochvil@redhat.com>
	    Eli Zaretskii  <eliz@gnu.org>

	Recognize virtual tail call frames.
	* gdb.texinfo (Optimized Code): Add reference to Tail Call Frames.
	(Tail Call Frames): New node.
	(Frames In Python): Add gdb.TAILCALL_FRAME.

gdb/testsuite/
2011-07-18  Jan Kratochvil  <jan.kratochvil@redhat.com>

	Recognize virtual tail call frames.
	* gdb.arch/amd64-entry-value.cc (c, a, b, amb_z, amb_y, amb_x, amb)
	(amb_b, amb_a): New.
	(main): Call a and b.
	* gdb.arch/amd64-entry-value.exp (tailcall: breakhere, tailcall: bt)
	(tailcall: p i, tailcall: p j, set $sp0=$sp, up, p $sp0 == $sp, frame 3)
	(p $sp0 + sizeof (void *) == $sp, ambiguous: breakhere, ambiguous: bt):
	New tests.

--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -695,6 +695,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
 	cp-name-parser.y \
 	dbxread.c demangle.c dictionary.c disasm.c doublest.c dummy-frame.c \
 	dwarf2expr.c dwarf2loc.c dwarf2read.c dwarf2-frame.c \
+	dwarf2-frame-tailcall.c \
 	elfread.c environ.c eval.c event-loop.c event-top.c \
 	exceptions.c expprint.c \
 	f-exp.y f-lang.c f-typeprint.c f-valprint.c filesystem.c \
@@ -771,7 +772,7 @@ cli/cli-decode.h cli/cli-cmds.h cli/cli-dump.h cli/cli-utils.h \
 cli/cli-script.h macrotab.h symtab.h version.h gnulib/wchar.in.h \
 gnulib/string.in.h gnulib/str-two-way.h \
 gnulib/stdint.in.h remote.h gdb.h sparc-nat.h \
-gdbthread.h dwarf2-frame.h nbsd-nat.h dcache.h \
+gdbthread.h dwarf2-frame.h dwarf2-frame-tailcall.h nbsd-nat.h dcache.h \
 amd64-nat.h s390-tdep.h arm-linux-tdep.h exceptions.h macroscope.h \
 gdbarch.h bsd-uthread.h gdb_stat.h memory-map.h	memrange.h \
 mdebugread.h m88k-tdep.h stabsread.h hppa-linux-offsets.h linux-fork.h \
@@ -879,7 +880,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
 	bcache.o objfiles.o observer.o minsyms.o maint.o demangle.o \
 	dbxread.o coffread.o coff-pe-read.o \
 	dwarf2read.o mipsread.o stabsread.o corefile.o \
-	dwarf2expr.o dwarf2loc.o dwarf2-frame.o \
+	dwarf2expr.o dwarf2loc.o dwarf2-frame.o dwarf2-frame-tailcall.o \
 	ada-lang.o c-lang.o d-lang.o f-lang.o objc-lang.o \
 	ada-tasks.o \
 	ui-out.o cli-out.o \
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -9486,6 +9486,7 @@ please report it to us as a bug (including a test case!).
 
 @menu
 * Inline Functions::            How @value{GDBN} presents inlining
+* Tail Call Frames::            @value{GDBN} analysis of jumps to functions
 @end menu
 
 @node Inline Functions
@@ -9553,6 +9554,126 @@ and print a variable where your program stored the return value.
 
 @end itemize
 
+@node Tail Call Frames
+@section Tail Call Frames
+@cindex tail call frames, debugging
+
+Function @code{B} can call function @code{C} in its very last statement.  In
+unoptimized compilation the call of @code{C} is immediately followed by return
+instruction at the end of @code{B} code.  Optimizing compiler may replace the
+call and return in function @code{B} into one jump to function @code{C}
+instead.  Such use of a jump instruction is called @dfn{tail call}.
+
+During execution of function @code{C}, there will be no indication in the
+function call stack frames that it was tail-called from @code{B}.  If function
+@code{A} regularly calls function @code{B} which tail-calls function @code{C},
+then @value{GDBN} will see @code{A} as the caller of @code{C}.  However, in
+some cases @value{GDBN} can determine that @code{C} was tail-called from
+@code{B}, and it will then create fictitious call frame for that, with the
+return address set up as if @code{B} called @code{C} normally.
+
+This functionality is currently supported only by DWARF 2 debugging format and
+the compiler has to produce @samp{DW_TAG_GNU_call_site} tags.  With
+@value{NGCC}, you need to specify @option{-O -g} during compilation, to get
+this information.
+
+@kbd{info frame} command (@pxref{Frame Info}) will indicate the tail call frame
+kind by text @code{tail call frame} such as in this sample @value{GDBN} output:
+
+@smallexample
+(gdb) x/i $pc - 2
+   0x40066b <b(int, double)+11>: jmp 0x400640 <c(int, double)>
+(gdb) info frame
+Stack level 1, frame at 0x7fffffffda30:
+ rip = 0x40066d in b (amd64-entry-value.cc:59); saved rip 0x4004c5
+ tail call frame, caller of frame at 0x7fffffffda30
+ source language c++.
+ Arglist at unknown address.
+ Locals at unknown address, Previous frame's sp is 0x7fffffffda30
+@end smallexample
+
+The detection of all the possible code path executions can find them ambiguous.
+There is no execution history stored (possible @ref{Reverse Execution} is never
+used for this purpose) and the last known caller could have reached the known
+callee by multiple different jump sequences.  In such case @value{GDBN} still
+tries to show at least all the unambiguous top tail callers and all the
+unambiguous bottom tail calees, if any.
+
+@table @code
+@item set debug entry-values
+@kindex set debug entry-values
+When set to on, enables printing of analysis messages for both frame argument
+values at function entry and tail calls.  It will show all the possible valid
+tail calls code paths it has considered.  It will also print the intersection
+of them with the final unambiguous (possibly partial or even empty) code path
+result.
+
+@item show debug entry-values
+@kindex show debug entry-values
+Show the current state of analysis messages printing for both frame argument
+values at function entry and tail calls.
+@end table
+
+The analysis messages for tail calls can for example show why the virtual tail
+call frame for function @code{c} has not been recognized (due to the indirect
+reference by variable @code{x}):
+
+@smallexample
+static void __attribute__((noinline, noclone)) c (void);
+void (*x) (void) = c;
+static void __attribute__((noinline, noclone)) a (void) @{ x++; @}
+static void __attribute__((noinline, noclone)) c (void) @{ a (); @}
+int main (void) @{ x (); return 0; @}
+
+Breakpoint 1, DW_OP_GNU_entry_value resolving cannot find
+DW_TAG_GNU_call_site 0x40039a in main
+a () at t.c:3
+3	static void __attribute__((noinline, noclone)) a (void) @{ x++; @}
+(gdb) bt
+#0  a () at t.c:3
+#1  0x000000000040039a in main () at t.c:5
+@end smallexample
+
+Another possibility is an ambiguous virtual tail call frames resolution:
+
+@smallexample
+int i;
+static void __attribute__((noinline, noclone)) f (void) @{ i++; @}
+static void __attribute__((noinline, noclone)) e (void) @{ f (); @}
+static void __attribute__((noinline, noclone)) d (void) @{ f (); @}
+static void __attribute__((noinline, noclone)) c (void) @{ d (); @}
+static void __attribute__((noinline, noclone)) b (void)
+@{ if (i) c (); else e (); @}
+static void __attribute__((noinline, noclone)) a (void) @{ b (); @}
+int main (void) @{ a (); return 0; @}
+
+tailcall: initial: 0x4004d2(a) 0x4004ce(b) 0x4004b2(c) 0x4004a2(d)
+tailcall: compare: 0x4004d2(a) 0x4004cc(b) 0x400492(e)
+tailcall: reduced: 0x4004d2(a) |
+(gdb) bt
+#0  f () at t.c:2
+#1  0x00000000004004d2 in a () at t.c:8
+#2  0x0000000000400395 in main () at t.c:9
+@end smallexample
+
+Frames #0 and #2 are real, #1 is a virtual tail call frame.  The code can have
+possible execution paths
+@code{main@arrow{}a@arrow{}b@arrow{}c@arrow{}d@arrow{}f} or
+@code{main@arrow{}a@arrow{}b@arrow{}e@arrow{}f}, @value{GDBN} cannot find which
+one from the inferior state.
+
+@code{initial:} state shows some random possible calling sequence @value{GDBN}
+has found.  It then finds another possible calling sequcen - that one is
+prefixed by @code{compare:}.  The non-ambiguous intersection of these two is
+printed as the @code{reduced:} calling sequence.  That one could have many
+futher @code{compare:} and @code{reduced:} statements as long as there remain
+any non-ambiguous sequence entries.
+
+For the frame of function @code{b} in both cases there are different possible
+@code{$pc} values (@code{0x4004cc} or @code{0x4004ce}), therefore this frame is
+also ambigous.  The only non-ambiguous frame is the one for function @code{a},
+therefore this one is displayed to the user while the ambiguous frames are
+omitted.
 
 @node Macros
 @chapter C Preprocessor Macros
@@ -23080,6 +23201,9 @@ inferior function call.
 A frame representing an inlined function.  The function was inlined
 into a @code{gdb.NORMAL_FRAME} that is older than this one.
 
+@item gdb.TAILCALL_FRAME
+A frame representing a tail call.  @xref{Tail Call Frames}.
+
 @item gdb.SIGTRAMP_FRAME
 A signal trampoline frame.  This is the frame created by the OS when
 it calls into a signal handler.
--- /dev/null
+++ b/gdb/dwarf2-frame-tailcall.c
@@ -0,0 +1,479 @@
+/* Virtual tail call frames unwinder for GDB.
+
+   Copyright (C) 2010, 2011 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+#include "gdb_assert.h"
+#include "frame.h"
+#include "dwarf2-frame-tailcall.h"
+#include "dwarf2loc.h"
+#include "frame-unwind.h"
+#include "block.h"
+#include "hashtab.h"
+#include "exceptions.h"
+#include "gdbtypes.h"
+#include "regcache.h"
+#include "value.h"
+
+/* Contains struct tailcall_cache indexed by next_bottom_frame.  */
+static htab_t cache_htab;
+
+/* Associate structure of the unwinder to call_site_chain.  Lifetime of this
+   structure is maintained by REFC decremented by dealloc_cache, all of them
+   get deleted during reinit_frame_cache.  */
+struct tailcall_cache
+{
+  /* It must be the first one of this struct.  It is the furthest callee.  */
+  struct frame_info *next_bottom_frame;
+
+  /* Reference count.  The whole chain of virtual tail call frames shares one
+     tailcall_cache.  */
+  int refc;
+
+  /* Associated found virtual taill call frames chain, it is never NULL.  */
+  struct call_site_chain *chain;
+
+  /* Cached pretended_chain_levels result.  */
+  int chain_levels;
+
+  /* Unwound PC from the top (caller) frame, as it is not contained
+     in CHAIN.  */
+  CORE_ADDR prev_pc;
+
+  /* Compensate SP in caller frames appropriately.  prev_sp and
+     entry_cfa_sp_offset are valid only if PREV_SP_P.  PREV_SP is SP at the top
+     (caller) frame.  ENTRY_CFA_SP_OFFSET is shift of SP in tail call frames
+     against next_bottom_frame SP.  */
+  unsigned prev_sp_p : 1;
+  CORE_ADDR prev_sp;
+  LONGEST entry_cfa_sp_offset;
+};
+
+/* hash_f for htab_create_alloc of cache_htab.  */
+
+static hashval_t
+cache_hash (const void *arg)
+{
+  const struct tailcall_cache *cache = arg;
+
+  return htab_hash_pointer (cache->next_bottom_frame);
+}
+
+/* eq_f for htab_create_alloc of cache_htab.  */
+
+static int
+cache_eq (const void *arg1, const void *arg2)
+{
+  const struct tailcall_cache *cache1 = arg1;
+  const struct tailcall_cache *cache2 = arg2;
+
+  return cache1->next_bottom_frame == cache2->next_bottom_frame;
+}
+
+/* Create new tailcall_cache for NEXT_BOTTOM_FRAME, NEXT_BOTTOM_FRAME must not
+   yet have been indexed by cache_htab.  Caller holds one reference of the new
+   tailcall_cache.  */
+
+static struct tailcall_cache *
+cache_new_ref1 (struct frame_info *next_bottom_frame)
+{
+  struct tailcall_cache *cache;
+  void **slot;
+
+  cache = xzalloc (sizeof (*cache));
+
+  cache->next_bottom_frame = next_bottom_frame;
+  cache->refc = 1;
+
+  slot = htab_find_slot (cache_htab, cache, INSERT);
+  gdb_assert (*slot == NULL);
+  *slot = cache;
+
+  return cache;
+}
+
+/* Create new reference to CACHE.  */
+
+static void
+cache_ref (struct tailcall_cache *cache)
+{
+  gdb_assert (cache->refc > 0);
+
+  cache->refc++;
+}
+
+/* Drop reference to CACHE, possibly fully freeing it and unregistering it from
+   cache_htab.  */
+
+static void
+cache_unref (struct tailcall_cache *cache)
+{
+  gdb_assert (cache->refc > 0);
+
+  if (!--cache->refc)
+    {
+      gdb_assert (htab_find_slot (cache_htab, cache, NO_INSERT) != NULL);
+      htab_remove_elt (cache_htab, cache);
+
+      xfree (cache->chain);
+      xfree (cache);
+    }
+}
+
+/* Return 1 if FI is a non-bottom (not the callee) tail call frame.  Otherwise
+   return 0.  */
+
+static int
+frame_is_tailcall (struct frame_info *fi)
+{
+  return frame_unwinder_is (fi, &dwarf2_tailcall_frame_unwind);
+}
+
+/* Try to find tailcall_cache in cache_htab if FI is a part of its virtual tail
+   call chain.  Otherwise return NULL.  No new reference is created.  */
+
+static struct tailcall_cache *
+cache_find (struct frame_info *fi)
+{
+  struct tailcall_cache *cache;
+  void **slot;
+
+  while (frame_is_tailcall (fi))
+    {
+      fi = get_next_frame (fi);
+      gdb_assert (fi != NULL);
+    }
+
+  slot = htab_find_slot (cache_htab, &fi, NO_INSERT);
+  if (slot == NULL)
+    return NULL;
+
+  cache = *slot;
+  gdb_assert (cache != NULL);
+  return cache;
+}
+
+/* Number of virtual frames between THIS_FRAME and CACHE->NEXT_BOTTOM_FRAME.
+   If THIS_FRAME is CACHE-> NEXT_BOTTOM_FRAME return -1.  */
+
+static int
+existing_next_levels (struct frame_info *this_frame,
+		      struct tailcall_cache *cache)
+{
+  int retval = (frame_relative_level (this_frame)
+		- frame_relative_level (cache->next_bottom_frame) - 1);
+
+  gdb_assert (retval >= -1);
+
+  return retval;
+}
+
+/* The number of virtual tail call frames in CHAIN.  With no virtual tail call
+   frames the function would return 0 (but CHAIN does not exist in such
+   case).  */
+
+static int
+pretended_chain_levels (struct call_site_chain *chain)
+{
+  int chain_levels;
+
+  gdb_assert (chain != NULL);
+
+  if (chain->callers == chain->length && chain->callees == chain->length)
+    return chain->length;
+
+  chain_levels = chain->callers + chain->callees;
+  gdb_assert (chain_levels < chain->length);
+
+  return chain_levels;
+}
+
+/* Implementation of frame_this_id_ftype.  THIS_CACHE must be already
+   initialized with tailcall_cache, THIS_FRAME must be a part of THIS_CACHE.
+
+   Specific virtual tail call frames are tracked by INLINE_DEPTH.  */
+
+static void
+tailcall_frame_this_id (struct frame_info *this_frame, void **this_cache,
+			struct frame_id *this_id)
+{
+  struct tailcall_cache *cache = *this_cache;
+  struct frame_info *next_frame;
+
+  /* Tail call does not make sense for a sentinel frame.  */
+  next_frame = get_next_frame (this_frame);
+  gdb_assert (next_frame != NULL);
+
+  *this_id = get_frame_id (next_frame);
+  (*this_id).code_addr = get_frame_pc (this_frame);
+  (*this_id).code_addr_p = 1;
+  (*this_id).inline_depth = (cache->chain_levels
+			     - existing_next_levels (this_frame, cache));
+  gdb_assert ((*this_id).inline_depth > 0);
+}
+
+/* Find PC to be unwound from THIS_FRAME.  THIS_FRAME must be a part of
+   CACHE.  */
+
+static CORE_ADDR
+pretend_pc (struct frame_info *this_frame, struct tailcall_cache *cache)
+{
+  int next_levels = existing_next_levels (this_frame, cache);
+  struct call_site_chain *chain = cache->chain;
+  int caller_no;
+
+  gdb_assert (chain != NULL);
+
+  next_levels++;
+  gdb_assert (next_levels >= 0);
+
+  if (next_levels < chain->callees)
+    return chain->call_site[chain->length - next_levels - 1]->pc;
+  next_levels -= chain->callees;
+
+  /* Otherwise CHAIN->CALLEES are already covered by CHAIN->CALLERS.  */
+  if (chain->callees != chain->length)
+    {
+      if (next_levels < chain->callers)
+	return chain->call_site[chain->callers - next_levels - 1]->pc;
+      next_levels -= chain->callers;
+    }
+
+  gdb_assert (next_levels == 0);
+  return cache->prev_pc;
+}
+
+/* Implementation of frame_prev_register_ftype.  If no specific register
+   override is supplied NULL is returned (this is incompatible with
+   frame_prev_register_ftype semantics).  next_bottom_frame and tail call
+   frames unwind the NULL case differently.  */
+
+struct value *
+dwarf2_tailcall_prev_register_first (struct frame_info *this_frame,
+				     void **tailcall_cachep, int regnum)
+{
+  struct gdbarch *this_gdbarch = get_frame_arch (this_frame);
+  struct tailcall_cache *cache = *tailcall_cachep;
+  CORE_ADDR addr;
+
+  if (regnum == gdbarch_pc_regnum (this_gdbarch))
+    addr = pretend_pc (this_frame, cache);
+  else if (cache->prev_sp_p && regnum == gdbarch_sp_regnum (this_gdbarch))
+    {
+      int next_levels = existing_next_levels (this_frame, cache);
+
+      if (next_levels == cache->chain_levels - 1)
+	addr = cache->prev_sp;
+      else
+	addr = get_frame_base (this_frame) - cache->entry_cfa_sp_offset;
+    }
+  else
+    return NULL;
+
+  return frame_unwind_got_address (this_frame, regnum, addr);
+}
+
+/* Implementation of frame_prev_register_ftype for tail call frames.  Register
+   set of virtual tail call frames is assumed to be the one of the top (caller)
+   frame - assume unchanged register value for NULL from
+   dwarf2_tailcall_prev_register_first.  */
+
+static struct value *
+tailcall_frame_prev_register (struct frame_info *this_frame,
+			       void **this_cache, int regnum)
+{
+  struct tailcall_cache *cache = *this_cache;
+  struct value *val;
+
+  gdb_assert (this_frame != cache->next_bottom_frame);
+
+  val = dwarf2_tailcall_prev_register_first (this_frame, this_cache, regnum);
+  if (val)
+    return val;
+
+  return frame_unwind_got_register (this_frame, regnum, regnum);
+}
+
+/* Implementation of frame_sniffer_ftype.  It will never find a new chain, use
+   dwarf2_tailcall_sniffer_first for the bottom (callee) frame.  It will find
+   all the predecessing virtual tail call frames, it will return false when
+   there exist no more tail call frames in this chain.  */
+
+static int
+tailcall_frame_sniffer (const struct frame_unwind *self,
+			 struct frame_info *this_frame, void **this_cache)
+{
+  struct frame_info *next_frame;
+  int next_levels;
+  struct tailcall_cache *cache;
+
+  /* Inner tail call element does not make sense for a sentinel frame.  */
+  next_frame = get_next_frame (this_frame);
+  if (next_frame == NULL)
+    return 0;
+
+  cache = cache_find (next_frame);
+  if (cache == NULL)
+    return 0;
+
+  cache_ref (cache);
+
+  next_levels = existing_next_levels (this_frame, cache);
+
+  /* NEXT_LEVELS is -1 only in dwarf2_tailcall_sniffer_first.  */
+  gdb_assert (next_levels >= 0);
+  gdb_assert (next_levels <= cache->chain_levels);
+
+  if (next_levels == cache->chain_levels)
+    {
+      cache_unref (cache);
+      return 0;
+    }
+
+  *this_cache = cache;
+  return 1;
+}
+
+/* The initial "sniffer" whether THIS_FRAME is a bottom (callee) frame of a new
+   chain to create.  Keep TAILCALL_CACHEP NULL if it did not find any chain,
+   initialize it otherwise.  No tail call chain is created if there are no
+   unambiguous virtual tail call frames to report.
+   
+   ENTRY_CFA_SP_OFFSETP is NULL if no special SP handling is possible,
+   otherwise *ENTRY_CFA_SP_OFFSETP is the number of bytes to subtract from tail
+   call frames frame base to get the SP value there - to simulate return
+   address pushed on the stack.  */
+
+void
+dwarf2_tailcall_sniffer_first (struct frame_info *this_frame,
+			       void **tailcall_cachep,
+			       const LONGEST *entry_cfa_sp_offsetp)
+{
+  CORE_ADDR prev_pc = 0, prev_sp = 0;	/* GCC warning.  */
+  int prev_sp_p = 0;
+  CORE_ADDR this_pc, pc;
+  struct gdbarch *prev_gdbarch;
+  struct call_site_chain *chain = NULL;
+  struct frame_info *fi;
+  struct tailcall_cache *cache;
+  volatile struct gdb_exception except;
+
+  gdb_assert (*tailcall_cachep == NULL);
+
+  this_pc = get_frame_pc (this_frame);
+
+  /* Catch any unwinding errors.  */
+  TRY_CATCH (except, RETURN_MASK_ERROR)
+    {
+      int pc_regnum, sp_regnum;
+
+      prev_gdbarch = frame_unwind_arch (this_frame);
+      pc_regnum = gdbarch_pc_regnum (prev_gdbarch);
+      if (pc_regnum == -1)
+	break;
+
+      /* Simulate frame_unwind_pc without setting this_frame->prev_pc.p.  */
+      prev_pc = frame_unwind_register_unsigned (this_frame, pc_regnum);
+
+      /* call_site_find_chain can throw an exception.  */
+      chain = call_site_find_chain (prev_gdbarch, prev_pc, this_pc);
+
+      if (entry_cfa_sp_offsetp == NULL)
+	break;
+      sp_regnum = gdbarch_sp_regnum (prev_gdbarch);
+      if (sp_regnum == -1)
+	break;
+      prev_sp = frame_unwind_register_unsigned (this_frame, sp_regnum);
+      prev_sp_p = 1;
+    }
+  if (except.reason < 0)
+    {
+      if (entry_values_debug)
+	exception_print (gdb_stdout, except);
+      return;
+    }
+
+  /* Ambiguous unwind or unambiguous unwind verified as matching.  */
+  if (chain == NULL || chain->length == 0)
+    {
+      xfree (chain);
+      return;
+    }
+
+  cache = cache_new_ref1 (this_frame);
+  *tailcall_cachep = cache;
+  cache->chain = chain;
+  cache->prev_pc = prev_pc;
+  cache->chain_levels = pretended_chain_levels (chain);
+  cache->prev_sp_p = prev_sp_p;
+  if (cache->prev_sp_p)
+    {
+      cache->prev_sp = prev_sp;
+      cache->entry_cfa_sp_offset = *entry_cfa_sp_offsetp;
+    }
+  gdb_assert (cache->chain_levels > 0);
+}
+
+/* Implementation of frame_dealloc_cache_ftype.  It can be called even for the
+   bottom chain frame from dwarf2_frame_dealloc_cache which is not a real
+   TAILCALL_FRAME.  */
+
+static void
+tailcall_frame_dealloc_cache (struct frame_info *self, void *this_cache)
+{
+  struct tailcall_cache *cache = this_cache;
+
+  cache_unref (cache);
+}
+
+/* Implementation of frame_prev_arch_ftype.  We assume all the virtual tail
+   call frames have gdbarch of the bottom (callee) frame.  */
+
+static struct gdbarch *
+tailcall_frame_prev_arch (struct frame_info *this_frame,
+			  void **this_prologue_cache)
+{
+  struct tailcall_cache *cache = *this_prologue_cache;
+
+  return get_frame_arch (cache->next_bottom_frame);
+}
+
+/* Virtual tail call frame unwinder if dwarf2_tailcall_sniffer_first finds
+   a chain to create.  */
+
+const struct frame_unwind dwarf2_tailcall_frame_unwind =
+{
+  TAILCALL_FRAME,
+  default_frame_unwind_stop_reason,
+  tailcall_frame_this_id,
+  tailcall_frame_prev_register,
+  NULL,
+  tailcall_frame_sniffer,
+  tailcall_frame_dealloc_cache,
+  tailcall_frame_prev_arch
+};
+
+/* Provide a prototype to silence -Wmissing-prototypes.  */
+extern initialize_file_ftype _initialize_tailcall_frame;
+
+void
+_initialize_tailcall_frame (void)
+{
+  cache_htab = htab_create_alloc (50, cache_hash, cache_eq, NULL, xcalloc,
+				  xfree);
+}
--- /dev/null
+++ b/gdb/dwarf2-frame-tailcall.h
@@ -0,0 +1,39 @@
+/* Definitions for virtual tail call frames unwinder for GDB.
+
+   Copyright (C) 2010, 2011 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DWARF2_FRAME_TAILCALL_H
+#define DWARF2_FRAME_TAILCALL_H 1
+
+struct frame_info;
+struct frame_unwind;
+
+/* The tail call frame unwinder.  */
+
+extern void
+  dwarf2_tailcall_sniffer_first (struct frame_info *this_frame,
+				 void **tailcall_cachep,
+				 const LONGEST *entry_cfa_sp_offsetp);
+
+extern struct value *
+  dwarf2_tailcall_prev_register_first (struct frame_info *this_frame,
+				       void **tailcall_cachep, int regnum);
+
+extern const struct frame_unwind dwarf2_tailcall_frame_unwind;
+
+#endif /* !DWARF2_FRAME_TAILCALL_H */
--- a/gdb/dwarf2-frame.c
+++ b/gdb/dwarf2-frame.c
@@ -41,6 +41,7 @@
 #include "ax.h"
 #include "dwarf2loc.h"
 #include "exceptions.h"
+#include "dwarf2-frame-tailcall.h"
 
 struct comp_unit;
 
@@ -398,7 +399,11 @@ Not implemented: computing unwound register using explicit value operator"));
 }
 
 
-static void
+/* Execute FDE program from INSN_PTR possibly up to INSN_END or up to inferior
+   PC.  Modify FS state accordingly.  Return current INSN_PTR where the
+   execution has stopped, one can resume it on the next call.  */
+
+static const gdb_byte *
 execute_cfa_program (struct dwarf2_fde *fde, const gdb_byte *insn_ptr,
 		     const gdb_byte *insn_end, struct gdbarch *gdbarch,
 		     CORE_ADDR pc, struct dwarf2_frame_state *fs)
@@ -681,9 +686,14 @@ bad CFI data; mismatched DW_CFA_restore_state at %s"),
 	}
     }
 
-  /* Don't allow remember/restore between CIE and FDE programs.  */
-  dwarf2_frame_state_free_regs (fs->regs.prev);
-  fs->regs.prev = NULL;
+  if (fs->initial.reg == NULL)
+    {
+      /* Don't allow remember/restore between CIE and FDE programs.  */
+      dwarf2_frame_state_free_regs (fs->regs.prev);
+      fs->regs.prev = NULL;
+    }
+
+  return insn_ptr;
 }
 
 
@@ -975,6 +985,13 @@ struct dwarf2_frame_cache
 
   /* The .text offset.  */
   CORE_ADDR text_offset;
+
+  /* If not NULL then this frame is the bottom frame of a TAILCALL_FRAME
+     sequence.  If NULL then it is a normal case with no TAILCALL_FRAME
+     involved.  Non-bottom frames of a virtual tail call frames chain use
+     dwarf2_tailcall_frame_unwind unwinder so this field does not apply for
+     them.  */
+  void *tailcall_cache;
 };
 
 static struct dwarf2_frame_cache *
@@ -988,6 +1005,10 @@ dwarf2_frame_cache (struct frame_info *this_frame, void **this_cache)
   struct dwarf2_frame_state *fs;
   struct dwarf2_fde *fde;
   volatile struct gdb_exception ex;
+  CORE_ADDR entry_pc;
+  LONGEST entry_cfa_sp_offset;
+  int entry_cfa_sp_offset_p = 0;
+  const gdb_byte *instr;
 
   if (*this_cache)
     return *this_cache;
@@ -1039,8 +1060,25 @@ dwarf2_frame_cache (struct frame_info *this_frame, void **this_cache)
   fs->initial = fs->regs;
   fs->initial.reg = dwarf2_frame_state_copy_regs (&fs->regs);
 
+  if (get_frame_func_if_available (this_frame, &entry_pc))
+    {
+      /* Decode the insns in the FDE up to the entry PC.  */
+      instr = execute_cfa_program (fde, fde->instructions, fde->end, gdbarch,
+				   entry_pc, fs);
+
+      if (fs->regs.cfa_how == CFA_REG_OFFSET
+	  && (gdbarch_dwarf2_reg_to_regnum (gdbarch, fs->regs.cfa_reg)
+	      == gdbarch_sp_regnum (gdbarch)))
+	{
+	  entry_cfa_sp_offset = fs->regs.cfa_offset;
+	  entry_cfa_sp_offset_p = 1;
+	}
+    }
+  else
+    instr = fde->instructions;
+
   /* Then decode the insns in the FDE up to our target PC.  */
-  execute_cfa_program (fde, fde->instructions, fde->end, gdbarch,
+  execute_cfa_program (fde, instr, fde->end, gdbarch,
 		       get_frame_pc (this_frame), fs);
 
   TRY_CATCH (ex, RETURN_MASK_ERROR)
@@ -1181,6 +1219,12 @@ incomplete CFI data; unspecified registers (e.g., %s) at %s"),
 
   do_cleanups (old_chain);
 
+  /* Try to find a virtual tail call frames chain with bottom (callee) frame
+     starting at THIS_FRAME.  */
+  dwarf2_tailcall_sniffer_first (this_frame, &cache->tailcall_cache,
+				 (entry_cfa_sp_offset_p
+				  ? &entry_cfa_sp_offset : NULL));
+
   return cache;
 }
 
@@ -1226,6 +1270,22 @@ dwarf2_frame_prev_register (struct frame_info *this_frame, void **this_cache,
   CORE_ADDR addr;
   int realnum;
 
+  /* Non-bottom frames of a virtual tail call frames chain use
+     dwarf2_tailcall_frame_unwind unwinder so this code does not apply for
+     them.  If dwarf2_tailcall_prev_register_first does not have specific value
+     unwind the register, tail call frames are assumed to have the register set
+     of the top caller.  */
+  if (cache->tailcall_cache)
+    {
+      struct value *val;
+      
+      val = dwarf2_tailcall_prev_register_first (this_frame,
+						 &cache->tailcall_cache,
+						 regnum);
+      if (val)
+	return val;
+    }
+
   switch (cache->reg[regnum].how)
     {
     case DWARF2_FRAME_REG_UNDEFINED:
@@ -1295,6 +1355,18 @@ dwarf2_frame_prev_register (struct frame_info *this_frame, void **this_cache,
     }
 }
 
+/* Proxy for tailcall_frame_dealloc_cache for bottom frame of a virtual tail
+   call frames chain.  */
+
+static void
+dwarf2_frame_dealloc_cache (struct frame_info *self, void *this_cache)
+{
+  struct dwarf2_frame_cache *cache = dwarf2_frame_cache (self, &this_cache);
+
+  if (cache->tailcall_cache)
+    dwarf2_tailcall_frame_unwind.dealloc_cache (self, cache->tailcall_cache);
+}
+
 static int
 dwarf2_frame_sniffer (const struct frame_unwind *self,
 		      struct frame_info *this_frame, void **this_cache)
@@ -1321,7 +1393,14 @@ dwarf2_frame_sniffer (const struct frame_unwind *self,
 				      this_frame))
     return self->type == SIGTRAMP_FRAME;
 
-  return self->type != SIGTRAMP_FRAME;
+  if (self->type != NORMAL_FRAME)
+    return 0;
+
+  /* Preinitializa the cache so that TAILCALL_FRAME can find the record by
+     dwarf2_tailcall_sniffer_first.  */
+  dwarf2_frame_cache (this_frame, this_cache);
+
+  return 1;
 }
 
 static const struct frame_unwind dwarf2_frame_unwind =
@@ -1331,7 +1410,8 @@ static const struct frame_unwind dwarf2_frame_unwind =
   dwarf2_frame_this_id,
   dwarf2_frame_prev_register,
   NULL,
-  dwarf2_frame_sniffer
+  dwarf2_frame_sniffer,
+  dwarf2_frame_dealloc_cache
 };
 
 static const struct frame_unwind dwarf2_signal_frame_unwind =
@@ -1341,7 +1421,10 @@ static const struct frame_unwind dwarf2_signal_frame_unwind =
   dwarf2_frame_this_id,
   dwarf2_frame_prev_register,
   NULL,
-  dwarf2_frame_sniffer
+  dwarf2_frame_sniffer,
+
+  /* TAILCALL_CACHE can never be in such frame to need dealloc_cache.  */
+  NULL
 };
 
 /* Append the DWARF-2 frame unwinders to GDBARCH's list.  */
@@ -1349,6 +1432,10 @@ static const struct frame_unwind dwarf2_signal_frame_unwind =
 void
 dwarf2_append_unwinders (struct gdbarch *gdbarch)
 {
+  /* TAILCALL_FRAME must be first to find the record by
+     dwarf2_tailcall_sniffer_first.  */
+  frame_unwind_append_unwinder (gdbarch, &dwarf2_tailcall_frame_unwind);
+
   frame_unwind_append_unwinder (gdbarch, &dwarf2_frame_unwind);
   frame_unwind_append_unwinder (gdbarch, &dwarf2_signal_frame_unwind);
 }
@@ -1400,7 +1487,8 @@ dwarf2_frame_cfa (struct frame_info *this_frame)
   /* This restriction could be lifted if other unwinders are known to
      compute the frame base in a way compatible with the DWARF
      unwinder.  */
-  if (! frame_unwinder_is (this_frame, &dwarf2_frame_unwind))
+  if (!frame_unwinder_is (this_frame, &dwarf2_frame_unwind)
+      && !frame_unwinder_is (this_frame, &dwarf2_tailcall_frame_unwind))
     error (_("can't compute CFA for this frame"));
   return get_frame_base (this_frame);
 }
--- a/gdb/dwarf2loc.c
+++ b/gdb/dwarf2loc.c
@@ -399,6 +399,321 @@ call_site_to_target_addr (struct gdbarch *call_site_gdbarch,
     }
 }
 
+/* Convert function entry point exact address ADDR to the function which is
+   compliant with TAIL_CALL_LIST_COMPLETE condition.  Throw
+   NO_ENTRY_VALUE_ERROR otherwise.  */
+
+static struct symbol *
+func_addr_to_tail_call_list (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  struct symbol *sym = find_pc_function (addr);
+  struct type *type;
+
+  if (sym == NULL || BLOCK_START (SYMBOL_BLOCK_VALUE (sym)) != addr)
+    throw_error (NO_ENTRY_VALUE_ERROR,
+		 _("DW_TAG_GNU_call_site resolving failed to find function "
+		   "name for address %s"),
+		 paddress (gdbarch, addr));
+
+  type = SYMBOL_TYPE (sym);
+  gdb_assert (TYPE_CODE (type) == TYPE_CODE_FUNC);
+  gdb_assert (TYPE_SPECIFIC_FIELD (type) == TYPE_SPECIFIC_FUNC);
+
+  return sym;
+}
+
+/* Print user readable form of CALL_SITE->PC to gdb_stdlog.  Used only for
+   ENTRY_VALUES_DEBUG.  */
+
+static void
+tailcall_dump (struct gdbarch *gdbarch, const struct call_site *call_site)
+{
+  CORE_ADDR addr = call_site->pc;
+  struct minimal_symbol *msym = lookup_minimal_symbol_by_pc (addr - 1);
+
+  fprintf_unfiltered (gdb_stdlog, " %s(%s)", paddress (gdbarch, addr),
+		      msym == NULL ? "???" : SYMBOL_PRINT_NAME (msym));
+
+}
+
+/* vec.h needs single word type name, typedef it.  */
+typedef struct call_site *call_sitep;
+
+/* Define VEC (call_sitep) functions.  */
+DEF_VEC_P (call_sitep);
+
+/* Intersect RESULTP with CHAIN to keep RESULTP unambiguous, keep in RESULTP
+   only top callers and bottom callees which are present in both.  GDBARCH is
+   used only for ENTRY_VALUES_DEBUG.  RESULTP is NULL after return if there are
+   no remaining possibilities to provide unambiguous non-trivial result.
+   RESULTP should point to NULL on the first (initialization) call.  Caller is
+   responsible for xfree of any RESULTP data.  */
+
+static void
+chain_candidate (struct gdbarch *gdbarch, struct call_site_chain **resultp,
+		 VEC (call_sitep) *chain)
+{
+  struct call_site_chain *result = *resultp;
+  long length = VEC_length (call_sitep, chain);
+  int callers, callees, idx;
+
+  if (result == NULL)
+    {
+      /* Create the initial chain containing all the passed PCs.  */
+
+      result = xmalloc (sizeof (*result) + sizeof (*result->call_site)
+					   * (length - 1));
+      result->length = length;
+      result->callers = result->callees = length;
+      memcpy (result->call_site, VEC_address (call_sitep, chain),
+	      sizeof (*result->call_site) * length);
+      *resultp = result;
+
+      if (entry_values_debug)
+	{
+	  fprintf_unfiltered (gdb_stdlog, "tailcall: initial:");
+	  for (idx = 0; idx < length; idx++)
+	    tailcall_dump (gdbarch, result->call_site[idx]);
+	  fputc_unfiltered ('\n', gdb_stdlog);
+	}
+
+      return;
+    }
+
+  if (entry_values_debug)
+    {
+      fprintf_unfiltered (gdb_stdlog, "tailcall: compare:");
+      for (idx = 0; idx < length; idx++)
+	tailcall_dump (gdbarch, VEC_index (call_sitep, chain, idx));
+      fputc_unfiltered ('\n', gdb_stdlog);
+    }
+
+  /* Intersect callers.  */
+
+  callers = min (result->callers, length);
+  for (idx = 0; idx < callers; idx++)
+    if (result->call_site[idx] != VEC_index (call_sitep, chain, idx))
+      {
+	result->callers = idx;
+	break;
+      }
+
+  /* Intersect callees.  */
+
+  callees = min (result->callees, length);
+  for (idx = 0; idx < callees; idx++)
+    if (result->call_site[result->length - 1 - idx]
+	!= VEC_index (call_sitep, chain, length - 1 - idx))
+      {
+	result->callees = idx;
+	break;
+      }
+
+  if (entry_values_debug)
+    {
+      fprintf_unfiltered (gdb_stdlog, "tailcall: reduced:");
+      for (idx = 0; idx < result->callers; idx++)
+	tailcall_dump (gdbarch, result->call_site[idx]);
+      fputs_unfiltered (" |", gdb_stdlog);
+      for (idx = 0; idx < result->callees; idx++)
+	tailcall_dump (gdbarch, result->call_site[result->length
+						  - result->callees + idx]);
+      fputc_unfiltered ('\n', gdb_stdlog);
+    }
+
+  if (result->callers == 0 && result->callees == 0)
+    {
+      /* There are no common callers or callees.  It could be also a direct
+	 call (which has length 0) with ambiguous possibility of an indirect
+	 call - CALLERS == CALLEES == 0 is valid during the first allocation
+	 but any subsequence processing of such entry means ambiguity.  */
+      xfree (result);
+      *resultp = NULL;
+      return;
+    }
+
+  /* See call_site_find_chain_1 why there is no way to reach the bottom callee
+     PC again.  In such case there must be two different code paths to reach
+     it, therefore some of the former determined intermediate PCs must differ
+     and the unambiguous chain gets shortened.  */
+  gdb_assert (result->callers + result->callees < result->length);
+}
+
+/* Create and return call_site_chain for CALLER_PC and CALLEE_PC.  All the
+   assumed frames between them use GDBARCH.  Use depth first search so we can
+   keep single CHAIN of call_site's back to CALLER_PC.  Function recursion
+   would have needless GDB stack overhead.  Caller is responsible for xfree of
+   the returned result.  Any unreliability results in thrown
+   NO_ENTRY_VALUE_ERROR.  */
+
+static struct call_site_chain *
+call_site_find_chain_1 (struct gdbarch *gdbarch, CORE_ADDR caller_pc,
+			CORE_ADDR callee_pc)
+{
+  struct func_type *func_specific;
+  struct obstack addr_obstack;
+  struct cleanup *back_to_retval, *back_to_workdata;
+  struct call_site_chain *retval = NULL;
+  struct call_site *call_site;
+
+  /* Mark CALL_SITEs so we do not visit the same ones twice.  */
+  htab_t addr_hash;
+
+  /* CHAIN contains only the intermediate CALL_SITEs.  Neither CALLER_PC's
+     call_site nor any possible call_site at CALLEE_PC's function is there.
+     Any CALL_SITE in CHAIN will be iterated to its siblings - via
+     TAIL_CALL_NEXT.  This is inappropriate for CALLER_PC's call_site.  */
+  VEC (call_sitep) *chain = NULL;
+
+  /* We are not interested in the specific PC inside the callee function.  */
+  callee_pc = get_pc_function_start (callee_pc);
+  if (callee_pc == 0)
+    throw_error (NO_ENTRY_VALUE_ERROR, _("Unable to find function for PC %s"),
+		 paddress (gdbarch, callee_pc));
+
+  back_to_retval = make_cleanup (free_current_contents, &retval);
+
+  obstack_init (&addr_obstack);
+  back_to_workdata = make_cleanup_obstack_free (&addr_obstack);   
+  addr_hash = htab_create_alloc_ex (64, core_addr_hash, core_addr_eq, NULL,
+				    &addr_obstack, hashtab_obstack_allocate,
+				    NULL);
+  make_cleanup_htab_delete (addr_hash);
+
+  make_cleanup (VEC_cleanup (call_sitep), &chain);
+
+  /* Do not push CALL_SITE to CHAIN.  Push there only the first tail call site
+     at the target's function.  All the possible tail call sites in the
+     target's function will get iterated as already pushed into CHAIN via their
+     TAIL_CALL_NEXT.  */
+  call_site = call_site_for_pc (gdbarch, caller_pc);
+
+  while (call_site)
+    {
+      CORE_ADDR target_func_addr;
+      struct call_site *target_call_site;
+
+      /* CALLER_FRAME with registers is not available for tail-call jumped
+	 frames.  */
+      target_func_addr = call_site_to_target_addr (gdbarch, call_site, NULL);
+
+      if (target_func_addr == callee_pc)
+	{
+	  chain_candidate (gdbarch, &retval, chain);
+	  if (retval == NULL)
+	    break;
+
+	  /* There is no way to reach CALLEE_PC again as we would prevent
+	     entering it twice as being already marked in ADDR_HASH.  */
+	  target_call_site = NULL;
+	}
+      else
+	{
+	  struct symbol *target_func;
+
+	  target_func = func_addr_to_tail_call_list (gdbarch, target_func_addr);
+	  target_call_site = TYPE_TAIL_CALL_LIST (SYMBOL_TYPE (target_func));
+	}
+
+      do
+	{
+	  /* Attempt to visit TARGET_CALL_SITE.  */
+
+	  if (target_call_site)
+	    {
+	      void **slot;
+
+	      slot = htab_find_slot (addr_hash, &target_call_site->pc, INSERT);
+	      if (*slot == NULL)
+		{
+		  /* Successfully entered TARGET_CALL_SITE.  */
+
+		  *slot = &target_call_site->pc;
+		  VEC_safe_push (call_sitep, chain, target_call_site);
+		  break;
+		}
+	    }
+
+	  /* Backtrack (without revisiting the originating call_site).  Try the
+	     callers's sibling; if there isn't any try the callers's callers's
+	     sibling etc.  */
+
+	  target_call_site = NULL;
+	  while (!VEC_empty (call_sitep, chain))
+	    {
+	      call_site = VEC_pop (call_sitep, chain);
+
+	      gdb_assert (htab_find_slot (addr_hash, &call_site->pc,
+					  NO_INSERT) != NULL);
+	      htab_remove_elt (addr_hash, &call_site->pc);
+
+	      target_call_site = call_site->tail_call_next;
+	      if (target_call_site)
+		break;
+	    }
+	}
+      while (target_call_site);
+
+      if (VEC_empty (call_sitep, chain))
+	call_site = NULL;
+      else
+	call_site = VEC_last (call_sitep, chain);
+    }
+
+  if (retval == NULL)
+    {
+      struct minimal_symbol *msym_caller, *msym_callee;
+      
+      msym_caller = lookup_minimal_symbol_by_pc (caller_pc);
+      msym_callee = lookup_minimal_symbol_by_pc (callee_pc);
+      throw_error (NO_ENTRY_VALUE_ERROR,
+		   _("There are no unambiguously determinable intermediate "
+		     "callers or callees between caller function \"%s\" at %s "
+		     "and callee function \"%s\" at %s"),
+		   (msym_caller == NULL
+		    ? "???" : SYMBOL_PRINT_NAME (msym_caller)),
+		   paddress (gdbarch, caller_pc),
+		   (msym_callee == NULL
+		    ? "???" : SYMBOL_PRINT_NAME (msym_callee)),
+		   paddress (gdbarch, callee_pc));
+    }
+
+  do_cleanups (back_to_workdata);
+  discard_cleanups (back_to_retval);
+  return retval;
+}
+
+/* Create and return call_site_chain for CALLER_PC and CALLEE_PC.  All the
+   assumed frames between them use GDBARCH.  If valid call_site_chain cannot be
+   constructed return NULL.  Caller is responsible for xfree of the returned
+   result.  */
+
+struct call_site_chain *
+call_site_find_chain (struct gdbarch *gdbarch, CORE_ADDR caller_pc,
+		      CORE_ADDR callee_pc)
+{
+  volatile struct gdb_exception e;
+  struct call_site_chain *retval = NULL;
+
+  TRY_CATCH (e, RETURN_MASK_ERROR)
+    {
+      retval = call_site_find_chain_1 (gdbarch, caller_pc, callee_pc);
+    }
+  if (e.reason < 0)
+    {
+      if (e.error == NO_ENTRY_VALUE_ERROR)
+	{
+	  if (entry_values_debug)
+	    exception_print (gdb_stdout, e);
+
+	  return NULL;
+	}
+      else
+	throw_exception (e);
+    }
+  return retval;
+}
+
 /* Fetch call_site_parameter from caller matching the parameters.  FRAME is for
    callee.  See DWARF_REG and FB_OFFSET description at struct
    dwarf_expr_context_funcs->push_dwarf_reg_entry_value.
--- a/gdb/dwarf2loc.h
+++ b/gdb/dwarf2loc.h
@@ -135,4 +135,23 @@ extern void dwarf2_compile_expr_to_ax (struct agent_expr *expr,
 				       const gdb_byte *op_end,
 				       struct dwarf2_per_cu_data *per_cu);
 
+/* Determined tail calls for constructing virtual tail call frames.  */
+
+struct call_site_chain
+  {
+    /* Initially CALLERS == CALLEES == LENGTH.  For partially ambiguous result
+       CALLERS + CALLEES < LENGTH.  */
+    int callers, callees, length;
+
+    /* Variably sized array with LENGTH elements.  Later [0..CALLERS-1] contain
+       top (GDB "prev") sites and [LENGTH-CALLEES..LENGTH-1] contain bottom
+       (GDB "next") sites.  One is interested primarily in the PC field.  */
+    struct call_site *call_site[1];
+  };
+
+struct call_site_stuff;
+extern struct call_site_chain *call_site_find_chain (struct gdbarch *gdbarch,
+						     CORE_ADDR caller_pc,
+						     CORE_ADDR callee_pc);
+
 #endif /* dwarf2loc.h */
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -2035,8 +2035,10 @@ get_frame_address_in_block (struct frame_info *this_frame)
   while (get_frame_type (next_frame) == INLINE_FRAME)
     next_frame = next_frame->next;
 
-  if (get_frame_type (next_frame) == NORMAL_FRAME
+  if ((get_frame_type (next_frame) == NORMAL_FRAME
+       || get_frame_type (next_frame) == TAILCALL_FRAME)
       && (get_frame_type (this_frame) == NORMAL_FRAME
+	  || get_frame_type (this_frame) == TAILCALL_FRAME
 	  || get_frame_type (this_frame) == INLINE_FRAME))
     return pc - 1;
 
--- a/gdb/frame.h
+++ b/gdb/frame.h
@@ -206,6 +206,8 @@ enum frame_type
   /* A frame representing an inlined function, associated with an
      upcoming (prev, outer, older) NORMAL_FRAME.  */
   INLINE_FRAME,
+  /* A virtual frame of a tail call - see dwarf2_tailcall_frame_unwind.  */
+  TAILCALL_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,
--- a/gdb/python/py-frame.c
+++ b/gdb/python/py-frame.c
@@ -595,6 +595,7 @@ gdbpy_initialize_frames (void)
   PyModule_AddIntConstant (gdb_module, "NORMAL_FRAME", NORMAL_FRAME);
   PyModule_AddIntConstant (gdb_module, "DUMMY_FRAME", DUMMY_FRAME);
   PyModule_AddIntConstant (gdb_module, "INLINE_FRAME", INLINE_FRAME);
+  PyModule_AddIntConstant (gdb_module, "TAILCALL_FRAME", TAILCALL_FRAME);
   PyModule_AddIntConstant (gdb_module, "SIGTRAMP_FRAME", SIGTRAMP_FRAME);
   PyModule_AddIntConstant (gdb_module, "ARCH_FRAME", ARCH_FRAME);
   PyModule_AddIntConstant (gdb_module, "SENTINEL_FRAME", SENTINEL_FRAME);
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -1086,6 +1086,8 @@ frame_info (char *addr_exp, int from_tty)
 	printf_filtered (_(" Outermost frame: %s\n"),
 			 frame_stop_reason_string (reason));
     }
+  else if (get_frame_type (fi) == TAILCALL_FRAME)
+    puts_filtered (" tail call frame");
   else if (get_frame_type (fi) == INLINE_FRAME)
     printf_filtered (" inlined into frame %d",
 		     frame_relative_level (get_prev_frame (fi)));
--- a/gdb/testsuite/gdb.arch/amd64-entry-value.cc
+++ b/gdb/testsuite/gdb.arch/amd64-entry-value.cc
@@ -34,9 +34,71 @@ asm ("breakhere:");
   e (v, v);
 }
 
+static void __attribute__((noinline, noclone))
+c (int i, double j)
+{
+  d (i * 10, j * 10);
+}
+
+static void __attribute__((noinline, noclone))
+a (int i, double j)
+{
+  c (i + 1, j + 1);
+}
+
+static void __attribute__((noinline, noclone))
+b (int i, double j)
+{
+  c (i + 2, j + 2);
+}
+
+static void __attribute__((noinline, noclone))
+amb_z (int i)
+{
+  d (i + 7, i + 7.5);
+}
+
+static void __attribute__((noinline, noclone))
+amb_y (int i)
+{
+  amb_z (i + 6);
+}
+
+static void __attribute__((noinline, noclone))
+amb_x (int i)
+{
+  amb_y (i + 5);
+}
+
+static void __attribute__((noinline, noclone))
+amb (int i)
+{
+  if (i < 0)
+    amb_x (i + 3);
+  else
+    amb_x (i + 4);
+}
+
+static void __attribute__((noinline, noclone))
+amb_b (int i)
+{
+  amb (i + 2);
+}
+
+static void __attribute__((noinline, noclone))
+amb_a (int i)
+{
+  amb_b (i + 1);
+}
+
 int
 main ()
 {
   d (30, 30.5);
+  if (v)
+    a (1, 1.25);
+  else
+    b (5, 5.25);
+  amb_a (100);
   return 0;
 }
--- a/gdb/testsuite/gdb.arch/amd64-entry-value.exp
+++ b/gdb/testsuite/gdb.arch/amd64-entry-value.exp
@@ -45,3 +45,31 @@ gdb_test "bt" "^bt\r\n#0 +d *\\(i=31, j=31\\.5\\) \[^\r\n\]*\r\n#1 +0x\[0-9a-f\]
 	 "entry: bt"
 gdb_test "p i" " = 31" "entry: p i"
 gdb_test "p j" { = 31\.5} "entry: p j"
+
+
+# Test virtual tail call frames.
+
+gdb_continue_to_breakpoint "tailcall: breakhere"
+
+gdb_test "bt" "^bt\r\n#0 +d *\\(i=71, j=73\\.5\\) \[^\r\n\]*\r\n#1 +0x\[0-9a-f\]+ in c \\(i=7, j=7\\.25\\) \[^\r\n\]*\r\n#2 +0x\[0-9a-f\]+ in b \\(i=5, j=5\\.25\\) \[^\r\n\]*\r\n#3 +0x\[0-9a-f\]+ in main \[^\r\n\]*" \
+	 "tailcall: bt"
+gdb_test "p i" " = 71" "tailcall: p i"
+gdb_test "p j" " = 73\\.5" "tailcall: p j"
+
+# Test $sp simulation for tail call frames.
+#gdb_test {p/x $sp} " = 0x.*"
+#gdb_test {p/x $pc} " = 0x.*"
+gdb_test_no_output {set $sp0=$sp}
+gdb_test "up" "\r\n#1 .*"
+#gdb_test {p/x $sp} " = 0x.*"
+gdb_test {p $sp0 == $sp} " = true"
+gdb_test "frame 3" "\r\n#3 .*"
+gdb_test {p $sp0 + sizeof (void *) == $sp} " = true"
+
+
+# Test partial-ambiguous virtual tail call frames chain.
+
+gdb_continue_to_breakpoint "ambiguous: breakhere"
+
+gdb_test "bt" "^bt\r\n#0 +d \\(i=<optimized out>, j=<optimized out>\\)\[^\r\n\]*\r\n#1 +0x\[0-9a-f\]+ in amb_z \\(i=<optimized out>\\)\[^\r\n\]*\r\n#2 +0x\[0-9a-f\]+ in amb_y \\(i=<optimized out>\\)\[^\r\n\]*\r\n#3 +0x\[0-9a-f\]+ in amb_x \\(i=<optimized out>\\)\[^\r\n\]*\r\n#4 +0x\[0-9a-f\]+ in amb_b \\(i=101\\)\[^\r\n\]*\r\n#5 +0x\[0-9a-f\]+ in amb_a \\(i=100\\)\[^\r\n\]*\r\n#6 +0x\[0-9a-f\]+ in main \\(\\)\[^\r\n\]*" \
+	 "ambiguous: bt"


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]