Skip to content

Commit 3b1bb73

Browse files
Abseil Teamcopybara-github
authored andcommitted
Adds functionality to return stack frame pointers during stack walking, in addition to code addresses
Together with the frame sizes already being returned, this gives us the bounds on each stack frame. Note that this may slightly affect MSVC in debug builds, as it does not respect ABSL_ATTRIBUTE_ALWAYS_INLINE without optimizations. To remedy this, either enable inlining (/Ob1 or higher), or write a wrapper that ensures any stack traces obtained are robust to frames being added (such as by manually filtering everything below the caller). Do not depend on the new APIs publicly. They are subject to change or removal. PiperOrigin-RevId: 740075920 Change-Id: I6cd0909e7be3af4c9f32d39633230f655946b6a8
1 parent 9927576 commit 3b1bb73

15 files changed

+338
-91
lines changed

absl/debugging/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,9 @@ cc_test(
6666
linkopts = ABSL_DEFAULT_LINKOPTS,
6767
deps = [
6868
":stacktrace",
69+
"//absl/base:config",
6970
"//absl/base:core_headers",
71+
"//absl/types:span",
7072
"@googletest//:gtest",
7173
"@googletest//:gtest_main",
7274
],

absl/debugging/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,9 @@ absl_cc_test(
5555
${ABSL_TEST_COPTS}
5656
DEPS
5757
absl::stacktrace
58+
absl::config
5859
absl::core_headers
60+
absl::span
5961
GTest::gmock_main
6062
)
6163

absl/debugging/internal/addresses.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@ namespace debugging_internal {
2525

2626
// Removes any metadata (tag bits) from the given pointer, converting it into a
2727
// user-readable address.
28-
inline uintptr_t StripPointerMetadata(void* ptr) {
28+
inline uintptr_t StripPointerMetadata(uintptr_t ptr) {
2929
#if defined(__aarch64__)
3030
// When PAC-RET (-mbranch-protection=pac-ret) is enabled, return addresses
3131
// stored on the stack will be signed, which means that pointer bits outside
3232
// of the virtual address range are potentially set. Since the stacktrace code
3333
// is expected to return normal code pointers, this function clears those
3434
// bits.
35-
register uintptr_t x30 __asm__("x30") = reinterpret_cast<uintptr_t>(ptr);
35+
register uintptr_t x30 __asm__("x30") = ptr;
3636
// The normal instruction for clearing PAC bits is XPACI, but for
3737
// compatibility with ARM platforms that do not support pointer
3838
// authentication, we use the hint space instruction XPACLRI instead. Hint
@@ -42,10 +42,14 @@ inline uintptr_t StripPointerMetadata(void* ptr) {
4242
#undef ABSL_XPACLRI_HINT
4343
return x30;
4444
#else
45-
return reinterpret_cast<uintptr_t>(ptr);
45+
return ptr;
4646
#endif
4747
}
4848

49+
inline uintptr_t StripPointerMetadata(void* ptr) {
50+
return StripPointerMetadata(reinterpret_cast<uintptr_t>(ptr));
51+
}
52+
4953
} // namespace debugging_internal
5054
ABSL_NAMESPACE_END
5155
} // namespace absl

absl/debugging/internal/stacktrace_aarch64-inl.inc

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,9 @@ template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
185185
ABSL_ATTRIBUTE_NOINLINE
186186
ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack.
187187
ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack.
188-
static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
189-
const void *ucp, int *min_dropped_frames) {
188+
static int UnwindImpl(void **result, uintptr_t *frames, int *sizes,
189+
int max_depth, int skip_count, const void *ucp,
190+
int *min_dropped_frames) {
190191
#ifdef __GNUC__
191192
void **frame_pointer = reinterpret_cast<void**>(__builtin_frame_address(0));
192193
#else
@@ -223,8 +224,15 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
223224
result[n] = reinterpret_cast<void *>(
224225
absl::debugging_internal::StripPointerMetadata(prev_return_address));
225226
if (IS_STACK_FRAMES) {
226-
sizes[n] = static_cast<int>(
227-
ComputeStackFrameSize(prev_frame_pointer, frame_pointer));
227+
if (frames != nullptr) {
228+
frames[n] = absl::debugging_internal::StripPointerMetadata(
229+
prev_frame_pointer) +
230+
2 * sizeof(void *) /* go past the return address */;
231+
}
232+
if (sizes != nullptr) {
233+
sizes[n] = static_cast<int>(
234+
ComputeStackFrameSize(prev_frame_pointer, frame_pointer));
235+
}
228236
}
229237
n++;
230238
}

absl/debugging/internal/stacktrace_arm-inl.inc

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include <cstdint>
2121

22+
#include "absl/debugging/internal/addresses.h"
2223
#include "absl/debugging/stacktrace.h"
2324

2425
// WARNING:
@@ -67,8 +68,9 @@ void StacktraceArmDummyFunction() { __asm__ volatile(""); }
6768
#endif
6869

6970
template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
70-
static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
71-
const void * /* ucp */, int *min_dropped_frames) {
71+
static int UnwindImpl(void **result, uintptr_t *frames, int *sizes,
72+
int max_depth, int skip_count, const void * /* ucp */,
73+
int *min_dropped_frames) {
7274
#ifdef __GNUC__
7375
void **sp = reinterpret_cast<void**>(__builtin_frame_address(0));
7476
#else
@@ -97,11 +99,18 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
9799
result[n] = *sp;
98100

99101
if (IS_STACK_FRAMES) {
100-
if (next_sp > sp) {
101-
sizes[n] = (uintptr_t)next_sp - (uintptr_t)sp;
102-
} else {
103-
// A frame-size of 0 is used to indicate unknown frame size.
104-
sizes[n] = 0;
102+
if (frames != nullptr) {
103+
frames[n] = absl::debugging_internal::StripPointerMetadata(sp) +
104+
1 * sizeof(void *) /* go past the return address */;
105+
}
106+
if (sizes != nullptr) {
107+
if (next_sp > sp) {
108+
sizes[n] = absl::debugging_internal::StripPointerMetadata(next_sp) -
109+
absl::debugging_internal::StripPointerMetadata(sp);
110+
} else {
111+
// A frame-size of 0 is used to indicate unknown frame size.
112+
sizes[n] = 0;
113+
}
105114
}
106115
}
107116
n++;

absl/debugging/internal/stacktrace_emscripten-inl.inc

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_EMSCRIPTEN_INL_H_
2222

2323
#include <emscripten.h>
24+
#include <stdint.h>
2425

2526
#include <atomic>
2627
#include <cstring>
@@ -62,8 +63,9 @@ ABSL_ATTRIBUTE_UNUSED static int stacktraces_enabler = []() {
6263
}();
6364

6465
template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
65-
static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count,
66-
const void *ucp, int *min_dropped_frames) {
66+
static int UnwindImpl(void **result, uintptr_t *frames, int *sizes,
67+
int max_depth, int skip_count, const void *ucp,
68+
int *min_dropped_frames) {
6769
if (recursive || disable_stacktraces.load(std::memory_order_relaxed)) {
6870
return 0;
6971
}
@@ -83,8 +85,13 @@ static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count,
8385
for (int i = 0; i < result_count; i++) result[i] = stack[i + skip_count];
8486

8587
if (IS_STACK_FRAMES) {
86-
// No implementation for finding out the stack frame sizes yet.
87-
memset(sizes, 0, sizeof(*sizes) * result_count);
88+
// No implementation for finding out the stack frames yet.
89+
if (frames != nullptr) {
90+
memset(frames, 0, sizeof(*frames) * result_count);
91+
}
92+
if (sizes != nullptr) {
93+
memset(sizes, 0, sizeof(*sizes) * result_count);
94+
}
8895
}
8996
if (min_dropped_frames != nullptr) {
9097
if (size - skip_count - max_depth > 0) {

absl/debugging/internal/stacktrace_generic-inl.inc

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,9 @@ ABSL_ATTRIBUTE_UNUSED static int stacktraces_enabler = []() {
5656
}();
5757

5858
template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
59-
static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
60-
const void *ucp, int *min_dropped_frames) {
59+
static int UnwindImpl(void** result, uintptr_t* frames, int* sizes,
60+
int max_depth, int skip_count, const void* ucp,
61+
int* min_dropped_frames) {
6162
if (recursive || disable_stacktraces.load(std::memory_order_relaxed)) {
6263
return 0;
6364
}
@@ -79,8 +80,13 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
7980
result[i] = stack[i + skip_count];
8081

8182
if (IS_STACK_FRAMES) {
82-
// No implementation for finding out the stack frame sizes yet.
83-
memset(sizes, 0, sizeof(*sizes) * static_cast<size_t>(result_count));
83+
// No implementation for finding out the stack frames yet.
84+
if (frames != nullptr) {
85+
memset(frames, 0, sizeof(*frames) * static_cast<size_t>(result_count));
86+
}
87+
if (sizes != nullptr) {
88+
memset(sizes, 0, sizeof(*sizes) * static_cast<size_t>(result_count));
89+
}
8490
}
8591
if (min_dropped_frames != nullptr) {
8692
if (size - skip_count - max_depth > 0) {

absl/debugging/internal/stacktrace_powerpc-inl.inc

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_POWERPC_INL_H_
2222
#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_POWERPC_INL_H_
2323

24+
#include "absl/debugging/internal/addresses.h"
2425
#if defined(__linux__)
2526
#include <asm/ptrace.h> // for PT_NIP.
2627
#include <ucontext.h> // for ucontext_t
@@ -40,22 +41,22 @@
4041

4142
// Given a stack pointer, return the saved link register value.
4243
// Note that this is the link register for a callee.
43-
static inline void *StacktracePowerPCGetLR(void **sp) {
44+
static inline void **StacktracePowerPCGetLRPtr(void **sp) {
4445
// PowerPC has 3 main ABIs, which say where in the stack the
4546
// Link Register is. For DARWIN and AIX (used by apple and
4647
// linux ppc64), it's in sp[2]. For SYSV (used by linux ppc),
4748
// it's in sp[1].
4849
#if defined(_CALL_AIX) || defined(_CALL_DARWIN)
49-
return *(sp+2);
50+
return (sp + 2);
5051
#elif defined(_CALL_SYSV)
51-
return *(sp+1);
52+
return (sp + 1);
5253
#elif defined(__APPLE__) || defined(__FreeBSD__) || \
5354
(defined(__linux__) && defined(__PPC64__))
5455
// This check is in case the compiler doesn't define _CALL_AIX/etc.
55-
return *(sp+2);
56+
return (sp + 2);
5657
#elif defined(__linux)
5758
// This check is in case the compiler doesn't define _CALL_SYSV.
58-
return *(sp+1);
59+
return (sp + 1);
5960
#else
6061
#error Need to specify the PPC ABI for your architecture.
6162
#endif
@@ -125,9 +126,8 @@ static void **NextStackFrame(void **old_sp, const void *uc) {
125126
}
126127
}
127128

128-
if (new_sp != nullptr &&
129-
kernel_symbol_status == kAddressValid &&
130-
StacktracePowerPCGetLR(new_sp) == kernel_sigtramp_rt64_address) {
129+
if (new_sp != nullptr && kernel_symbol_status == kAddressValid &&
130+
*StacktracePowerPCGetLRPtr(new_sp) == kernel_sigtramp_rt64_address) {
131131
const ucontext_t* signal_context =
132132
reinterpret_cast<const ucontext_t*>(uc);
133133
void **const sp_before_signal =
@@ -164,8 +164,9 @@ ABSL_ATTRIBUTE_NOINLINE static void AbslStacktracePowerPCDummyFunction() {
164164
template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
165165
ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack.
166166
ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack.
167-
static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
168-
const void *ucp, int *min_dropped_frames) {
167+
static int UnwindImpl(void **result, uintptr_t *frames, int *sizes,
168+
int max_depth, int skip_count, const void *ucp,
169+
int *min_dropped_frames) {
169170
void **sp;
170171
// Apple macOS uses an old version of gnu as -- both Darwin 7.9.0 (Panther)
171172
// and Darwin 8.8.1 (Tiger) use as 1.38. This means we have to use a
@@ -211,13 +212,21 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
211212
if (skip_count > 0) {
212213
skip_count--;
213214
} else {
214-
result[n] = StacktracePowerPCGetLR(sp);
215+
void **lr = StacktracePowerPCGetLRPtr(sp);
216+
result[n] = *lr;
215217
if (IS_STACK_FRAMES) {
216-
if (next_sp > sp) {
217-
sizes[n] = (uintptr_t)next_sp - (uintptr_t)sp;
218-
} else {
219-
// A frame-size of 0 is used to indicate unknown frame size.
220-
sizes[n] = 0;
218+
if (frames != nullptr) {
219+
frames[n] = absl::debugging_internal::StripPointerMetadata(lr) +
220+
1 * sizeof(void *) /* go past the return address */;
221+
}
222+
if (sizes != nullptr) {
223+
if (next_sp > sp) {
224+
sizes[n] = absl::debugging_internal::StripPointerMetadata(next_sp) -
225+
absl::debugging_internal::StripPointerMetadata(sp);
226+
} else {
227+
// A frame-size of 0 is used to indicate unknown frame size.
228+
sizes[n] = 0;
229+
}
221230
}
222231
}
223232
n++;

absl/debugging/internal/stacktrace_riscv-inl.inc

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <sys/ucontext.h>
2121

2222
#include "absl/base/config.h"
23+
#include "absl/debugging/internal/addresses.h"
2324
#if defined(__linux__)
2425
#include <sys/mman.h>
2526
#include <ucontext.h>
@@ -117,8 +118,9 @@ static void ** NextStackFrame(void **old_frame_pointer, const void *uc,
117118
template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
118119
ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack.
119120
ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack.
120-
static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count,
121-
const void *ucp, int *min_dropped_frames) {
121+
static int UnwindImpl(void **result, uintptr_t *frames, int *sizes,
122+
int max_depth, int skip_count, const void *ucp,
123+
int *min_dropped_frames) {
122124
// The `frame_pointer` that is computed here points to the top of the frame.
123125
// The two words preceding the address are the return address and the previous
124126
// frame pointer.
@@ -153,8 +155,13 @@ static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count,
153155
result[n] = return_address;
154156
if (IS_STACK_FRAMES) {
155157
// NextStackFrame() has already checked that frame size fits to int
156-
sizes[n] = static_cast<int>(ComputeStackFrameSize(frame_pointer,
157-
next_frame_pointer));
158+
if (frames != nullptr) {
159+
frames[n] =
160+
absl::debugging_internal::StripPointerMetadata(frame_pointer);
161+
}
162+
if (sizes != nullptr) {
163+
sizes[n] = ComputeStackFrameSize(frame_pointer, next_frame_pointer);
164+
}
158165
}
159166
n++;
160167
}

absl/debugging/internal/stacktrace_unimplemented-inl.inc

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_UNIMPLEMENTED_INL_H_
33

44
template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
5-
static int UnwindImpl(void** /* result */, int* /* sizes */,
6-
int /* max_depth */, int /* skip_count */,
7-
const void* /* ucp */, int *min_dropped_frames) {
5+
static int UnwindImpl(void** /* result */, uintptr_t* /* frames */,
6+
int* /* sizes */, int /* max_depth */,
7+
int /* skip_count */, const void* /* ucp */,
8+
int* min_dropped_frames) {
89
if (min_dropped_frames != nullptr) {
910
*min_dropped_frames = 0;
1011
}

0 commit comments

Comments
 (0)