summaryrefslogtreecommitdiff
path: root/base/debug/stack_trace.h
diff options
context:
space:
mode:
Diffstat (limited to 'base/debug/stack_trace.h')
-rw-r--r--base/debug/stack_trace.h65
1 files changed, 62 insertions, 3 deletions
diff --git a/base/debug/stack_trace.h b/base/debug/stack_trace.h
index 23e7b5164b..4c9b73e87d 100644
--- a/base/debug/stack_trace.h
+++ b/base/debug/stack_trace.h
@@ -11,6 +11,7 @@
#include <string>
#include "base/base_export.h"
+#include "base/macros.h"
#include "build/build_config.h"
#if defined(OS_POSIX)
@@ -44,6 +45,11 @@ namespace debug {
// done in official builds because it has security implications).
BASE_EXPORT bool EnableInProcessStackDumping();
+// Returns end of the stack, or 0 if we couldn't get it.
+#if HAVE_TRACE_STACK_FRAME_POINTERS
+BASE_EXPORT uintptr_t GetStackEnd();
+#endif
+
// A stacktrace can be helpful in debugging. For example, you can include a
// stacktrace member in a object (probably around #ifndef NDEBUG) so that you
// can later see where the given object was created from.
@@ -52,9 +58,13 @@ class BASE_EXPORT StackTrace {
// Creates a stacktrace from the current location.
StackTrace();
+ // Creates a stacktrace from the current location, of up to |count| entries.
+ // |count| will be limited to at most |kMaxTraces|.
+ explicit StackTrace(size_t count);
+
// Creates a stacktrace from an existing array of instruction
// pointers (such as returned by Addresses()). |count| will be
- // trimmed to |kMaxTraces|.
+ // limited to at most |kMaxTraces|.
StackTrace(const void* const* trace, size_t count);
#if defined(OS_WIN)
@@ -67,8 +77,6 @@ class BASE_EXPORT StackTrace {
// Copying and assignment are allowed with the default functions.
- ~StackTrace();
-
// Gets an array of instruction pointer values. |*count| will be set to the
// number of elements in the returned array.
const void* const* Addresses(size_t* count) const;
@@ -113,6 +121,57 @@ class BASE_EXPORT StackTrace {
BASE_EXPORT size_t TraceStackFramePointers(const void** out_trace,
size_t max_depth,
size_t skip_initial);
+
+// Links stack frame |fp| to |parent_fp|, so that during stack unwinding
+// TraceStackFramePointers() visits |parent_fp| after visiting |fp|.
+// Both frame pointers must come from __builtin_frame_address().
+// Destructor restores original linkage of |fp| to avoid corrupting caller's
+// frame register on return.
+//
+// This class can be used to repair broken stack frame chain in cases
+// when execution flow goes into code built without frame pointers:
+//
+// void DoWork() {
+// Call_SomeLibrary();
+// }
+// static __thread void* g_saved_fp;
+// void Call_SomeLibrary() {
+// g_saved_fp = __builtin_frame_address(0);
+// some_library_call(...); // indirectly calls SomeLibrary_Callback()
+// }
+// void SomeLibrary_Callback() {
+// ScopedStackFrameLinker linker(__builtin_frame_address(0), g_saved_fp);
+// ...
+// TraceStackFramePointers(...);
+// }
+//
+// This produces the following trace:
+//
+// #0 SomeLibrary_Callback()
+// #1 <address of the code inside SomeLibrary that called #0>
+// #2 DoWork()
+// ...rest of the trace...
+//
+// SomeLibrary doesn't use frame pointers, so when SomeLibrary_Callback()
+// is called, stack frame register contains bogus value that becomes callback'
+// parent frame address. Without ScopedStackFrameLinker unwinding would've
+// stopped at that bogus frame address yielding just two first frames (#0, #1).
+// ScopedStackFrameLinker overwrites callback's parent frame address with
+// Call_SomeLibrary's frame, so unwinder produces full trace without even
+// noticing that stack frame chain was broken.
+class BASE_EXPORT ScopedStackFrameLinker {
+ public:
+ ScopedStackFrameLinker(void* fp, void* parent_fp);
+ ~ScopedStackFrameLinker();
+
+ private:
+ void* fp_;
+ void* parent_fp_;
+ void* original_parent_fp_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedStackFrameLinker);
+};
+
#endif // HAVE_TRACE_STACK_FRAME_POINTERS
namespace internal {