frame, backtrace: allow targets to supply a frame unwinder
Allow targets to supply their own target-specific frame unwinders; one for normal frames and one for tailcall frames. If a target-specific unwinder is supplied, it will be chosen before any other unwinder. The original patch has been split into this and the next two patches. gdb/ 2013-02-11 Jan Kratochvil <jan.kratochvil@redhat.com> * frame-unwind.c: Include target.h. (frame_unwind_try_unwinder): New function with code from ... (frame_unwind_find_by_frame): ... here. New variable unwinder_from_target, call also target_get_unwinder) (target_get_tailcall_unwinder, and frame_unwind_try_unwinder for it. * target.c (target_get_unwinder, target_get_tailcall_unwinder): New. * target.h (struct target_ops): New fields to_get_unwinder and to_get_tailcall_unwinder. (target_get_unwinder, target_get_tailcall_unwinder): New declarations.
This commit is contained in:
parent
1f3ef5810c
commit
ea001bdce2
@ -1,3 +1,15 @@
|
||||
2014-01-16 Jan Kratochvil <jan.kratochvil@redhat.com>
|
||||
|
||||
* frame-unwind.c: Include target.h.
|
||||
(frame_unwind_try_unwinder): New function with code from ...
|
||||
(frame_unwind_find_by_frame): ... here. New variable
|
||||
unwinder_from_target, call also target_get_unwinder)
|
||||
(target_get_tailcall_unwinder, and frame_unwind_try_unwinder for it.
|
||||
* target.c (target_get_unwinder, target_get_tailcall_unwinder): New.
|
||||
* target.h (struct target_ops): New fields to_get_unwinder and
|
||||
to_get_tailcall_unwinder.
|
||||
(target_get_unwinder, target_get_tailcall_unwinder): New declarations.
|
||||
|
||||
2014-01-16 Markus Metzger <markus.t.metzger@intel.com>
|
||||
|
||||
* record-btrace.c (record_btrace_fetch_registers)
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "exceptions.h"
|
||||
#include "gdb_assert.h"
|
||||
#include "gdb_obstack.h"
|
||||
#include "target.h"
|
||||
|
||||
static struct gdbarch_data *frame_unwind_data;
|
||||
|
||||
@ -88,6 +89,48 @@ frame_unwind_append_unwinder (struct gdbarch *gdbarch,
|
||||
(*ip)->unwinder = unwinder;
|
||||
}
|
||||
|
||||
/* Call SNIFFER from UNWINDER. If it succeeded set UNWINDER for
|
||||
THIS_FRAME and return 1. Otherwise the function keeps THIS_FRAME
|
||||
unchanged and returns 0. */
|
||||
|
||||
static int
|
||||
frame_unwind_try_unwinder (struct frame_info *this_frame, void **this_cache,
|
||||
const struct frame_unwind *unwinder)
|
||||
{
|
||||
struct cleanup *old_cleanup;
|
||||
volatile struct gdb_exception ex;
|
||||
int res = 0;
|
||||
|
||||
old_cleanup = frame_prepare_for_sniffer (this_frame, unwinder);
|
||||
|
||||
TRY_CATCH (ex, RETURN_MASK_ERROR)
|
||||
{
|
||||
res = unwinder->sniffer (unwinder, this_frame, this_cache);
|
||||
}
|
||||
if (ex.reason < 0 && ex.error == NOT_AVAILABLE_ERROR)
|
||||
{
|
||||
/* This usually means that not even the PC is available,
|
||||
thus most unwinders aren't able to determine if they're
|
||||
the best fit. Keep trying. Fallback prologue unwinders
|
||||
should always accept the frame. */
|
||||
do_cleanups (old_cleanup);
|
||||
return 0;
|
||||
}
|
||||
else if (ex.reason < 0)
|
||||
throw_exception (ex);
|
||||
else if (res)
|
||||
{
|
||||
discard_cleanups (old_cleanup);
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
do_cleanups (old_cleanup);
|
||||
return 0;
|
||||
}
|
||||
gdb_assert_not_reached ("frame_unwind_try_unwinder");
|
||||
}
|
||||
|
||||
/* Iterate through sniffers for THIS_FRAME frame until one returns with an
|
||||
unwinder implementation. THIS_FRAME->UNWIND must be NULL, it will get set
|
||||
by this function. Possibly initialize THIS_CACHE. */
|
||||
@ -98,37 +141,24 @@ frame_unwind_find_by_frame (struct frame_info *this_frame, void **this_cache)
|
||||
struct gdbarch *gdbarch = get_frame_arch (this_frame);
|
||||
struct frame_unwind_table *table = gdbarch_data (gdbarch, frame_unwind_data);
|
||||
struct frame_unwind_table_entry *entry;
|
||||
const struct frame_unwind *unwinder_from_target;
|
||||
|
||||
unwinder_from_target = target_get_unwinder ();
|
||||
if (unwinder_from_target != NULL
|
||||
&& frame_unwind_try_unwinder (this_frame, this_cache,
|
||||
unwinder_from_target))
|
||||
return;
|
||||
|
||||
unwinder_from_target = target_get_tailcall_unwinder ();
|
||||
if (unwinder_from_target != NULL
|
||||
&& frame_unwind_try_unwinder (this_frame, this_cache,
|
||||
unwinder_from_target))
|
||||
return;
|
||||
|
||||
for (entry = table->list; entry != NULL; entry = entry->next)
|
||||
{
|
||||
struct cleanup *old_cleanup;
|
||||
volatile struct gdb_exception ex;
|
||||
int res = 0;
|
||||
if (frame_unwind_try_unwinder (this_frame, this_cache, entry->unwinder))
|
||||
return;
|
||||
|
||||
old_cleanup = frame_prepare_for_sniffer (this_frame, entry->unwinder);
|
||||
|
||||
TRY_CATCH (ex, RETURN_MASK_ERROR)
|
||||
{
|
||||
res = entry->unwinder->sniffer (entry->unwinder, this_frame,
|
||||
this_cache);
|
||||
}
|
||||
if (ex.reason < 0 && ex.error == NOT_AVAILABLE_ERROR)
|
||||
{
|
||||
/* This usually means that not even the PC is available,
|
||||
thus most unwinders aren't able to determine if they're
|
||||
the best fit. Keep trying. Fallback prologue unwinders
|
||||
should always accept the frame. */
|
||||
}
|
||||
else if (ex.reason < 0)
|
||||
throw_exception (ex);
|
||||
else if (res)
|
||||
{
|
||||
discard_cleanups (old_cleanup);
|
||||
return;
|
||||
}
|
||||
|
||||
do_cleanups (old_cleanup);
|
||||
}
|
||||
internal_error (__FILE__, __LINE__, _("frame_unwind_find_by_frame failed"));
|
||||
}
|
||||
|
||||
|
28
gdb/target.c
28
gdb/target.c
@ -4471,6 +4471,34 @@ debug_to_prepare_to_store (struct target_ops *self, struct regcache *regcache)
|
||||
fprintf_unfiltered (gdb_stdlog, "target_prepare_to_store ()\n");
|
||||
}
|
||||
|
||||
/* See target.h. */
|
||||
|
||||
const struct frame_unwind *
|
||||
target_get_unwinder (void)
|
||||
{
|
||||
struct target_ops *t;
|
||||
|
||||
for (t = current_target.beneath; t != NULL; t = t->beneath)
|
||||
if (t->to_get_unwinder != NULL)
|
||||
return t->to_get_unwinder;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* See target.h. */
|
||||
|
||||
const struct frame_unwind *
|
||||
target_get_tailcall_unwinder (void)
|
||||
{
|
||||
struct target_ops *t;
|
||||
|
||||
for (t = current_target.beneath; t != NULL; t = t->beneath)
|
||||
if (t->to_get_tailcall_unwinder != NULL)
|
||||
return t->to_get_tailcall_unwinder;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
deprecated_debug_xfer_memory (CORE_ADDR memaddr, bfd_byte *myaddr, int len,
|
||||
int write, struct mem_attrib *attrib,
|
||||
|
11
gdb/target.h
11
gdb/target.h
@ -900,6 +900,11 @@ struct target_ops
|
||||
non-empty annex. */
|
||||
int (*to_augmented_libraries_svr4_read) (void);
|
||||
|
||||
/* Those unwinders are tried before any other arch unwinders. Use NULL if
|
||||
it is not used. */
|
||||
const struct frame_unwind *to_get_unwinder;
|
||||
const struct frame_unwind *to_get_tailcall_unwinder;
|
||||
|
||||
int to_magic;
|
||||
/* Need sub-structure for target machine related rather than comm related?
|
||||
*/
|
||||
@ -1791,6 +1796,12 @@ extern char *target_fileio_read_stralloc (const char *filename);
|
||||
|
||||
extern int target_core_of_thread (ptid_t ptid);
|
||||
|
||||
/* See to_get_unwinder in struct target_ops. */
|
||||
extern const struct frame_unwind *target_get_unwinder (void);
|
||||
|
||||
/* See to_get_tailcall_unwinder in struct target_ops. */
|
||||
extern const struct frame_unwind *target_get_tailcall_unwinder (void);
|
||||
|
||||
/* Verify that the memory in the [MEMADDR, MEMADDR+SIZE) range matches
|
||||
the contents of [DATA,DATA+SIZE). Returns 1 if there's a match, 0
|
||||
if there's a mismatch, and -1 if an error is encountered while
|
||||
|
Loading…
x
Reference in New Issue
Block a user