aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLassi Tuura <lat@iki.fi>2011-03-19 10:00:48 +0100
committerArun Sharma <asharma@fb.com>2011-03-24 22:33:17 -0700
commit9e98f15e9aee12e67cd5956d06ccb559f6a06213 (patch)
tree5131aa7120043dab6da38f6765514858d31f9b23
parent6c1a58fd06eea3a45c6de38eb5d1f79f636bb8d5 (diff)
downloadlibunwind-9e98f15e9aee12e67cd5956d06ccb559f6a06213.tar.gz
Fast back-trace for x86_64 for only collecting the call stack.
Adds new function to perform a pure stack walk without unwinding, functionally similar to backtrace() but accelerated by an address attribute cache the caller maintains across calls.
-rw-r--r--include/dwarf.h1
-rw-r--r--include/libunwind-arm.h10
-rw-r--r--include/libunwind-hppa.h10
-rw-r--r--include/libunwind-ia64.h10
-rw-r--r--include/libunwind-mips.h10
-rw-r--r--include/libunwind-ppc32.h10
-rw-r--r--include/libunwind-ppc64.h10
-rw-r--r--include/libunwind-x86.h10
-rw-r--r--include/libunwind-x86_64.h33
-rw-r--r--include/tdep-arm/libunwind_i.h1
-rw-r--r--include/tdep-hppa/libunwind_i.h1
-rw-r--r--include/tdep-ia64/libunwind_i.h1
-rw-r--r--include/tdep-mips/libunwind_i.h1
-rw-r--r--include/tdep-ppc32/libunwind_i.h3
-rw-r--r--include/tdep-ppc64/libunwind_i.h3
-rw-r--r--include/tdep-x86/libunwind_i.h1
-rw-r--r--include/tdep-x86_64/libunwind_i.h6
-rw-r--r--src/Makefile.am8
-rw-r--r--src/arm/init.h1
-rw-r--r--src/dwarf/Gparser.c4
-rw-r--r--src/hppa/init.h1
-rw-r--r--src/mips/init.h1
-rw-r--r--src/ppc32/init.h1
-rw-r--r--src/ppc64/init.h1
-rw-r--r--src/x86/init.h1
-rw-r--r--src/x86_64/Ginit_local.c4
-rw-r--r--src/x86_64/Gos-linux.c33
-rw-r--r--src/x86_64/Gstash_frame.c95
-rw-r--r--src/x86_64/Gstep.c44
-rw-r--r--src/x86_64/Gtrace.c401
-rw-r--r--src/x86_64/Lstash_frame.c5
-rw-r--r--src/x86_64/Ltrace.c5
-rw-r--r--src/x86_64/init.h1
-rw-r--r--tests/Gtest-trace.c265
-rw-r--r--tests/Ltest-trace.c5
-rw-r--r--tests/Makefile.am3
-rw-r--r--tests/check-namespace.sh.in6
37 files changed, 965 insertions, 41 deletions
diff --git a/include/dwarf.h b/include/dwarf.h
index 82d1f9f8..334aaadc 100644
--- a/include/dwarf.h
+++ b/include/dwarf.h
@@ -296,6 +296,7 @@ typedef struct dwarf_cursor
dwarf_loc_t loc[DWARF_NUM_PRESERVED_REGS];
+ unsigned int stash_frames :1; /* stash frames for fast lookup */
unsigned int use_prev_instr :1; /* use previous (= call) or current (= signal) instruction? */
unsigned int pi_valid :1; /* is proc_info valid? */
unsigned int pi_is_dynamic :1; /* proc_info found via dynamic proc info? */
diff --git a/include/libunwind-arm.h b/include/libunwind-arm.h
index 492331e4..5cbfe860 100644
--- a/include/libunwind-arm.h
+++ b/include/libunwind-arm.h
@@ -286,11 +286,21 @@ typedef struct
}
unw_tdep_proc_info_t;
+typedef struct
+ {
+ /* no arm-specific fast trace */
+ }
+unw_tdep_frame_t;
+
#include "libunwind-common.h"
#define unw_tdep_is_fpreg UNW_ARCH_OBJ(is_fpreg)
extern int unw_tdep_is_fpreg (int);
+#define unw_tdep_make_frame_cache(n) (0)
+#define unw_tdep_free_frame_cache(p) do {} while(0)
+#define unw_tdep_trace(cur,addr,n,c) (-UNW_ENOINFO)
+
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
diff --git a/include/libunwind-hppa.h b/include/libunwind-hppa.h
index 74ea70da..e28fcaaf 100644
--- a/include/libunwind-hppa.h
+++ b/include/libunwind-hppa.h
@@ -103,6 +103,12 @@ unw_tdep_save_loc_t;
/* On PA-RISC, we can directly use ucontext_t as the unwind context. */
typedef ucontext_t unw_tdep_context_t;
+typedef struct
+ {
+ /* no hppa-specific fast trace */
+ }
+unw_tdep_frame_t;
+
#define unw_tdep_is_fpreg(r) ((unsigned) ((r) - UNW_HPPA_FR) < 32)
#include "libunwind-dynamic.h"
@@ -118,6 +124,10 @@ unw_tdep_proc_info_t;
#define unw_tdep_getcontext UNW_ARCH_OBJ (getcontext)
extern int unw_tdep_getcontext (unw_tdep_context_t *);
+#define unw_tdep_make_frame_cache(n) (0)
+#define unw_tdep_free_frame_cache(p) do {} while(0)
+#define unw_tdep_trace(cur,addr,n,c) (-UNW_ENOINFO)
+
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
diff --git a/include/libunwind-ia64.h b/include/libunwind-ia64.h
index fb2fbfe5..e4ac85c2 100644
--- a/include/libunwind-ia64.h
+++ b/include/libunwind-ia64.h
@@ -150,6 +150,12 @@ unw_tdep_save_loc_t;
/* On IA-64, we can directly use ucontext_t as the unwind context. */
typedef ucontext_t unw_tdep_context_t;
+typedef struct
+ {
+ /* no ia64-specific fast trace */
+ }
+unw_tdep_frame_t;
+
#define unw_tdep_is_fpreg(r) ((unsigned) ((r) - UNW_IA64_FR) < 128)
#include "libunwind-dynamic.h"
@@ -187,6 +193,10 @@ extern unw_word_t _Uia64_find_dyn_list (unw_addr_space_t, unw_dyn_info_t *,
signal-safe. */
extern int _Uia64_get_kernel_table (unw_dyn_info_t *);
+#define unw_tdep_make_frame_cache(n) (0)
+#define unw_tdep_free_frame_cache(p) do {} while(0)
+#define unw_tdep_trace(cur,addr,n,c) (-UNW_ENOINFO)
+
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
diff --git a/include/libunwind-mips.h b/include/libunwind-mips.h
index 91f70015..cbaa5dd1 100644
--- a/include/libunwind-mips.h
+++ b/include/libunwind-mips.h
@@ -137,6 +137,12 @@ typedef struct
}
unw_tdep_proc_info_t;
+typedef struct
+ {
+ /* no mips-specific fast trace */
+ }
+unw_tdep_frame_t;
+
#include "libunwind-common.h"
/* There is no getcontext() on MIPS. Use a stub version which only saves GP
@@ -148,6 +154,10 @@ extern int unw_tdep_getcontext (ucontext_t *uc);
#define unw_tdep_is_fpreg UNW_ARCH_OBJ(is_fpreg)
extern int unw_tdep_is_fpreg (int);
+#define unw_tdep_make_frame_cache(n) (0)
+#define unw_tdep_free_frame_cache(p) do {} while(0)
+#define unw_tdep_trace(cur,addr,n,c) (-UNW_ENOINFO)
+
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
diff --git a/include/libunwind-ppc32.h b/include/libunwind-ppc32.h
index b40a84ef..b22c2793 100644
--- a/include/libunwind-ppc32.h
+++ b/include/libunwind-ppc32.h
@@ -195,11 +195,21 @@ typedef struct
}
unw_tdep_proc_info_t;
+typedef struct
+ {
+ /* no ppc32-specific fast trace */
+ }
+unw_tdep_frame_t;
+
#include "libunwind-common.h"
#define unw_tdep_is_fpreg UNW_ARCH_OBJ(is_fpreg)
extern int unw_tdep_is_fpreg (int);
+#define unw_tdep_make_frame_cache(n) (0)
+#define unw_tdep_free_frame_cache(p) do {} while(0)
+#define unw_tdep_trace(cur,addr,n,c) (-UNW_ENOINFO)
+
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
diff --git a/include/libunwind-ppc64.h b/include/libunwind-ppc64.h
index 66420b33..b1c2b072 100644
--- a/include/libunwind-ppc64.h
+++ b/include/libunwind-ppc64.h
@@ -252,11 +252,21 @@ typedef struct
}
unw_tdep_proc_info_t;
+typedef struct
+ {
+ /* no ppc64-specific fast trace */
+ }
+unw_tdep_frame_t;
+
#include "libunwind-common.h"
#define unw_tdep_is_fpreg UNW_ARCH_OBJ(is_fpreg)
extern int unw_tdep_is_fpreg (int);
+#define unw_tdep_make_frame_cache(n) (0)
+#define unw_tdep_free_frame_cache(p) do {} while(0)
+#define unw_tdep_trace(cur,addr,n,c) (-UNW_ENOINFO)
+
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
diff --git a/include/libunwind-x86.h b/include/libunwind-x86.h
index 32533df9..e1f2ba4d 100644
--- a/include/libunwind-x86.h
+++ b/include/libunwind-x86.h
@@ -172,6 +172,12 @@ typedef struct
}
unw_tdep_proc_info_t;
+typedef struct
+ {
+ /* no x86-specific fast trace */
+ }
+unw_tdep_frame_t;
+
#include "libunwind-common.h"
#define unw_tdep_getcontext UNW_ARCH_OBJ(getcontext)
@@ -180,6 +186,10 @@ extern int unw_tdep_getcontext (unw_tdep_context_t *);
#define unw_tdep_is_fpreg UNW_ARCH_OBJ(is_fpreg)
extern int unw_tdep_is_fpreg (int);
+#define unw_tdep_make_frame_cache(n) (0)
+#define unw_tdep_free_frame_cache(p) do {} while(0)
+#define unw_tdep_trace(cur,addr,n,c) (-UNW_ENOINFO)
+
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
diff --git a/include/libunwind-x86_64.h b/include/libunwind-x86_64.h
index 53789cc3..cdde5579 100644
--- a/include/libunwind-x86_64.h
+++ b/include/libunwind-x86_64.h
@@ -104,15 +104,44 @@ typedef struct
}
unw_tdep_proc_info_t;
+typedef enum
+ {
+ UNW_X86_64_FRAME_STANDARD = -2, /* regular rbp, rsp +/- offset */
+ UNW_X86_64_FRAME_SIGRETURN = -1, /* special sigreturn frame */
+ UNW_X86_64_FRAME_OTHER = 0, /* not cacheable (special or unrecognised) */
+ UNW_X86_64_FRAME_GUESSED = 1 /* guessed it was regular, but not known */
+ }
+unw_tdep_frame_type_t;
+
+typedef struct
+ {
+ uint64_t virtual_address;
+ int64_t frame_type : 2; /* unw_tdep_frame_type_t classification */
+ int64_t last_frame : 1; /* non-zero if last frame in chain */
+ int64_t cfa_reg_rsp : 1; /* cfa dwarf base register is rsp vs. rbp */
+ int64_t cfa_reg_offset : 30; /* cfa is at this offset from base register value */
+ int64_t rbp_cfa_offset : 15; /* rbp saved at this offset from cfa (-1 = not saved) */
+ int64_t rsp_cfa_offset : 15; /* rsp saved at this offset from cfa (-1 = not saved) */
+ }
+unw_tdep_frame_t;
+
#include "libunwind-dynamic.h"
#include "libunwind-common.h"
#define unw_tdep_getcontext UNW_ARCH_OBJ(getcontext)
-extern int unw_tdep_getcontext (unw_tdep_context_t *);
-
#define unw_tdep_is_fpreg UNW_ARCH_OBJ(is_fpreg)
+#define unw_tdep_make_frame_cache UNW_OBJ(make_frame_cache)
+#define unw_tdep_free_frame_cache UNW_OBJ(free_frame_cache)
+#define unw_tdep_trace UNW_OBJ(trace)
+
+extern int unw_tdep_getcontext (unw_tdep_context_t *);
extern int unw_tdep_is_fpreg (int);
+extern unw_tdep_frame_t *unw_tdep_make_frame_cache (size_t n);
+extern int unw_tdep_free_frame_cache (unw_tdep_frame_t *p);
+extern int unw_tdep_trace (unw_cursor_t *cursor, void **addresses,
+ int *n, unw_tdep_frame_t *cache);
+
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
diff --git a/include/tdep-arm/libunwind_i.h b/include/tdep-arm/libunwind_i.h
index acaf6d71..0cb4a719 100644
--- a/include/tdep-arm/libunwind_i.h
+++ b/include/tdep-arm/libunwind_i.h
@@ -221,6 +221,7 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val)
#define tdep_fetch_frame(c,ip,n) do {} while(0)
#define tdep_cache_frame(c,rs) do {} while(0)
#define tdep_reuse_frame(c,rs) do {} while(0)
+#define tdep_stash_frame(c,rs) do {} while(0)
#ifdef UNW_LOCAL_ONLY
# define tdep_find_proc_info(c,ip,n) \
diff --git a/include/tdep-hppa/libunwind_i.h b/include/tdep-hppa/libunwind_i.h
index 50d1aabd..4e6c1c3a 100644
--- a/include/tdep-hppa/libunwind_i.h
+++ b/include/tdep-hppa/libunwind_i.h
@@ -227,6 +227,7 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val)
#define tdep_fetch_frame(c,ip,n) do {} while(0)
#define tdep_cache_frame(c,rs) do {} while(0)
#define tdep_reuse_frame(c,rs) do {} while(0)
+#define tdep_stash_frame(c,rs) do {} while(0)
#ifdef UNW_LOCAL_ONLY
# define tdep_find_proc_info(c,ip,n) \
diff --git a/include/tdep-ia64/libunwind_i.h b/include/tdep-ia64/libunwind_i.h
index 75cc220d..3193a646 100644
--- a/include/tdep-ia64/libunwind_i.h
+++ b/include/tdep-ia64/libunwind_i.h
@@ -223,6 +223,7 @@ struct ia64_global_unwind_state
#define tdep_fetch_frame(c,ip,n) do {} while(0)
#define tdep_cache_frame(c,rs) do {} while(0)
#define tdep_reuse_frame(c,rs) do {} while(0)
+#define tdep_stash_frame(c,rs) do {} while(0)
#define tdep_get_as(c) ((c)->as)
#define tdep_get_as_arg(c) ((c)->as_arg)
#define tdep_get_ip(c) ((c)->ip)
diff --git a/include/tdep-mips/libunwind_i.h b/include/tdep-mips/libunwind_i.h
index 160a6372..2e6fc4fd 100644
--- a/include/tdep-mips/libunwind_i.h
+++ b/include/tdep-mips/libunwind_i.h
@@ -282,6 +282,7 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val)
#define tdep_fetch_frame(c,ip,n) do {} while(0)
#define tdep_cache_frame(c,rs) do {} while(0)
#define tdep_reuse_frame(c,rs) do {} while(0)
+#define tdep_stash_frame(c,rs) do {} while(0)
#ifdef UNW_LOCAL_ONLY
# define tdep_find_proc_info(c,ip,n) \
diff --git a/include/tdep-ppc32/libunwind_i.h b/include/tdep-ppc32/libunwind_i.h
index e775dd64..7db2b393 100644
--- a/include/tdep-ppc32/libunwind_i.h
+++ b/include/tdep-ppc32/libunwind_i.h
@@ -245,8 +245,6 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val)
1, c->as_arg);
}
-
-
#define tdep_needs_initialization UNW_OBJ(needs_initialization)
#define tdep_init UNW_OBJ(init)
/* Platforms that support UNW_INFO_FORMAT_TABLE need to define
@@ -259,6 +257,7 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val)
#define tdep_fetch_frame(c,ip,n) do {} while(0)
#define tdep_cache_frame(c,rs) do {} while(0)
#define tdep_reuse_frame(c,rs) do {} while(0)
+#define tdep_stash_frame(c,rs) do {} while(0)
#define tdep_get_func_addr UNW_OBJ(get_func_addr)
#ifdef UNW_LOCAL_ONLY
diff --git a/include/tdep-ppc64/libunwind_i.h b/include/tdep-ppc64/libunwind_i.h
index bb6c977b..6c46e1a7 100644
--- a/include/tdep-ppc64/libunwind_i.h
+++ b/include/tdep-ppc64/libunwind_i.h
@@ -245,8 +245,6 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val)
1, c->as_arg);
}
-
-
#define tdep_needs_initialization UNW_OBJ(needs_initialization)
#define tdep_init UNW_OBJ(init)
/* Platforms that support UNW_INFO_FORMAT_TABLE need to define
@@ -259,6 +257,7 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val)
#define tdep_fetch_frame(c,ip,n) do {} while(0)
#define tdep_cache_frame(c,rs) do {} while(0)
#define tdep_reuse_frame(c,rs) do {} while(0)
+#define tdep_stash_frame(c,rs) do {} while(0)
#define tdep_get_func_addr UNW_OBJ(get_func_addr)
#ifdef UNW_LOCAL_ONLY
diff --git a/include/tdep-x86/libunwind_i.h b/include/tdep-x86/libunwind_i.h
index bc381e91..88ab2467 100644
--- a/include/tdep-x86/libunwind_i.h
+++ b/include/tdep-x86/libunwind_i.h
@@ -243,6 +243,7 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val)
#define tdep_fetch_frame(c,ip,n) do {} while(0)
#define tdep_cache_frame(c,rs) do {} while(0)
#define tdep_reuse_frame(c,rs) do {} while(0)
+#define tdep_stash_frame(c,rs) do {} while(0)
#ifdef UNW_LOCAL_ONLY
# define tdep_find_proc_info(c,ip,n) \
diff --git a/include/tdep-x86_64/libunwind_i.h b/include/tdep-x86_64/libunwind_i.h
index fa5ebe97..b076bd18 100644
--- a/include/tdep-x86_64/libunwind_i.h
+++ b/include/tdep-x86_64/libunwind_i.h
@@ -57,6 +57,8 @@ struct cursor
{
struct dwarf_cursor dwarf; /* must be first */
+ unw_tdep_frame_t frame_info; /* quick tracing assist info */
+
/* Format of sigcontext structure and address at which it is
stored: */
enum
@@ -173,6 +175,7 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val)
# define tdep_cache_frame(c,rs) do {} while(0)
# define tdep_reuse_frame(c,rs) do {} while(0)
#endif
+#define tdep_stash_frame UNW_OBJ(stash_frame)
#ifdef UNW_LOCAL_ONLY
# define tdep_find_proc_info(c,ip,n) \
@@ -215,7 +218,8 @@ extern void tdep_cache_frame (struct dwarf_cursor *c,
struct dwarf_reg_state *rs);
extern void tdep_reuse_frame (struct dwarf_cursor *c,
struct dwarf_reg_state *rs);
+extern void tdep_stash_frame (struct dwarf_cursor *c,
+ struct dwarf_reg_state *rs);
#endif
-
#endif /* X86_64_LIBUNWIND_I_H */
diff --git a/src/Makefile.am b/src/Makefile.am
index 68470eab..9703724e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -258,8 +258,8 @@ libunwind_la_SOURCES_x86_64 = $(libunwind_la_SOURCES_x86_64_common) \
x86_64/setcontext.S \
x86_64/Lcreate_addr_space.c x86_64/Lget_save_loc.c x86_64/Lglobal.c \
x86_64/Linit.c x86_64/Linit_local.c x86_64/Linit_remote.c \
- x86_64/Lget_proc_info.c x86_64/Lregs.c \
- x86_64/Lresume.c x86_64/Lstep.c x86_64/getcontext.S
+ x86_64/Lget_proc_info.c x86_64/Lregs.c x86_64/Lresume.c \
+ x86_64/Lstash_frame.c x86_64/Lstep.c x86_64/Ltrace.c x86_64/getcontext.S
# The list of files that go into libunwind-x86_64:
libunwind_x86_64_la_SOURCES_x86_64 = $(libunwind_la_SOURCES_x86_64_common) \
@@ -267,8 +267,8 @@ libunwind_x86_64_la_SOURCES_x86_64 = $(libunwind_la_SOURCES_x86_64_common) \
$(libunwind_la_SOURCES_generic) \
x86_64/Gcreate_addr_space.c x86_64/Gget_save_loc.c x86_64/Gglobal.c \
x86_64/Ginit.c x86_64/Ginit_local.c x86_64/Ginit_remote.c \
- x86_64/Gget_proc_info.c x86_64/Gregs.c \
- x86_64/Gresume.c x86_64/Gstep.c
+ x86_64/Gget_proc_info.c x86_64/Gregs.c x86_64/Gresume.c \
+ x86_64/Gstash_frame.c x86_64/Gstep.c x86_64/Gtrace.c
# The list of local files that go to Power 64 and 32:
libunwind_la_SOURCES_ppc = ppc/Lcreate_addr_space.c \
diff --git a/src/arm/init.h b/src/arm/init.h
index a3025698..1f8d7c11 100644
--- a/src/arm/init.h
+++ b/src/arm/init.h
@@ -62,6 +62,7 @@ common_init (struct cursor *c, unsigned use_prev_instr)
c->dwarf.args_size = 0;
c->dwarf.ret_addr_column = 0;
+ c->dwarf.stash_frames = 0;
c->dwarf.use_prev_instr = use_prev_instr;
c->dwarf.pi_valid = 0;
c->dwarf.pi_is_dynamic = 0;
diff --git a/src/dwarf/Gparser.c b/src/dwarf/Gparser.c
index 13bd9a27..1db15461 100644
--- a/src/dwarf/Gparser.c
+++ b/src/dwarf/Gparser.c
@@ -803,6 +803,10 @@ apply_reg_state (struct dwarf_cursor *c, struct dwarf_reg_state *rs)
__FUNCTION__, (long) c->ip);
return -UNW_EBADFRAME;
}
+
+ if (c->stash_frames)
+ tdep_stash_frame (c, rs);
+
return 0;
}
diff --git a/src/hppa/init.h b/src/hppa/init.h
index d14354f6..6fabb154 100644
--- a/src/hppa/init.h
+++ b/src/hppa/init.h
@@ -41,6 +41,7 @@ common_init (struct cursor *c, unsigned use_prev_instr)
if (ret < 0)
return ret;
+ c->dwarf.stash_frames = 0;
c->dwarf.use_prev_instr = use_prev_instr;
return 0;
}
diff --git a/src/mips/init.h b/src/mips/init.h
index e32e3c9e..3a4bb008 100644
--- a/src/mips/init.h
+++ b/src/mips/init.h
@@ -47,6 +47,7 @@ common_init (struct cursor *c, unsigned use_prev_instr)
c->dwarf.args_size = 0;
c->dwarf.ret_addr_column = 0;
+ c->dwarf.stash_frames = 0;
c->dwarf.use_prev_instr = use_prev_instr;
c->dwarf.pi_valid = 0;
c->dwarf.pi_is_dynamic = 0;
diff --git a/src/ppc32/init.h b/src/ppc32/init.h
index c2208ad9..4cb91920 100644
--- a/src/ppc32/init.h
+++ b/src/ppc32/init.h
@@ -62,6 +62,7 @@ common_init_ppc32 (struct cursor *c, unsigned use_prev_instr)
c->dwarf.args_size = 0;
c->dwarf.ret_addr_column = 0;
+ c->dwarf.stash_frames = 0;
c->dwarf.use_prev_instr = use_prev_instr;
c->dwarf.pi_valid = 0;
c->dwarf.pi_is_dynamic = 0;
diff --git a/src/ppc64/init.h b/src/ppc64/init.h
index 64847b84..7503a7c7 100644
--- a/src/ppc64/init.h
+++ b/src/ppc64/init.h
@@ -72,6 +72,7 @@ common_init_ppc64 (struct cursor *c, unsigned use_prev_instr)
c->dwarf.args_size = 0;
c->dwarf.ret_addr_column = 0;
+ c->dwarf.stash_frames = 0;
c->dwarf.use_prev_instr = use_prev_instr;
c->dwarf.pi_valid = 0;
c->dwarf.pi_is_dynamic = 0;
diff --git a/src/x86/init.h b/src/x86/init.h
index b59ad842..f35387d4 100644
--- a/src/x86/init.h
+++ b/src/x86/init.h
@@ -59,6 +59,7 @@ common_init (struct cursor *c, unsigned use_prev_instr)
c->dwarf.args_size = 0;
c->dwarf.ret_addr_column = 0;
+ c->dwarf.stash_frames = 0;
c->dwarf.use_prev_instr = use_prev_instr;
c->dwarf.pi_valid = 0;
c->dwarf.pi_is_dynamic = 0;
diff --git a/src/x86_64/Ginit_local.c b/src/x86_64/Ginit_local.c
index 18b3d989..70bef3e1 100644
--- a/src/x86_64/Ginit_local.c
+++ b/src/x86_64/Ginit_local.c
@@ -51,11 +51,7 @@ unw_init_local (unw_cursor_t *cursor, ucontext_t *uc)
c->dwarf.as = unw_local_addr_space;
c->dwarf.as_arg = c;
c->uc = uc;
-#if CONSERVATIVE_CHECKS
- c->validate = 1;
-#else
c->validate = 0;
-#endif
return common_init (c, 1);
}
diff --git a/src/x86_64/Gos-linux.c b/src/x86_64/Gos-linux.c
index b7f832ca..c0278881 100644
--- a/src/x86_64/Gos-linux.c
+++ b/src/x86_64/Gos-linux.c
@@ -39,18 +39,12 @@ tdep_fetch_frame (struct dwarf_cursor *dw, unw_word_t ip, int need_unwind_info)
if (dw->pi_valid
&& dw->pi.unwind_info
&& ((struct dwarf_cie_info *) dw->pi.unwind_info)->signal_frame)
- {
c->sigcontext_format = X86_64_SCF_LINUX_RT_SIGFRAME;
- c->sigcontext_addr = dw->cfa;
- }
else
- {
c->sigcontext_format = X86_64_SCF_NONE;
- c->sigcontext_addr = 0;
- }
- Debug(15, "fetch frame ip=0x%lx cfa=0x%lx format=%d addr=0x%lx\n",
- dw->ip, dw->cfa, c->sigcontext_format, c->sigcontext_addr);
+ Debug(5, "fetch frame ip=0x%lx cfa=0x%lx format=%d\n",
+ dw->ip, dw->cfa, c->sigcontext_format);
}
HIDDEN void
@@ -59,8 +53,8 @@ tdep_cache_frame (struct dwarf_cursor *dw, struct dwarf_reg_state *rs)
struct cursor *c = (struct cursor *) dw;
rs->signal_frame = c->sigcontext_format;
- Debug(15, "cache frame ip=0x%lx cfa=0x%lx format=%d addr=0x%lx\n",
- dw->ip, dw->cfa, c->sigcontext_format, c->sigcontext_addr);
+ Debug(5, "cache frame ip=0x%lx cfa=0x%lx format=%d\n",
+ dw->ip, dw->cfa, c->sigcontext_format);
}
HIDDEN void
@@ -69,12 +63,20 @@ tdep_reuse_frame (struct dwarf_cursor *dw, struct dwarf_reg_state *rs)
struct cursor *c = (struct cursor *) dw;
c->sigcontext_format = rs->signal_frame;
if (c->sigcontext_format == X86_64_SCF_LINUX_RT_SIGFRAME)
+ {
+ /* Rest will be filled by tdep_stash_frame(), save what it needs. */
+ c->frame_info.frame_type = UNW_X86_64_FRAME_SIGRETURN;
+ c->frame_info.cfa_reg_offset = -1;
+ c->frame_info.cfa_reg_rsp = -1;
c->sigcontext_addr = dw->cfa;
+ }
else
c->sigcontext_addr = 0;
- Debug(15, "reuse frame ip=0x%lx cfa=0x%lx format=%d addr=0x%lx\n",
- dw->ip, dw->cfa, c->sigcontext_format, c->sigcontext_addr);
+ Debug(5, "reuse frame ip=0x%lx cfa=0x%lx format=%d addr=0x%lx offset=%+d\n",
+ dw->ip, dw->cfa, c->sigcontext_format, c->sigcontext_addr,
+ (c->sigcontext_format == X86_64_SCF_LINUX_RT_SIGFRAME
+ ? c->frame_info.cfa_reg_offset : 0));
}
PROTECTED int
@@ -90,17 +92,14 @@ unw_handle_signal_frame (unw_cursor_t *cursor)
#if UNW_DEBUG /* To silence compiler warnings */
/* Should not get here because we now use kernel-provided dwarf
information for the signal trampoline and dwarf_step() works.
- Hence dwarf_step() should never call this function. Maybe
+ Hence unw_step() should never call this function. Maybe
restore old non-dwarf signal handling here, but then the
gating on unw_is_signal_frame() needs to be removed. */
struct cursor *c = (struct cursor *) cursor;
Debug(1, "old format signal frame? format=%d addr=0x%lx cfa=0x%lx\n",
c->sigcontext_format, c->sigcontext_addr, c->dwarf.cfa);
- assert(c->sigcontext_format == X86_64_SCF_LINUX_RT_SIGFRAME);
- assert(c->sigcontext_addr == c->dwarf.cfa);
- assert(0);
#endif
- return 1;
+ return -UNW_EBADFRAME;
}
#ifndef UNW_REMOTE_ONLY
diff --git a/src/x86_64/Gstash_frame.c b/src/x86_64/Gstash_frame.c
new file mode 100644
index 00000000..8cbc947f
--- /dev/null
+++ b/src/x86_64/Gstash_frame.c
@@ -0,0 +1,95 @@
+/* libunwind - a platform-independent unwind library
+ Copyright (C) 2010 by Lassi Tuura <lat@iki.fi>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+#include "unwind_i.h"
+#include "ucontext_i.h"
+
+HIDDEN void
+tdep_stash_frame (struct dwarf_cursor *d, struct dwarf_reg_state *rs)
+{
+ struct cursor *c = (struct cursor *) dwarf_to_cursor (d);
+ unw_tdep_frame_t *f = &c->frame_info;
+
+ Debug (4, "ip=0x%lx cfa=0x%lx type %d cfa [where=%d val=%ld] cfaoff=%ld"
+ " ra=0x%lx rbp [where=%d val=%ld @0x%lx] rsp [where=%d val=%ld @0x%lx]\n",
+ d->ip, d->cfa, f->frame_type,
+ rs->reg[DWARF_CFA_REG_COLUMN].where,
+ rs->reg[DWARF_CFA_REG_COLUMN].val,
+ rs->reg[DWARF_CFA_OFF_COLUMN].val,
+ DWARF_GET_LOC(d->loc[d->ret_addr_column]),
+ rs->reg[RBP].where, rs->reg[RBP].val, DWARF_GET_LOC(d->loc[RBP]),
+ rs->reg[RSP].where, rs->reg[RSP].val, DWARF_GET_LOC(d->loc[RSP]));
+
+ /* A standard frame is defined as:
+ - CFA is register-relative offset off RBP or RSP;
+ - Return address is saved at CFA-8;
+ - RBP is unsaved or saved at CFA+offset, offset != -1;
+ - RSP is unsaved or saved at CFA+offset, offset != -1. */
+ if (f->frame_type == UNW_X86_64_FRAME_OTHER
+ && (rs->reg[DWARF_CFA_REG_COLUMN].where == DWARF_WHERE_REG)
+ && (rs->reg[DWARF_CFA_REG_COLUMN].val == RBP
+ || rs->reg[DWARF_CFA_REG_COLUMN].val == RSP)
+ && labs(rs->reg[DWARF_CFA_OFF_COLUMN].val) < (1 << 29)
+ && DWARF_GET_LOC(d->loc[d->ret_addr_column]) == d->cfa-8
+ && (rs->reg[RBP].where == DWARF_WHERE_UNDEF
+ || rs->reg[RBP].where == DWARF_WHERE_SAME
+ || (rs->reg[RBP].where == DWARF_WHERE_CFAREL
+ && labs(rs->reg[RBP].val) < (1 << 14)
+ && rs->reg[RBP].val+1 != 0))
+ && (rs->reg[RSP].where == DWARF_WHERE_UNDEF
+ || rs->reg[RSP].where == DWARF_WHERE_SAME
+ || (rs->reg[RSP].where == DWARF_WHERE_CFAREL
+ && labs(rs->reg[RSP].val) < (1 << 14)
+ && rs->reg[RSP].val+1 != 0)))
+ {
+ /* Save information for a standard frame. */
+ f->frame_type = UNW_X86_64_FRAME_STANDARD;
+ f->cfa_reg_rsp = (rs->reg[DWARF_CFA_REG_COLUMN].val == RSP);
+ f->cfa_reg_offset = rs->reg[DWARF_CFA_OFF_COLUMN].val;
+ if (rs->reg[RBP].where == DWARF_WHERE_CFAREL)
+ f->rbp_cfa_offset = rs->reg[RBP].val;
+ if (rs->reg[RSP].where == DWARF_WHERE_CFAREL)
+ f->rsp_cfa_offset = rs->reg[RSP].val;
+ Debug (4, " standard frame\n");
+ }
+
+ /* Signal frame was detected via augmentation in tdep_fetch_frame()
+ and partially filled in tdep_reuse_frame(). Now that we have
+ the delta between inner and outer CFAs available to use, fill in
+ the offsets for CFA and stored registers. We don't have space
+ for RIP, it's location is calculated relative to RBP location. */
+ else if (f->frame_type == UNW_X86_64_FRAME_SIGRETURN)
+ {
+ assert (f->cfa_reg_offset == -1);
+ f->cfa_reg_offset = d->cfa - c->sigcontext_addr;
+ f->rbp_cfa_offset = DWARF_GET_LOC(d->loc[RBP]) - d->cfa;
+ f->rsp_cfa_offset = DWARF_GET_LOC(d->loc[RSP]) - d->cfa;
+ Debug (4, " sigreturn frame rbpoff %d rspoff %d\n",
+ f->rbp_cfa_offset, f->rsp_cfa_offset);
+ }
+
+ /* PLT and guessed RBP-walked frames are handled in unw_step(). */
+ else
+ Debug (4, " unusual frame\n");
+}
diff --git a/src/x86_64/Gstep.c b/src/x86_64/Gstep.c
index 85e3989a..0d2eef80 100644
--- a/src/x86_64/Gstep.c
+++ b/src/x86_64/Gstep.c
@@ -58,6 +58,11 @@ unw_step (unw_cursor_t *cursor)
struct cursor *c = (struct cursor *) cursor;
int ret, i;
+#if CONSERVATIVE_CHECKS
+ int val = c->validate;
+ c->validate = 1;
+#endif
+
Debug (1, "(cursor=%p, ip=0x%016lx, cfa=0x%016lx)\n",
c, c->dwarf.ip, c->dwarf.cfa);
@@ -65,6 +70,10 @@ unw_step (unw_cursor_t *cursor)
c->sigcontext_format = X86_64_SCF_NONE;
ret = dwarf_step (&c->dwarf);
+#if CONSERVATIVE_CHECKS
+ c->validate = val;
+#endif
+
if (ret < 0 && ret != -UNW_ENOINFO)
{
Debug (2, "returning %d\n", ret);
@@ -112,7 +121,11 @@ unw_step (unw_cursor_t *cursor)
}
else if (is_plt_entry (&c->dwarf))
{
+ /* Like regular frame, CFA = RSP+8, RA = [CFA-8], no regs saved. */
Debug (2, "found plt entry\n");
+ c->frame_info.cfa_reg_offset = 8;
+ c->frame_info.cfa_reg_rsp = -1;
+ c->frame_info.frame_type = UNW_X86_64_FRAME_STANDARD;
c->dwarf.loc[RIP] = DWARF_LOC (c->dwarf.cfa, 0);
c->dwarf.cfa += 8;
}
@@ -142,19 +155,32 @@ unw_step (unw_cursor_t *cursor)
}
else
{
- unw_word_t rbp1;
- Debug (1, "[RBP=0x%Lx] = 0x%Lx (cfa = 0x%Lx)\n",
- (unsigned long long) DWARF_GET_LOC (c->dwarf.loc[RBP]),
- (unsigned long long) rbp,
- (unsigned long long) c->dwarf.cfa);
-
+ unw_word_t rbp1 = 0;
rbp_loc = DWARF_LOC(rbp, 0);
rsp_loc = DWARF_NULL_LOC;
rip_loc = DWARF_LOC (rbp + 8, 0);
- /* Heuristic to recognize a bogus frame pointer */
ret = dwarf_get (&c->dwarf, rbp_loc, &rbp1);
- if (ret || ((rbp1 - rbp) > 0x4000))
- rbp_loc = DWARF_NULL_LOC;
+ Debug (1, "[RBP=0x%lx] = 0x%lx (cfa = 0x%lx) -> 0x%lx\n",
+ (unsigned long) DWARF_GET_LOC (c->dwarf.loc[RBP]),
+ rbp, c->dwarf.cfa, rbp1);
+
+ /* Heuristic to determine incorrect guess. For RBP to be a
+ valid frame it needs to be above current CFA, but don't
+ let it go more than a little. Note that we can't deduce
+ anything about new RBP (rbp1) since it may not be a frame
+ pointer in the frame above. Just check we get the value. */
+ if (ret < 0
+ || rbp <= c->dwarf.cfa
+ || (rbp - c->dwarf.cfa) > 0x4000)
+ {
+ rip_loc = DWARF_NULL_LOC;
+ rbp_loc = DWARF_NULL_LOC;
+ }
+
+ c->frame_info.frame_type = UNW_X86_64_FRAME_GUESSED;
+ c->frame_info.cfa_reg_rsp = 0;
+ c->frame_info.cfa_reg_offset = 16;
+ c->frame_info.rbp_cfa_offset = -16;
c->dwarf.cfa += 16;
}
diff --git a/src/x86_64/Gtrace.c b/src/x86_64/Gtrace.c
new file mode 100644
index 00000000..24087443
--- /dev/null
+++ b/src/x86_64/Gtrace.c
@@ -0,0 +1,401 @@
+/* libunwind - a platform-independent unwind library
+ Copyright (C) 2010 by Lassi Tuura <lat@iki.fi>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+#include "unwind_i.h"
+#include "ucontext_i.h"
+#include <signal.h>
+
+/* Utility for timing in debug mode. You'll probably want to
+ comment out all unnecessary debugging in this file if you
+ use this, otherwise the timings printed will not make sense. */
+#if UNW_DEBUG
+#define rdtsc(v) \
+ do { unsigned lo, hi; \
+ __asm__ volatile ("rdtsc" : "=a" (lo), "=d" (hi)); \
+ (v) = ((unsigned long) lo) | ((unsigned long) hi << 32); \
+ } while (0)
+#endif
+
+/* There's not enough space to store RIP's location in a signal
+ frame, but we can calculate it relative to RBP's (or RSP's)
+ position in mcontext structure. Note we don't want to use
+ the UC_MCONTEXT_GREGS_* directly since we rely on DWARF info. */
+#define dRIP (UC_MCONTEXT_GREGS_RIP - UC_MCONTEXT_GREGS_RBP)
+
+/* Allocate and initialise hash table for frame cache lookups.
+ Client requests size N, which should be 5 to 10 more than expected
+ number of unique addresses to trace. Minimum size of 10000 is
+ forced. Returns the cache, or NULL if there was a memory
+ allocation problem. */
+unw_tdep_frame_t *
+unw_tdep_make_frame_cache (size_t n)
+{
+ size_t i;
+ unw_tdep_frame_t *cache;
+
+ if (n < 10000)
+ n = 10000;
+
+ if (! (cache = malloc((n+1) * sizeof(unw_tdep_frame_t))))
+ return 0;
+
+ unw_tdep_frame_t empty = { 0, UNW_X86_64_FRAME_OTHER, -1, -1, 0, -1, -1 };
+ for (i = 0; i < n; ++i)
+ cache[i] = empty;
+
+ cache[0].virtual_address = n;
+ return cache+1;
+}
+
+/* Free the address cache allocated by unw_tdep_make_frame_cache().
+ Returns 0 on success, or -UNW_EINVAL if cache was NULL. */
+int
+unw_tdep_free_frame_cache (unw_tdep_frame_t *cache)
+{
+ if (! cache)
+ return -UNW_EINVAL;
+
+ free(cache-1);
+ return 0;
+}
+
+/* Initialise frame properties for address cache slot F at address
+ RIP using current CFA, RBP and RSP values. Modifies CURSOR to
+ that location, performs one unw_step(), and fills F with what
+ was discovered about the location. Returns F.
+
+ FIXME: This probably should tell DWARF handling to never evaluate
+ or use registers other than RBP, RSP and RIP in case there is
+ highly unusual unwind info which uses these creatively. */
+static unw_tdep_frame_t *
+trace_init_addr (unw_tdep_frame_t *f,
+ unw_cursor_t *cursor,
+ unw_word_t cfa,
+ unw_word_t rip,
+ unw_word_t rbp,
+ unw_word_t rsp)
+{
+ struct cursor *c = (struct cursor *) cursor;
+ struct dwarf_cursor *d = &c->dwarf;
+ int ret = -UNW_EINVAL;
+
+ /* Initialise frame properties: unknown, not last. */
+ f->virtual_address = rip;
+ f->frame_type = UNW_X86_64_FRAME_OTHER;
+ f->last_frame = 0;
+ f->cfa_reg_rsp = -1;
+ f->cfa_reg_offset = 0;
+ f->rbp_cfa_offset = -1;
+ f->rsp_cfa_offset = -1;
+
+ /* Reinitialise cursor to this instruction - but undo next/prev RIP
+ adjustment because unw_step will redo it - and force RIP, RBP
+ RSP into register locations (=~ ucontext we keep), then set
+ their desired values. Then perform the step. */
+ d->ip = rip + d->use_prev_instr;
+ d->cfa = cfa;
+ d->loc[UNW_X86_64_RIP] = DWARF_REG_LOC (d, UNW_X86_64_RIP);
+ d->loc[UNW_X86_64_RBP] = DWARF_REG_LOC (d, UNW_X86_64_RBP);
+ d->loc[UNW_X86_64_RSP] = DWARF_REG_LOC (d, UNW_X86_64_RSP);
+ c->frame_info = *f;
+
+ if (dwarf_put (d, d->loc[UNW_X86_64_RIP], rip) >= 0
+ && dwarf_put (d, d->loc[UNW_X86_64_RBP], rbp) >= 0
+ && dwarf_put (d, d->loc[UNW_X86_64_RSP], rsp) >= 0
+ && (ret = unw_step (cursor)) >= 0)
+ *f = c->frame_info;
+
+ /* If unw_step() stopped voluntarily, remember that, even if it
+ otherwise could not determine anything useful. This avoids
+ failing trace if we hit frames without unwind info, which is
+ common for the outermost frame (CRT stuff) on many systems.
+ This avoids failing trace in very common circumstances; failing
+ to unw_step() loop wouldn't produce any better result. */
+ if (ret == 0)
+ f->last_frame = -1;
+
+ Debug (3, "frame va %lx type %d last %d cfa %s+%d rbp @ cfa%+d rsp @ cfa%+d\n",
+ f->virtual_address, f->frame_type, f->last_frame,
+ f->cfa_reg_rsp ? "rsp" : "rbp", f->cfa_reg_offset,
+ f->rbp_cfa_offset, f->rsp_cfa_offset);
+
+ return f;
+}
+
+/* Look up and if necessary fill in frame attributes for address RIP
+ in CACHE using current CFA, RBP and RSP values. Uses CURSOR to
+ perform any unwind steps necessary to fill the cache. Returns the
+ frame cache slot which describes RIP. */
+static unw_tdep_frame_t *
+trace_lookup (unw_cursor_t *cursor,
+ unw_tdep_frame_t *cache,
+ unw_word_t cfa,
+ unw_word_t rip,
+ unw_word_t rbp,
+ unw_word_t rsp)
+{
+ /* First look up for previously cached information using cache as
+ linear probing hash table with probe step of 1. Majority of
+ lookups should be completed within few steps, but it is very
+ important the hash table does not fill up, or performance falls
+ off the cliff. */
+ uint64_t cache_size = cache[-1].virtual_address;
+ uint64_t probe_steps = (cache_size >> 5);
+ uint64_t slot = ((rip * 0x9e3779b97f4a7c16) >> 43) % cache_size;
+ uint64_t i;
+
+ for (i = 0; i < probe_steps; ++i)
+ {
+ uint64_t addr = cache[slot].virtual_address;
+
+ /* Return if we found the address. */
+ if (addr == rip)
+ {
+ Debug (4, "found address after %ld steps\n", i);
+ return &cache[slot];
+ }
+
+ /* If slot is empty, reuse it. */
+ if (! addr)
+ break;
+
+ /* Linear probe to next slot candidate, step = 1. */
+ if (++slot > cache_size)
+ slot -= cache_size;
+ }
+
+ /* Fill this slot, whether it's free or hash collision. */
+ Debug (4, "updating slot after %ld steps\n", i);
+ return trace_init_addr (&cache[slot], cursor, cfa, rip, rbp, rsp);
+}
+
+/* Fast stack backtrace for x86-64.
+
+ Intended for use when the application makes frequent queries to the
+ current call stack without any desire to unwind. Somewhat like the
+ GLIBC backtrace() function: fills BUFFER with the call tree from
+ CURSOR upwards, and SIZE with the number of stack levels so found.
+ When called, SIZE should tell the maximum number of entries that
+ can be stored in BUFFER. CACHE is used to accelerate the stack
+ queries; no other thread may use the same cache concurrently.
+
+ The caller should fall back to a unw_step() loop if this function
+ fails by returning -UNW_ESTOPUNWIND, meaning the routine hit a
+ stack frame that is too complex to be traced in the fast path.
+
+ This function is tuned for clients which only need to walk the
+ stack to get the call tree as fast as possible but without any
+ other details, for example profilers sampling the stack thousands
+ to millions of times per second. The routine handles the most
+ common x86-64 ABI stack layouts: CFA is RBP or RSP plus/minus
+ constant offset, return address is at CFA-8, and RBP and RSP are
+ either unchanged or saved on stack at constant offset from the CFA;
+ the signal return frame; and frames without unwind info provided
+ they are at the outermost (final) frame or can conservatively be
+ assumed to be frame-pointer based.
+
+ Any other stack layout will cause the routine to give up. There
+ are only a handful of relatively rarely used functions which do
+ not have a stack in the standard form: vfork, longjmp, setcontext
+ and _dl_runtime_profile on common linux systems for example.
+
+ On success BUFFER and *SIZE reflect the trace progress up to *SIZE
+ stack levels or the outermost frame, which ever is less. It may
+ stop short of outermost frame if unw_step() loop would also do so,
+ e.g. if there is no more unwind information; this is not reported
+ as an error.
+
+ The function returns a negative value for errors, -UNW_ESTOPUNWIND
+ if tracing stopped because of an unusual frame unwind info. The
+ BUFFER and *SIZE reflect tracing progress up to the error frame.
+
+ Callers of this function would normally look like this:
+
+ unw_cursor_t cur;
+ unw_context_t ctx, saved;
+ unw_tdep_frame_t *cache = ...;
+ void addrs[128];
+ int depth = 128;
+ int ret;
+
+ unw_getcontext(&ctx);
+ memcpy(&saved, &ctx, sizeof(ctx));
+
+ unw_init_local(&cur, &ctx);
+ if (! cache || (ret = unw_tdep_trace(&cur, addrs, &depth, cache)) < 0)
+ {
+ depth = 0;
+ unw_init_local(&cur, &saved);
+ while (depth < 128)
+ {
+ unw_word_t ip;
+ unw_get_reg(&cur, UNW_REG_IP, &ip);
+ addresses[depth++] = (void *) ip;
+ if ((ret = unw_step(&cur)) <= 0)
+ break;
+ }
+ }
+*/
+int
+unw_tdep_trace (unw_cursor_t *cursor,
+ void **buffer,
+ int *size,
+ unw_tdep_frame_t *cache)
+{
+ struct cursor *c = (struct cursor *) cursor;
+ struct dwarf_cursor *d = &c->dwarf;
+ unw_word_t rbp, rsp, rip, cfa;
+ int maxdepth = 0;
+ int depth = 0;
+ int ret;
+#if UNW_DEBUG
+ unsigned long start, end;
+ rdtsc(start);
+#endif
+
+ /* Check input parametres. */
+ if (! cursor || ! buffer || ! size || ! cache || (maxdepth = *size) <= 0)
+ return -UNW_EINVAL;
+
+ Debug (1, "begin ip 0x%lx cfa 0x%lx\n", d->ip, d->cfa);
+
+ /* Tell core dwarf routines to call back to us. */
+ d->stash_frames = 1;
+
+ /* Determine initial register values. */
+ rip = d->ip;
+ rsp = cfa = d->cfa;
+ if ((ret = dwarf_get (d, d->loc[UNW_X86_64_RBP], &rbp)) < 0)
+ {
+ *size = 0;
+ return ret;
+ }
+
+ /* Trace the stack upwards, starting from current RIP. Adjust
+ the RIP address for previous/next instruction as the main
+ unwinding logic would also do. We undo this before calling
+ back into unw_step(). */
+ while (depth < maxdepth)
+ {
+ rip -= d->use_prev_instr;
+ Debug (2, "depth %d cfa 0x%lx rip 0x%lx rsp 0x%lx rbp 0x%lx\n",
+ depth, cfa, rip, rsp, rbp);
+
+ /* See if we have this address cached. If not, evaluate enough of
+ the dwarf unwind information to fill the cache line data, or to
+ decide this frame cannot be handled in fast trace mode. We
+ cache negative results too to prevent unnecessary dwarf parsing
+ for common failures. */
+ unw_tdep_frame_t *f = trace_lookup (cursor, cache, cfa, rip, rbp, rsp);
+
+ /* Record this address in stack trace. */
+ buffer[depth++] = (void *) rip;
+
+ /* If we don't have information for this frame, give up. */
+ if (! f)
+ {
+ ret = -UNW_ENOINFO;
+ break;
+ }
+
+ Debug (3, "frame va %lx type %d last %d cfa %s+%d rbp @ cfa%+d rsp @ cfa%+d\n",
+ f->virtual_address, f->frame_type, f->last_frame,
+ f->cfa_reg_rsp ? "rsp" : "rbp", f->cfa_reg_offset,
+ f->rbp_cfa_offset, f->rsp_cfa_offset);
+
+ assert (f->virtual_address == rip);
+
+ /* Stop if this was the last frame. In particular don't evaluate
+ new register values as it may not be safe - we don't normally
+ run with full validation on, and do not want to - and there's
+ enough bad unwind info floating around that we need to trust
+ what unw_step() previously said, in potentially bogus frames. */
+ if (f->last_frame)
+ break;
+
+ /* Evaluate CFA and registers for the next frame. */
+ switch (f->frame_type)
+ {
+ case UNW_X86_64_FRAME_GUESSED:
+ /* Fall thru to standard processing after forcing validation. */
+ c->validate = 1;
+
+ case UNW_X86_64_FRAME_STANDARD:
+ /* Advance standard traceable frame. */
+ cfa = (f->cfa_reg_rsp ? rsp : rbp) + f->cfa_reg_offset;
+ ret = dwarf_get (d, DWARF_MEM_LOC (d, cfa - 8), &rip);
+ if (ret >= 0 && f->rbp_cfa_offset != -1)
+ ret = dwarf_get (d, DWARF_MEM_LOC (d, cfa + f->rbp_cfa_offset), &rbp);
+
+ /* Don't bother reading RSP from DWARF, CFA becomes new RSP. */
+ rsp = cfa;
+
+ /* Next frame needs to back up for unwind info lookup. */
+ d->use_prev_instr = 1;
+ break;
+
+ case UNW_X86_64_FRAME_SIGRETURN:
+ /* Advance standard signal frame, whose CFA points above saved
+ registers (ucontext) among other things. We know the info
+ is stored at some unknown constant offset off inner frame's
+ CFA. We determine the actual offset from DWARF unwind info. */
+ d->use_prev_instr = 0;
+ cfa = cfa + f->cfa_reg_offset;
+ ret = dwarf_get (d, DWARF_MEM_LOC (d, cfa + f->rbp_cfa_offset + dRIP), &rip);
+ if (ret >= 0)
+ ret = dwarf_get (d, DWARF_MEM_LOC (d, cfa + f->rbp_cfa_offset), &rbp);
+ if (ret >= 0)
+ ret = dwarf_get (d, DWARF_MEM_LOC (d, cfa + f->rsp_cfa_offset), &rsp);
+
+ /* Resume stack at signal restoration point. The stack is not
+ necessarily continuous here, especially with sigaltstack(). */
+ cfa = rsp;
+
+ /* Next frame should not back up. */
+ d->use_prev_instr = 0;
+ break;
+
+ default:
+ /* We cannot trace through this frame, give up and tell the
+ caller we had to stop. Data collected so far may still be
+ useful to the caller, so let it know how far we got. */
+ ret = -UNW_ESTOPUNWIND;
+ break;
+ }
+
+ Debug (4, "new cfa 0x%lx rip 0x%lx rsp 0x%lx rbp 0x%lx\n",
+ cfa, rip, rsp, rbp);
+
+ /* If we failed on ended up somewhere bogus, stop. */
+ if (ret < 0 || rip < 0x4000)
+ break;
+ }
+
+#if UNW_DEBUG
+ rdtsc(end);
+ Debug (1, "returning %d depth %d, dt=%ld\n", ret, depth, end - start);
+#endif
+ *size = depth;
+ return ret;
+}
diff --git a/src/x86_64/Lstash_frame.c b/src/x86_64/Lstash_frame.c
new file mode 100644
index 00000000..77587803
--- /dev/null
+++ b/src/x86_64/Lstash_frame.c
@@ -0,0 +1,5 @@
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY)
+#include "Gstash_frame.c"
+#endif
diff --git a/src/x86_64/Ltrace.c b/src/x86_64/Ltrace.c
new file mode 100644
index 00000000..fcd3f239
--- /dev/null
+++ b/src/x86_64/Ltrace.c
@@ -0,0 +1,5 @@
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY)
+#include "Gtrace.c"
+#endif
diff --git a/src/x86_64/init.h b/src/x86_64/init.h
index dcd4aea2..f04ecda3 100644
--- a/src/x86_64/init.h
+++ b/src/x86_64/init.h
@@ -64,6 +64,7 @@ common_init (struct cursor *c, unsigned use_prev_instr)
c->dwarf.args_size = 0;
c->dwarf.ret_addr_column = RIP;
+ c->dwarf.stash_frames = 0;
c->dwarf.use_prev_instr = use_prev_instr;
c->dwarf.pi_valid = 0;
c->dwarf.pi_is_dynamic = 0;
diff --git a/tests/Gtest-trace.c b/tests/Gtest-trace.c
new file mode 100644
index 00000000..90452be6
--- /dev/null
+++ b/tests/Gtest-trace.c
@@ -0,0 +1,265 @@
+/* libunwind - a platform-independent unwind library
+ Copyright (C) 2010 by Lassi Tuura <lat@iki.fi>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <errno.h>
+#if HAVE_EXECINFO_H
+# include <execinfo.h>
+#else
+ extern int backtrace (void **, int);
+#endif
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <libunwind.h>
+
+#define panic(args...) \
+ { fprintf (stderr, args); exit (-1); }
+
+#ifndef HAVE_SIGHANDLER_T
+typedef RETSIGTYPE (*sighandler_t) (int);
+#endif
+
+int verbose;
+int num_errors;
+
+/* These variables are global because they
+ * cause the signal stack to overflow */
+char buf[512], name[256];
+void *addresses[2][128];
+unw_cursor_t cursor;
+ucontext_t uc;
+#if UNW_TARGET_X86_64
+unw_tdep_frame_t *cache;
+#endif
+
+static void
+do_backtrace (void)
+{
+ unw_word_t ip;
+ int ret = -UNW_ENOINFO;
+ int depth = 128;
+ int i, n;
+
+ if (verbose)
+ printf ("\tfast backtrace:\n");
+
+ unw_getcontext (&uc);
+ if (unw_init_local (&cursor, &uc) < 0)
+ panic ("unw_init_local failed!\n");
+
+#if UNW_TARGET_X86_64
+ if ((ret = unw_tdep_trace (&cursor, addresses[0], &depth, cache)) < 0)
+ {
+ unw_get_reg (&cursor, UNW_REG_IP, &ip);
+ printf ("FAILURE: unw_tdep_trace() returned %d for ip=%lx\n", ret, (long) ip);
+ ++num_errors;
+ }
+#endif
+
+ if (ret < 0)
+ {
+ i = 0;
+ do
+ {
+ unw_get_reg (&cursor, UNW_REG_IP, &ip);
+ addresses[0][i] = (void *) ip;
+ }
+ while ((ret = unw_step (&cursor)) >= 0 && ++i < 128);
+
+ if (ret < 0)
+ {
+ unw_get_reg (&cursor, UNW_REG_IP, &ip);
+ printf ("FAILURE: unw_step() returned %d for ip=%lx\n", ret, (long) ip);
+ ++num_errors;
+ }
+ }
+
+ if (verbose)
+ for (i = 0; i < depth; ++i)
+ printf ("\t #%-3d ip=%p\n", i, addresses[0][i]);
+
+ if (verbose)
+ printf ("\n\tvia backtrace():\n");
+
+ n = backtrace (addresses[1], 128);
+
+ if (verbose)
+ for (i = 0; i < n; ++i)
+ printf ("\t #%-3d ip=%p\n", i, addresses[1][i]);
+
+ if (n != depth)
+ {
+ printf ("FAILURE: unw_tdep_trace() and backtrace() depths differ: %d vs. %d\n", depth, n);
+ ++num_errors;
+ }
+ else
+ for (i = 1; i < depth; ++i)
+ /* Allow one in difference in comparison, trace returns adjusted addresses. */
+ if (labs((unw_word_t) addresses[0][i] - (unw_word_t) addresses[1][i]) > 1)
+ {
+ printf ("FAILURE: unw_tdep_trace() and backtrace() addresses differ at %d: %p vs. %p\n",
+ i, addresses[0][n], addresses[1][n]);
+ ++num_errors;
+ }
+}
+
+void
+foo (long val)
+{
+ do_backtrace ();
+}
+
+void
+bar (long v)
+{
+ extern long f (long);
+ int arr[v];
+
+ /* This is a vain attempt to use up lots of registers to force
+ the frame-chain info to be saved on the memory stack on ia64.
+ It happens to work with gcc v3.3.4 and gcc v3.4.1 but perhaps
+ not with any other compiler. */
+ foo (f (arr[0]) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + f (v))
+ ))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
+ )))))))))))))))))))))))))))))))))))))))))))))))))))))));
+}
+
+void
+sighandler (int signal, void *siginfo, void *context)
+{
+ ucontext_t *uc = context;
+ int sp;
+
+ if (verbose)
+ {
+ printf ("sighandler: got signal %d, sp=%p", signal, &sp);
+#if UNW_TARGET_IA64
+# if defined(__linux__)
+ printf (" @ %lx", uc->uc_mcontext.sc_ip);
+# else
+ {
+ uint16_t reason;
+ uint64_t ip;
+
+ __uc_get_reason (uc, &reason);
+ __uc_get_ip (uc, &ip);
+ printf (" @ %lx (reason=%d)", ip, reason);
+ }
+# endif
+#elif UNW_TARGET_X86
+#if defined __linux__
+ printf (" @ %lx", (unsigned long) uc->uc_mcontext.gregs[REG_EIP]);
+#elif defined __FreeBSD__
+ printf (" @ %lx", (unsigned long) uc->uc_mcontext.mc_eip);
+#endif
+#elif UNW_TARGET_X86_64
+#if defined __linux__
+ printf (" @ %lx", (unsigned long) uc->uc_mcontext.gregs[REG_RIP]);
+#elif defined __FreeBSD__
+ printf (" @ %lx", (unsigned long) uc->uc_mcontext.mc_rip);
+#endif
+#endif
+ printf ("\n");
+ }
+ do_backtrace();
+}
+
+int
+main (int argc, char **argv)
+{
+ struct sigaction act;
+ stack_t stk;
+
+#if UNW_TARGET_X86_64
+ cache = unw_tdep_make_frame_cache (0);
+#endif
+
+ verbose = (argc > 1);
+
+ if (verbose)
+ printf ("Normal backtrace:\n");
+
+ bar (1);
+
+ memset (&act, 0, sizeof (act));
+ act.sa_handler = (void (*)(int)) sighandler;
+ act.sa_flags = SA_SIGINFO;
+ if (sigaction (SIGTERM, &act, NULL) < 0)
+ panic ("sigaction: %s\n", strerror (errno));
+
+ if (verbose)
+ printf ("\nBacktrace across signal handler:\n");
+ kill (getpid (), SIGTERM);
+
+ if (verbose)
+ printf ("\nBacktrace across signal handler on alternate stack:\n");
+ stk.ss_sp = malloc (SIGSTKSZ);
+ if (!stk.ss_sp)
+ panic ("failed to allocate SIGSTKSZ (%u) bytes\n", SIGSTKSZ);
+ stk.ss_size = SIGSTKSZ;
+ stk.ss_flags = 0;
+ if (sigaltstack (&stk, NULL) < 0)
+ panic ("sigaltstack: %s\n", strerror (errno));
+
+ memset (&act, 0, sizeof (act));
+ act.sa_handler = (void (*)(int)) sighandler;
+ act.sa_flags = SA_ONSTACK | SA_SIGINFO;
+ if (sigaction (SIGTERM, &act, NULL) < 0)
+ panic ("sigaction: %s\n", strerror (errno));
+ kill (getpid (), SIGTERM);
+
+ if (num_errors > 0)
+ {
+ fprintf (stderr, "FAILURE: detected %d errors\n", num_errors);
+ exit (-1);
+ }
+
+#if UNW_TARGET_X86_64
+ unw_tdep_free_frame_cache (cache);
+#endif
+
+ if (verbose)
+ printf ("SUCCESS.\n");
+ return 0;
+}
diff --git a/tests/Ltest-trace.c b/tests/Ltest-trace.c
new file mode 100644
index 00000000..fb0e9c10
--- /dev/null
+++ b/tests/Ltest-trace.c
@@ -0,0 +1,5 @@
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+#if !defined(UNW_REMOTE_ONLY)
+#include "Gtest-trace.c"
+#endif
diff --git a/tests/Makefile.am b/tests/Makefile.am
index fe4a1afe..a981d635 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -41,6 +41,7 @@ endif #ARCH_IA64
Gtest-concurrent Ltest-concurrent \
Gtest-resume-sig Ltest-resume-sig \
Gtest-dyn1 Ltest-dyn1 \
+ Gtest-trace Ltest-trace \
test-async-sig test-flush-cache test-init-remote \
test-mem test-setjmp test-ptrace \
Ltest-nomalloc rs-race
@@ -98,6 +99,8 @@ Gtest_bt_SOURCES = Gtest-bt.c ident.c
Ltest_bt_SOURCES = Ltest-bt.c ident.c
test_ptrace_misc_SOURCES = test-ptrace-misc.c ident.c
Ltest_nomalloc_SOURCES = Ltest-nomalloc.c
+Gtest_trace_SOURCES = Gtest-trace.c ident.c
+Ltest_trace_SOURCES = Ltest-trace.c ident.c
LIBUNWIND = $(top_builddir)/src/libunwind-$(arch).la $(LIBUNWIND_local)
LIBUNWIND_ptrace = $(top_builddir)/src/libunwind-ptrace.a
diff --git a/tests/check-namespace.sh.in b/tests/check-namespace.sh.in
index 9c6b12dc..9d9fb974 100644
--- a/tests/check-namespace.sh.in
+++ b/tests/check-namespace.sh.in
@@ -123,6 +123,9 @@ check_local_unw_abi () {
match _U${plat}_is_fpreg
match _UL${plat}_dwarf_search_unwind_table
match _U${plat}_setcontext
+ match _UL${plat}_free_frame_cache
+ match _UL${plat}_make_frame_cache
+ match _UL${plat}_trace
;;
*)
match _U${plat}_is_fpreg
@@ -186,6 +189,9 @@ check_generic_unw_abi () {
match _U${plat}_get_elf_image
match _U${plat}_is_fpreg
match _U${plat}_dwarf_search_unwind_table
+ match _U${plat}_free_frame_cache
+ match _U${plat}_make_frame_cache
+ match _U${plat}_trace
;;
*)
match _U${plat}_is_fpreg