gdb/
authorJan Kratochvil <jan.kratochvil@redhat.com>
Sun, 9 Oct 2011 20:21:48 +0000 (20:21 +0000)
committerJan Kratochvil <jan.kratochvil@redhat.com>
Sun, 9 Oct 2011 20:21:48 +0000 (20:21 +0000)
Add forgotten gdb/dwarf2-frame-tailcall.c.
Add forgotten gdb/dwarf2-frame-tailcall.h.

gdb/dwarf2-frame-tailcall.c [new file with mode: 0644]
gdb/dwarf2-frame-tailcall.h [new file with mode: 0644]

diff --git a/gdb/dwarf2-frame-tailcall.c b/gdb/dwarf2-frame-tailcall.c
new file mode 100644 (file)
index 0000000..3813115
--- /dev/null
@@ -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);
+}
diff --git a/gdb/dwarf2-frame-tailcall.h b/gdb/dwarf2-frame-tailcall.h
new file mode 100644 (file)
index 0000000..4adf84d
--- /dev/null
@@ -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 */