gdb/
Add forgotten gdb/dwarf2-frame-tailcall.c. Add forgotten gdb/dwarf2-frame-tailcall.h.
This commit is contained in:
parent
2bda9cc5ee
commit
e1b16eb466
479
gdb/dwarf2-frame-tailcall.c
Normal file
479
gdb/dwarf2-frame-tailcall.c
Normal file
@ -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);
|
||||
}
|
39
gdb/dwarf2-frame-tailcall.h
Normal file
39
gdb/dwarf2-frame-tailcall.h
Normal file
@ -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 */
|
Loading…
Reference in New Issue
Block a user