summaryrefslogtreecommitdiff
path: root/absl/debugging/internal
diff options
context:
space:
mode:
authorGravatar Pirate Praveen <praveen@debian.org>2023-03-06 20:25:41 +0530
committerGravatar Pirate Praveen <praveen@debian.org>2023-03-06 20:25:41 +0530
commit079dd8737bbaaaeeca3a95c2b858a62d8a620d5a (patch)
tree6af54966e17bcfe48ecdb0b5cdf43cc953d5358c /absl/debugging/internal
parent2bbc47f307f1e24f3f44a108d571bffa5a3faa63 (diff)
parentf5afcb784c9b1c501c1144b7aab84555881ca871 (diff)
Merge tag '20220623.1-1' into bullseye-backports-staging
Diffstat (limited to 'absl/debugging/internal')
-rw-r--r--absl/debugging/internal/address_is_readable.cc133
-rw-r--r--absl/debugging/internal/demangle.cc12
-rw-r--r--absl/debugging/internal/elf_mem_image.cc27
-rw-r--r--absl/debugging/internal/elf_mem_image.h9
-rw-r--r--absl/debugging/internal/examine_stack.cc222
-rw-r--r--absl/debugging/internal/examine_stack.h34
-rw-r--r--absl/debugging/internal/stack_consumption.cc2
-rw-r--r--absl/debugging/internal/stack_consumption.h2
-rw-r--r--absl/debugging/internal/stacktrace_aarch64-inl.inc11
-rw-r--r--absl/debugging/internal/stacktrace_arm-inl.inc11
-rw-r--r--absl/debugging/internal/stacktrace_config.h18
-rw-r--r--absl/debugging/internal/stacktrace_emscripten-inl.inc110
-rw-r--r--absl/debugging/internal/stacktrace_powerpc-inl.inc11
-rw-r--r--absl/debugging/internal/stacktrace_riscv-inl.inc236
-rw-r--r--absl/debugging/internal/stacktrace_x86-inl.inc43
-rw-r--r--absl/debugging/internal/symbolize.h10
-rw-r--r--absl/debugging/internal/vdso_support.cc39
17 files changed, 735 insertions, 195 deletions
diff --git a/absl/debugging/internal/address_is_readable.cc b/absl/debugging/internal/address_is_readable.cc
index 329c285f..4be6256b 100644
--- a/absl/debugging/internal/address_is_readable.cc
+++ b/absl/debugging/internal/address_is_readable.cc
@@ -30,16 +30,12 @@ bool AddressIsReadable(const void* /* addr */) { return true; }
ABSL_NAMESPACE_END
} // namespace absl
-#else
+#else // __linux__ && !__ANDROID__
-#include <fcntl.h>
-#include <sys/syscall.h>
+#include <stdint.h>
+#include <syscall.h>
#include <unistd.h>
-#include <atomic>
-#include <cerrno>
-#include <cstdint>
-
#include "absl/base/internal/errno_saver.h"
#include "absl/base/internal/raw_logging.h"
@@ -47,93 +43,54 @@ namespace absl {
ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
-// Pack a pid and two file descriptors into a 64-bit word,
-// using 16, 24, and 24 bits for each respectively.
-static uint64_t Pack(uint64_t pid, uint64_t read_fd, uint64_t write_fd) {
- ABSL_RAW_CHECK((read_fd >> 24) == 0 && (write_fd >> 24) == 0,
- "fd out of range");
- return (pid << 48) | ((read_fd & 0xffffff) << 24) | (write_fd & 0xffffff);
-}
-
-// Unpack x into a pid and two file descriptors, where x was created with
-// Pack().
-static void Unpack(uint64_t x, int *pid, int *read_fd, int *write_fd) {
- *pid = x >> 48;
- *read_fd = (x >> 24) & 0xffffff;
- *write_fd = x & 0xffffff;
-}
+// NOTE: be extra careful about adding any interposable function calls here
+// (such as open(), read(), etc.). These symbols may be interposed and will get
+// invoked in contexts they don't expect.
+//
+// NOTE: any new system calls here may also require sandbox reconfiguration.
+//
+bool AddressIsReadable(const void *addr) {
+ // Align address on 8-byte boundary. On aarch64, checking last
+ // byte before inaccessible page returned unexpected EFAULT.
+ const uintptr_t u_addr = reinterpret_cast<uintptr_t>(addr) & ~7;
+ addr = reinterpret_cast<const void *>(u_addr);
-// Return whether the byte at *addr is readable, without faulting.
-// Save and restores errno. Returns true on systems where
-// unimplemented.
-// This is a namespace-scoped variable for correct zero-initialization.
-static std::atomic<uint64_t> pid_and_fds; // initially 0, an invalid pid.
+ // rt_sigprocmask below will succeed for this input.
+ if (addr == nullptr) return false;
-bool AddressIsReadable(const void *addr) {
absl::base_internal::ErrnoSaver errno_saver;
- // We test whether a byte is readable by using write(). Normally, this would
- // be done via a cached file descriptor to /dev/null, but linux fails to
- // check whether the byte is readable when the destination is /dev/null, so
- // we use a cached pipe. We store the pid of the process that created the
- // pipe to handle the case where a process forks, and the child closes all
- // the file descriptors and then calls this routine. This is not perfect:
- // the child could use the routine, then close all file descriptors and then
- // use this routine again. But the likely use of this routine is when
- // crashing, to test the validity of pages when dumping the stack. Beware
- // that we may leak file descriptors, but we're unlikely to leak many.
- int bytes_written;
- int current_pid = getpid() & 0xffff; // we use only the low order 16 bits
- do { // until we do not get EBADF trying to use file descriptors
- int pid;
- int read_fd;
- int write_fd;
- uint64_t local_pid_and_fds = pid_and_fds.load(std::memory_order_acquire);
- Unpack(local_pid_and_fds, &pid, &read_fd, &write_fd);
- while (current_pid != pid) {
- int p[2];
- // new pipe
- if (pipe(p) != 0) {
- ABSL_RAW_LOG(FATAL, "Failed to create pipe, errno=%d", errno);
- }
- fcntl(p[0], F_SETFD, FD_CLOEXEC);
- fcntl(p[1], F_SETFD, FD_CLOEXEC);
- uint64_t new_pid_and_fds = Pack(current_pid, p[0], p[1]);
- if (pid_and_fds.compare_exchange_strong(
- local_pid_and_fds, new_pid_and_fds, std::memory_order_release,
- std::memory_order_relaxed)) {
- local_pid_and_fds = new_pid_and_fds; // fds exposed to other threads
- } else { // fds not exposed to other threads; we can close them.
- close(p[0]);
- close(p[1]);
- local_pid_and_fds = pid_and_fds.load(std::memory_order_acquire);
- }
- Unpack(local_pid_and_fds, &pid, &read_fd, &write_fd);
- }
- errno = 0;
- // Use syscall(SYS_write, ...) instead of write() to prevent ASAN
- // and other checkers from complaining about accesses to arbitrary
- // memory.
- do {
- bytes_written = syscall(SYS_write, write_fd, addr, 1);
- } while (bytes_written == -1 && errno == EINTR);
- if (bytes_written == 1) { // remove the byte from the pipe
- char c;
- while (read(read_fd, &c, 1) == -1 && errno == EINTR) {
- }
- }
- if (errno == EBADF) { // Descriptors invalid.
- // If pid_and_fds contains the problematic file descriptors we just used,
- // this call will forget them, and the loop will try again.
- pid_and_fds.compare_exchange_strong(local_pid_and_fds, 0,
- std::memory_order_release,
- std::memory_order_relaxed);
- }
- } while (errno == EBADF);
- return bytes_written == 1;
+
+ // Here we probe with some syscall which
+ // - accepts an 8-byte region of user memory as input
+ // - tests for EFAULT before other validation
+ // - has no problematic side-effects
+ //
+ // rt_sigprocmask(2) works for this. It copies sizeof(kernel_sigset_t)==8
+ // bytes from the address into the kernel memory before any validation.
+ //
+ // The call can never succeed, since the `how` parameter is not one of
+ // SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK.
+ //
+ // This strategy depends on Linux implementation details,
+ // so we rely on the test to alert us if it stops working.
+ //
+ // Some discarded past approaches:
+ // - msync() doesn't reject PROT_NONE regions
+ // - write() on /dev/null doesn't return EFAULT
+ // - write() on a pipe requires creating it and draining the writes
+ // - connect() works but is problematic for sandboxes and needs a valid
+ // file descriptor
+ //
+ // This can never succeed (invalid first argument to sigprocmask).
+ ABSL_RAW_CHECK(syscall(SYS_rt_sigprocmask, ~0, addr, nullptr,
+ /*sizeof(kernel_sigset_t)*/ 8) == -1,
+ "unexpected success");
+ ABSL_RAW_CHECK(errno == EFAULT || errno == EINVAL, "unexpected errno");
+ return errno != EFAULT;
}
} // namespace debugging_internal
ABSL_NAMESPACE_END
} // namespace absl
-#endif
+#endif // __linux__ && !__ANDROID__
diff --git a/absl/debugging/internal/demangle.cc b/absl/debugging/internal/demangle.cc
index 5cd56320..93ae3279 100644
--- a/absl/debugging/internal/demangle.cc
+++ b/absl/debugging/internal/demangle.cc
@@ -1617,6 +1617,7 @@ static bool ParseUnresolvedName(State *state) {
// ::= <2-ary operator-name> <expression> <expression>
// ::= <3-ary operator-name> <expression> <expression> <expression>
// ::= cl <expression>+ E
+// ::= cp <simple-id> <expression>* E # Clang-specific.
// ::= cv <type> <expression> # type (expression)
// ::= cv <type> _ <expression>* E # type (expr-list)
// ::= st <type>
@@ -1639,14 +1640,23 @@ static bool ParseExpression(State *state) {
return true;
}
- // Object/function call expression.
ParseState copy = state->parse_state;
+
+ // Object/function call expression.
if (ParseTwoCharToken(state, "cl") && OneOrMore(ParseExpression, state) &&
ParseOneCharToken(state, 'E')) {
return true;
}
state->parse_state = copy;
+ // Clang-specific "cp <simple-id> <expression>* E"
+ // https://clang.llvm.org/doxygen/ItaniumMangle_8cpp_source.html#l04338
+ if (ParseTwoCharToken(state, "cp") && ParseSimpleId(state) &&
+ ZeroOrMore(ParseExpression, state) && ParseOneCharToken(state, 'E')) {
+ return true;
+ }
+ state->parse_state = copy;
+
// Function-param expression (level 0).
if (ParseTwoCharToken(state, "fp") && Optional(ParseCVQualifiers(state)) &&
Optional(ParseNumber(state, nullptr)) && ParseOneCharToken(state, '_')) {
diff --git a/absl/debugging/internal/elf_mem_image.cc b/absl/debugging/internal/elf_mem_image.cc
index 24cc0130..a9d66714 100644
--- a/absl/debugging/internal/elf_mem_image.cc
+++ b/absl/debugging/internal/elf_mem_image.cc
@@ -22,6 +22,7 @@
#include <string.h>
#include <cassert>
#include <cstddef>
+#include "absl/base/config.h"
#include "absl/base/internal/raw_logging.h"
// From binutils/include/elf/common.h (this doesn't appear to be documented
@@ -43,11 +44,11 @@ namespace debugging_internal {
namespace {
-#if __WORDSIZE == 32
+#if __SIZEOF_POINTER__ == 4
const int kElfClass = ELFCLASS32;
int ElfBind(const ElfW(Sym) *symbol) { return ELF32_ST_BIND(symbol->st_info); }
int ElfType(const ElfW(Sym) *symbol) { return ELF32_ST_TYPE(symbol->st_info); }
-#elif __WORDSIZE == 64
+#elif __SIZEOF_POINTER__ == 8
const int kElfClass = ELFCLASS64;
int ElfBind(const ElfW(Sym) *symbol) { return ELF64_ST_BIND(symbol->st_info); }
int ElfType(const ElfW(Sym) *symbol) { return ELF64_ST_TYPE(symbol->st_info); }
@@ -175,17 +176,17 @@ void ElfMemImage::Init(const void *base) {
}
switch (base_as_char[EI_DATA]) {
case ELFDATA2LSB: {
- if (__LITTLE_ENDIAN != __BYTE_ORDER) {
- assert(false);
- return;
- }
+#ifndef ABSL_IS_LITTLE_ENDIAN
+ assert(false);
+ return;
+#endif
break;
}
case ELFDATA2MSB: {
- if (__BIG_ENDIAN != __BYTE_ORDER) {
- assert(false);
- return;
- }
+#ifndef ABSL_IS_BIG_ENDIAN
+ assert(false);
+ return;
+#endif
break;
}
default: {
@@ -221,7 +222,7 @@ void ElfMemImage::Init(const void *base) {
reinterpret_cast<ElfW(Dyn) *>(dynamic_program_header->p_vaddr +
relocation);
for (; dynamic_entry->d_tag != DT_NULL; ++dynamic_entry) {
- const ElfW(Xword) value = dynamic_entry->d_un.d_val + relocation;
+ const auto value = dynamic_entry->d_un.d_val + relocation;
switch (dynamic_entry->d_tag) {
case DT_HASH:
hash_ = reinterpret_cast<ElfW(Word) *>(value);
@@ -350,7 +351,11 @@ void ElfMemImage::SymbolIterator::Update(int increment) {
const ElfW(Versym) *version_symbol = image->GetVersym(index_);
ABSL_RAW_CHECK(symbol && version_symbol, "");
const char *const symbol_name = image->GetDynstr(symbol->st_name);
+#if defined(__NetBSD__)
+ const int version_index = version_symbol->vs_vers & VERSYM_VERSION;
+#else
const ElfW(Versym) version_index = version_symbol[0] & VERSYM_VERSION;
+#endif
const ElfW(Verdef) *version_definition = nullptr;
const char *version_name = "";
if (symbol->st_shndx == SHN_UNDEF) {
diff --git a/absl/debugging/internal/elf_mem_image.h b/absl/debugging/internal/elf_mem_image.h
index 46bfade3..113071a9 100644
--- a/absl/debugging/internal/elf_mem_image.h
+++ b/absl/debugging/internal/elf_mem_image.h
@@ -31,8 +31,9 @@
#error ABSL_HAVE_ELF_MEM_IMAGE cannot be directly set
#endif
-#if defined(__ELF__) && defined(__GLIBC__) && !defined(__native_client__) && \
- !defined(__asmjs__) && !defined(__wasm__)
+#if defined(__ELF__) && !defined(__OpenBSD__) && !defined(__QNX__) && \
+ !defined(__native_client__) && !defined(__asmjs__) && \
+ !defined(__wasm__) && !defined(__HAIKU__)
#define ABSL_HAVE_ELF_MEM_IMAGE 1
#endif
@@ -40,6 +41,10 @@
#include <link.h> // for ElfW
+#if defined(__FreeBSD__) && !defined(ElfW)
+#define ElfW(x) __ElfN(x)
+#endif
+
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
diff --git a/absl/debugging/internal/examine_stack.cc b/absl/debugging/internal/examine_stack.cc
index 589a3ef3..5bdd341e 100644
--- a/absl/debugging/internal/examine_stack.cc
+++ b/absl/debugging/internal/examine_stack.cc
@@ -20,7 +20,13 @@
#include <unistd.h>
#endif
-#ifdef __APPLE__
+#include "absl/base/config.h"
+
+#ifdef ABSL_HAVE_MMAP
+#include <sys/mman.h>
+#endif
+
+#if defined(__linux__) || defined(__APPLE__)
#include <sys/ucontext.h>
#endif
@@ -37,10 +43,115 @@ namespace absl {
ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
+namespace {
+constexpr int kDefaultDumpStackFramesLimit = 64;
+// The %p field width for printf() functions is two characters per byte,
+// and two extra for the leading "0x".
+constexpr int kPrintfPointerFieldWidth = 2 + 2 * sizeof(void*);
+
+ABSL_CONST_INIT SymbolizeUrlEmitter debug_stack_trace_hook = nullptr;
+
+// Async-signal safe mmap allocator.
+void* Allocate(size_t num_bytes) {
+#ifdef ABSL_HAVE_MMAP
+ void* p = ::mmap(nullptr, num_bytes, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ return p == MAP_FAILED ? nullptr : p;
+#else
+ (void)num_bytes;
+ return nullptr;
+#endif // ABSL_HAVE_MMAP
+}
+
+void Deallocate(void* p, size_t size) {
+#ifdef ABSL_HAVE_MMAP
+ ::munmap(p, size);
+#else
+ (void)p;
+ (void)size;
+#endif // ABSL_HAVE_MMAP
+}
+
+// Print a program counter only.
+void DumpPC(OutputWriter* writer, void* writer_arg, void* const pc,
+ const char* const prefix) {
+ char buf[100];
+ snprintf(buf, sizeof(buf), "%s@ %*p\n", prefix, kPrintfPointerFieldWidth, pc);
+ writer(buf, writer_arg);
+}
+
+// Print a program counter and the corresponding stack frame size.
+void DumpPCAndFrameSize(OutputWriter* writer, void* writer_arg, void* const pc,
+ int framesize, const char* const prefix) {
+ char buf[100];
+ if (framesize <= 0) {
+ snprintf(buf, sizeof(buf), "%s@ %*p (unknown)\n", prefix,
+ kPrintfPointerFieldWidth, pc);
+ } else {
+ snprintf(buf, sizeof(buf), "%s@ %*p %9d\n", prefix,
+ kPrintfPointerFieldWidth, pc, framesize);
+ }
+ writer(buf, writer_arg);
+}
+
+// Print a program counter and the corresponding symbol.
+void DumpPCAndSymbol(OutputWriter* writer, void* writer_arg, void* const pc,
+ const char* const prefix) {
+ char tmp[1024];
+ const char* symbol = "(unknown)";
+ // Symbolizes the previous address of pc because pc may be in the
+ // next function. The overrun happens when the function ends with
+ // a call to a function annotated noreturn (e.g. CHECK).
+ // If symbolization of pc-1 fails, also try pc on the off-chance
+ // that we crashed on the first instruction of a function (that
+ // actually happens very often for e.g. __restore_rt).
+ const uintptr_t prev_pc = reinterpret_cast<uintptr_t>(pc) - 1;
+ if (absl::Symbolize(reinterpret_cast<const char*>(prev_pc), tmp,
+ sizeof(tmp)) ||
+ absl::Symbolize(pc, tmp, sizeof(tmp))) {
+ symbol = tmp;
+ }
+ char buf[1024];
+ snprintf(buf, sizeof(buf), "%s@ %*p %s\n", prefix, kPrintfPointerFieldWidth,
+ pc, symbol);
+ writer(buf, writer_arg);
+}
+
+// Print a program counter, its stack frame size, and its symbol name.
+// Note that there is a separate symbolize_pc argument. Return addresses may be
+// at the end of the function, and this allows the caller to back up from pc if
+// appropriate.
+void DumpPCAndFrameSizeAndSymbol(OutputWriter* writer, void* writer_arg,
+ void* const pc, void* const symbolize_pc,
+ int framesize, const char* const prefix) {
+ char tmp[1024];
+ const char* symbol = "(unknown)";
+ if (absl::Symbolize(symbolize_pc, tmp, sizeof(tmp))) {
+ symbol = tmp;
+ }
+ char buf[1024];
+ if (framesize <= 0) {
+ snprintf(buf, sizeof(buf), "%s@ %*p (unknown) %s\n", prefix,
+ kPrintfPointerFieldWidth, pc, symbol);
+ } else {
+ snprintf(buf, sizeof(buf), "%s@ %*p %9d %s\n", prefix,
+ kPrintfPointerFieldWidth, pc, framesize, symbol);
+ }
+ writer(buf, writer_arg);
+}
+
+} // namespace
+
+void RegisterDebugStackTraceHook(SymbolizeUrlEmitter hook) {
+ debug_stack_trace_hook = hook;
+}
+
+SymbolizeUrlEmitter GetDebugStackTraceHook() { return debug_stack_trace_hook; }
+
// Returns the program counter from signal context, nullptr if
// unknown. vuc is a ucontext_t*. We use void* to avoid the use of
// ucontext_t on non-POSIX systems.
-void* GetProgramCounter(void* vuc) {
+void* GetProgramCounter(void* const vuc) {
#ifdef __linux__
if (vuc != nullptr) {
ucontext_t* context = reinterpret_cast<ucontext_t*>(vuc);
@@ -82,6 +193,8 @@ void* GetProgramCounter(void* vuc) {
return reinterpret_cast<void*>(context->uc_mcontext.gregs[16]);
#elif defined(__e2k__)
return reinterpret_cast<void*>(context->uc_mcontext.cr0_hi);
+#elif defined(__loongarch__)
+ return reinterpret_cast<void*>(context->uc_mcontext.__pc);
#else
#error "Undefined Architecture."
#endif
@@ -120,59 +233,17 @@ void* GetProgramCounter(void* vuc) {
return nullptr;
}
-// The %p field width for printf() functions is two characters per byte,
-// and two extra for the leading "0x".
-static constexpr int kPrintfPointerFieldWidth = 2 + 2 * sizeof(void*);
-
-// Print a program counter, its stack frame size, and its symbol name.
-// Note that there is a separate symbolize_pc argument. Return addresses may be
-// at the end of the function, and this allows the caller to back up from pc if
-// appropriate.
-static void DumpPCAndFrameSizeAndSymbol(void (*writerfn)(const char*, void*),
- void* writerfn_arg, void* pc,
- void* symbolize_pc, int framesize,
- const char* const prefix) {
- char tmp[1024];
- const char* symbol = "(unknown)";
- if (absl::Symbolize(symbolize_pc, tmp, sizeof(tmp))) {
- symbol = tmp;
- }
- char buf[1024];
- if (framesize <= 0) {
- snprintf(buf, sizeof(buf), "%s@ %*p (unknown) %s\n", prefix,
- kPrintfPointerFieldWidth, pc, symbol);
- } else {
- snprintf(buf, sizeof(buf), "%s@ %*p %9d %s\n", prefix,
- kPrintfPointerFieldWidth, pc, framesize, symbol);
- }
- writerfn(buf, writerfn_arg);
-}
-
-// Print a program counter and the corresponding stack frame size.
-static void DumpPCAndFrameSize(void (*writerfn)(const char*, void*),
- void* writerfn_arg, void* pc, int framesize,
- const char* const prefix) {
- char buf[100];
- if (framesize <= 0) {
- snprintf(buf, sizeof(buf), "%s@ %*p (unknown)\n", prefix,
- kPrintfPointerFieldWidth, pc);
- } else {
- snprintf(buf, sizeof(buf), "%s@ %*p %9d\n", prefix,
- kPrintfPointerFieldWidth, pc, framesize);
- }
- writerfn(buf, writerfn_arg);
-}
-
-void DumpPCAndFrameSizesAndStackTrace(
- void* pc, void* const stack[], int frame_sizes[], int depth,
- int min_dropped_frames, bool symbolize_stacktrace,
- void (*writerfn)(const char*, void*), void* writerfn_arg) {
+void DumpPCAndFrameSizesAndStackTrace(void* const pc, void* const stack[],
+ int frame_sizes[], int depth,
+ int min_dropped_frames,
+ bool symbolize_stacktrace,
+ OutputWriter* writer, void* writer_arg) {
if (pc != nullptr) {
// We don't know the stack frame size for PC, use 0.
if (symbolize_stacktrace) {
- DumpPCAndFrameSizeAndSymbol(writerfn, writerfn_arg, pc, pc, 0, "PC: ");
+ DumpPCAndFrameSizeAndSymbol(writer, writer_arg, pc, pc, 0, "PC: ");
} else {
- DumpPCAndFrameSize(writerfn, writerfn_arg, pc, 0, "PC: ");
+ DumpPCAndFrameSize(writer, writer_arg, pc, 0, "PC: ");
}
}
for (int i = 0; i < depth; i++) {
@@ -182,20 +253,61 @@ void DumpPCAndFrameSizesAndStackTrace(
// call to a function annotated noreturn (e.g. CHECK). Note that we don't
// do this for pc above, as the adjustment is only correct for return
// addresses.
- DumpPCAndFrameSizeAndSymbol(writerfn, writerfn_arg, stack[i],
+ DumpPCAndFrameSizeAndSymbol(writer, writer_arg, stack[i],
reinterpret_cast<char*>(stack[i]) - 1,
frame_sizes[i], " ");
} else {
- DumpPCAndFrameSize(writerfn, writerfn_arg, stack[i], frame_sizes[i],
- " ");
+ DumpPCAndFrameSize(writer, writer_arg, stack[i], frame_sizes[i], " ");
}
}
if (min_dropped_frames > 0) {
char buf[100];
snprintf(buf, sizeof(buf), " @ ... and at least %d more frames\n",
min_dropped_frames);
- writerfn(buf, writerfn_arg);
+ writer(buf, writer_arg);
+ }
+}
+
+// Dump current stack trace as directed by writer.
+// Make sure this function is not inlined to avoid skipping too many top frames.
+ABSL_ATTRIBUTE_NOINLINE
+void DumpStackTrace(int min_dropped_frames, int max_num_frames,
+ bool symbolize_stacktrace, OutputWriter* writer,
+ void* writer_arg) {
+ // Print stack trace
+ void* stack_buf[kDefaultDumpStackFramesLimit];
+ void** stack = stack_buf;
+ int num_stack = kDefaultDumpStackFramesLimit;
+ int allocated_bytes = 0;
+
+ if (num_stack >= max_num_frames) {
+ // User requested fewer frames than we already have space for.
+ num_stack = max_num_frames;
+ } else {
+ const size_t needed_bytes = max_num_frames * sizeof(stack[0]);
+ void* p = Allocate(needed_bytes);
+ if (p != nullptr) { // We got the space.
+ num_stack = max_num_frames;
+ stack = reinterpret_cast<void**>(p);
+ allocated_bytes = needed_bytes;
+ }
}
+
+ size_t depth = absl::GetStackTrace(stack, num_stack, min_dropped_frames + 1);
+ for (size_t i = 0; i < depth; i++) {
+ if (symbolize_stacktrace) {
+ DumpPCAndSymbol(writer, writer_arg, stack[i], " ");
+ } else {
+ DumpPC(writer, writer_arg, stack[i], " ");
+ }
+ }
+
+ auto hook = GetDebugStackTraceHook();
+ if (hook != nullptr) {
+ (*hook)(stack, depth, writer, writer_arg);
+ }
+
+ if (allocated_bytes != 0) Deallocate(stack, allocated_bytes);
}
} // namespace debugging_internal
diff --git a/absl/debugging/internal/examine_stack.h b/absl/debugging/internal/examine_stack.h
index 39336913..190af87f 100644
--- a/absl/debugging/internal/examine_stack.h
+++ b/absl/debugging/internal/examine_stack.h
@@ -23,17 +23,39 @@ namespace absl {
ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
+// Type of function used for printing in stack trace dumping, etc.
+// We avoid closures to keep things simple.
+typedef void OutputWriter(const char*, void*);
+
+// RegisterDebugStackTraceHook() allows to register a single routine
+// `hook` that is called each time DumpStackTrace() is called.
+// `hook` may be called from a signal handler.
+typedef void (*SymbolizeUrlEmitter)(void* const stack[], int depth,
+ OutputWriter* writer, void* writer_arg);
+
+// Registration of SymbolizeUrlEmitter for use inside of a signal handler.
+// This is inherently unsafe and must be signal safe code.
+void RegisterDebugStackTraceHook(SymbolizeUrlEmitter hook);
+SymbolizeUrlEmitter GetDebugStackTraceHook();
+
// Returns the program counter from signal context, or nullptr if
// unknown. `vuc` is a ucontext_t*. We use void* to avoid the use of
// ucontext_t on non-POSIX systems.
-void* GetProgramCounter(void* vuc);
+void* GetProgramCounter(void* const vuc);
-// Uses `writerfn` to dump the program counter, stack trace, and stack
+// Uses `writer` to dump the program counter, stack trace, and stack
// frame sizes.
-void DumpPCAndFrameSizesAndStackTrace(
- void* pc, void* const stack[], int frame_sizes[], int depth,
- int min_dropped_frames, bool symbolize_stacktrace,
- void (*writerfn)(const char*, void*), void* writerfn_arg);
+void DumpPCAndFrameSizesAndStackTrace(void* const pc, void* const stack[],
+ int frame_sizes[], int depth,
+ int min_dropped_frames,
+ bool symbolize_stacktrace,
+ OutputWriter* writer, void* writer_arg);
+
+// Dump current stack trace omitting the topmost `min_dropped_frames` stack
+// frames.
+void DumpStackTrace(int min_dropped_frames, int max_num_frames,
+ bool symbolize_stacktrace, OutputWriter* writer,
+ void* writer_arg);
} // namespace debugging_internal
ABSL_NAMESPACE_END
diff --git a/absl/debugging/internal/stack_consumption.cc b/absl/debugging/internal/stack_consumption.cc
index e3dd51c3..51348649 100644
--- a/absl/debugging/internal/stack_consumption.cc
+++ b/absl/debugging/internal/stack_consumption.cc
@@ -43,7 +43,7 @@ namespace {
// unspecified. Therefore, instead we hardcode the direction of the
// stack on platforms we know about.
#if defined(__i386__) || defined(__x86_64__) || defined(__ppc__) || \
- defined(__aarch64__)
+ defined(__aarch64__) || defined(__riscv)
constexpr bool kStackGrowsDown = true;
#else
#error Need to define kStackGrowsDown
diff --git a/absl/debugging/internal/stack_consumption.h b/absl/debugging/internal/stack_consumption.h
index 2b5e7151..f41b64c3 100644
--- a/absl/debugging/internal/stack_consumption.h
+++ b/absl/debugging/internal/stack_consumption.h
@@ -26,7 +26,7 @@
#error ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION cannot be set directly
#elif !defined(__APPLE__) && !defined(_WIN32) && \
(defined(__i386__) || defined(__x86_64__) || defined(__ppc__) || \
- defined(__aarch64__))
+ defined(__aarch64__) || defined(__riscv))
#define ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION 1
namespace absl {
diff --git a/absl/debugging/internal/stacktrace_aarch64-inl.inc b/absl/debugging/internal/stacktrace_aarch64-inl.inc
index f4859d7c..4f9db9d6 100644
--- a/absl/debugging/internal/stacktrace_aarch64-inl.inc
+++ b/absl/debugging/internal/stacktrace_aarch64-inl.inc
@@ -176,12 +176,17 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
// Implementation detail: we clamp the max of frames we are willing to
// count, so as not to spend too much time in the loop below.
const int kMaxUnwind = 200;
- int j = 0;
- for (; frame_pointer != nullptr && j < kMaxUnwind; j++) {
+ int num_dropped_frames = 0;
+ for (int j = 0; frame_pointer != nullptr && j < kMaxUnwind; j++) {
+ if (skip_count > 0) {
+ skip_count--;
+ } else {
+ num_dropped_frames++;
+ }
frame_pointer =
NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp);
}
- *min_dropped_frames = j;
+ *min_dropped_frames = num_dropped_frames;
}
return n;
}
diff --git a/absl/debugging/internal/stacktrace_arm-inl.inc b/absl/debugging/internal/stacktrace_arm-inl.inc
index 2a1bf2e8..102a2a12 100644
--- a/absl/debugging/internal/stacktrace_arm-inl.inc
+++ b/absl/debugging/internal/stacktrace_arm-inl.inc
@@ -112,11 +112,16 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
// Implementation detail: we clamp the max of frames we are willing to
// count, so as not to spend too much time in the loop below.
const int kMaxUnwind = 200;
- int j = 0;
- for (; sp != nullptr && j < kMaxUnwind; j++) {
+ int num_dropped_frames = 0;
+ for (int j = 0; sp != nullptr && j < kMaxUnwind; j++) {
+ if (skip_count > 0) {
+ skip_count--;
+ } else {
+ num_dropped_frames++;
+ }
sp = NextStackFrame<!IS_STACK_FRAMES>(sp);
}
- *min_dropped_frames = j;
+ *min_dropped_frames = num_dropped_frames;
}
return n;
}
diff --git a/absl/debugging/internal/stacktrace_config.h b/absl/debugging/internal/stacktrace_config.h
index cca410d4..3929b1b7 100644
--- a/absl/debugging/internal/stacktrace_config.h
+++ b/absl/debugging/internal/stacktrace_config.h
@@ -35,7 +35,12 @@
// Thread local support required for UnwindImpl.
#define ABSL_STACKTRACE_INL_HEADER \
"absl/debugging/internal/stacktrace_generic-inl.inc"
-#endif
+#endif // defined(ABSL_HAVE_THREAD_LOCAL)
+
+// Emscripten stacktraces rely on JS. Do not use them in standalone mode.
+#elif defined(__EMSCRIPTEN__) && !defined(STANDALONE_WASM)
+#define ABSL_STACKTRACE_INL_HEADER \
+ "absl/debugging/internal/stacktrace_emscripten-inl.inc"
#elif defined(__linux__) && !defined(__ANDROID__)
@@ -51,7 +56,7 @@
// Note: When using glibc this may require -funwind-tables to function properly.
#define ABSL_STACKTRACE_INL_HEADER \
"absl/debugging/internal/stacktrace_generic-inl.inc"
-#endif
+#endif // __has_include(<execinfo.h>)
#elif defined(__i386__) || defined(__x86_64__)
#define ABSL_STACKTRACE_INL_HEADER \
"absl/debugging/internal/stacktrace_x86-inl.inc"
@@ -61,15 +66,18 @@
#elif defined(__aarch64__)
#define ABSL_STACKTRACE_INL_HEADER \
"absl/debugging/internal/stacktrace_aarch64-inl.inc"
+#elif defined(__riscv)
+#define ABSL_STACKTRACE_INL_HEADER \
+ "absl/debugging/internal/stacktrace_riscv-inl.inc"
#elif defined(__has_include)
#if __has_include(<execinfo.h>)
// Note: When using glibc this may require -funwind-tables to function properly.
#define ABSL_STACKTRACE_INL_HEADER \
"absl/debugging/internal/stacktrace_generic-inl.inc"
-#endif
-#endif
+#endif // __has_include(<execinfo.h>)
+#endif // defined(__has_include)
-#endif
+#endif // defined(__linux__) && !defined(__ANDROID__)
// Fallback to the empty implementation.
#if !defined(ABSL_STACKTRACE_INL_HEADER)
diff --git a/absl/debugging/internal/stacktrace_emscripten-inl.inc b/absl/debugging/internal/stacktrace_emscripten-inl.inc
new file mode 100644
index 00000000..0f444514
--- /dev/null
+++ b/absl/debugging/internal/stacktrace_emscripten-inl.inc
@@ -0,0 +1,110 @@
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Portable implementation - just use glibc
+//
+// Note: The glibc implementation may cause a call to malloc.
+// This can cause a deadlock in HeapProfiler.
+
+#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_EMSCRIPTEN_INL_H_
+#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_EMSCRIPTEN_INL_H_
+
+#include <emscripten.h>
+
+#include <atomic>
+#include <cstring>
+
+#include "absl/base/attributes.h"
+#include "absl/debugging/stacktrace.h"
+
+extern "C" {
+uintptr_t emscripten_stack_snapshot();
+uint32_t emscripten_stack_unwind_buffer(uintptr_t pc, void *buffer,
+ uint32_t depth);
+}
+
+// Sometimes, we can try to get a stack trace from within a stack
+// trace, which can cause a self-deadlock.
+// Protect against such reentrant call by failing to get a stack trace.
+//
+// We use __thread here because the code here is extremely low level -- it is
+// called while collecting stack traces from within malloc and mmap, and thus
+// can not call anything which might call malloc or mmap itself.
+static __thread int recursive = 0;
+
+// The stack trace function might be invoked very early in the program's
+// execution (e.g. from the very first malloc).
+// As such, we suppress usage of backtrace during this early stage of execution.
+static std::atomic<bool> disable_stacktraces(true); // Disabled until healthy.
+// Waiting until static initializers run seems to be late enough.
+// This file is included into stacktrace.cc so this will only run once.
+ABSL_ATTRIBUTE_UNUSED static int stacktraces_enabler = []() {
+ // Check if we can even create stacktraces. If not, bail early and leave
+ // disable_stacktraces set as-is.
+ // clang-format off
+ if (!EM_ASM_INT({ return (typeof wasmOffsetConverter !== 'undefined'); })) {
+ return 0;
+ }
+ // clang-format on
+ disable_stacktraces.store(false, std::memory_order_relaxed);
+ return 0;
+}();
+
+template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
+static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count,
+ const void *ucp, int *min_dropped_frames) {
+ if (recursive || disable_stacktraces.load(std::memory_order_relaxed)) {
+ return 0;
+ }
+ ++recursive;
+
+ static_cast<void>(ucp); // Unused.
+ constexpr int kStackLength = 64;
+ void *stack[kStackLength];
+
+ int size;
+ uintptr_t pc = emscripten_stack_snapshot();
+ size = emscripten_stack_unwind_buffer(pc, stack, kStackLength);
+
+ int result_count = size - skip_count;
+ if (result_count < 0) result_count = 0;
+ if (result_count > max_depth) result_count = max_depth;
+ for (int i = 0; i < result_count; i++) result[i] = stack[i + skip_count];
+
+ if (IS_STACK_FRAMES) {
+ // No implementation for finding out the stack frame sizes yet.
+ memset(sizes, 0, sizeof(*sizes) * result_count);
+ }
+ if (min_dropped_frames != nullptr) {
+ if (size - skip_count - max_depth > 0) {
+ *min_dropped_frames = size - skip_count - max_depth;
+ } else {
+ *min_dropped_frames = 0;
+ }
+ }
+
+ --recursive;
+
+ return result_count;
+}
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace debugging_internal {
+bool StackTraceWorksForTest() { return true; }
+} // namespace debugging_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_EMSCRIPTEN_INL_H_
diff --git a/absl/debugging/internal/stacktrace_powerpc-inl.inc b/absl/debugging/internal/stacktrace_powerpc-inl.inc
index cf8c0516..085cef67 100644
--- a/absl/debugging/internal/stacktrace_powerpc-inl.inc
+++ b/absl/debugging/internal/stacktrace_powerpc-inl.inc
@@ -231,11 +231,16 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
// Implementation detail: we clamp the max of frames we are willing to
// count, so as not to spend too much time in the loop below.
const int kMaxUnwind = 1000;
- int j = 0;
- for (; next_sp != nullptr && j < kMaxUnwind; j++) {
+ int num_dropped_frames = 0;
+ for (int j = 0; next_sp != nullptr && j < kMaxUnwind; j++) {
+ if (skip_count > 0) {
+ skip_count--;
+ } else {
+ num_dropped_frames++;
+ }
next_sp = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(next_sp, ucp);
}
- *min_dropped_frames = j;
+ *min_dropped_frames = num_dropped_frames;
}
return n;
}
diff --git a/absl/debugging/internal/stacktrace_riscv-inl.inc b/absl/debugging/internal/stacktrace_riscv-inl.inc
new file mode 100644
index 00000000..7123b71b
--- /dev/null
+++ b/absl/debugging/internal/stacktrace_riscv-inl.inc
@@ -0,0 +1,236 @@
+// Copyright 2021 The Abseil Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_RISCV_INL_H_
+#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_RISCV_INL_H_
+
+// Generate stack trace for riscv
+
+#include <sys/ucontext.h>
+
+#include "absl/base/config.h"
+#if defined(__linux__)
+#include <sys/mman.h>
+#include <ucontext.h>
+#include <unistd.h>
+#endif
+
+#include <atomic>
+#include <cassert>
+#include <cstdint>
+#include <iostream>
+
+#include "absl/base/attributes.h"
+#include "absl/debugging/internal/address_is_readable.h"
+#include "absl/debugging/internal/vdso_support.h"
+#include "absl/debugging/stacktrace.h"
+
+static const uintptr_t kUnknownFrameSize = 0;
+
+#if defined(__linux__)
+// Returns the address of the VDSO __kernel_rt_sigreturn function, if present.
+static const unsigned char *GetKernelRtSigreturnAddress() {
+ constexpr uintptr_t kImpossibleAddress = 0;
+ ABSL_CONST_INIT static std::atomic<uintptr_t> memoized(kImpossibleAddress);
+ uintptr_t address = memoized.load(std::memory_order_relaxed);
+ if (address != kImpossibleAddress) {
+ return reinterpret_cast<const unsigned char *>(address);
+ }
+
+ address = reinterpret_cast<uintptr_t>(nullptr);
+
+#if ABSL_HAVE_VDSO_SUPPORT
+ absl::debugging_internal::VDSOSupport vdso;
+ if (vdso.IsPresent()) {
+ absl::debugging_internal::VDSOSupport::SymbolInfo symbol_info;
+ // Symbol versioning pulled from arch/riscv/kernel/vdso/vdso.lds at v5.10.
+ auto lookup = [&](int type) {
+ return vdso.LookupSymbol("__vdso_rt_sigreturn", "LINUX_4.15", type,
+ &symbol_info);
+ };
+ if ((!lookup(STT_FUNC) && !lookup(STT_NOTYPE)) ||
+ symbol_info.address == nullptr) {
+ // Unexpected: VDSO is present, yet the expected symbol is missing or
+ // null.
+ assert(false && "VDSO is present, but doesn't have expected symbol");
+ } else {
+ if (reinterpret_cast<uintptr_t>(symbol_info.address) !=
+ kImpossibleAddress) {
+ address = reinterpret_cast<uintptr_t>(symbol_info.address);
+ } else {
+ assert(false && "VDSO returned invalid address");
+ }
+ }
+ }
+#endif
+
+ memoized.store(address, std::memory_order_relaxed);
+ return reinterpret_cast<const unsigned char *>(address);
+}
+#endif // __linux__
+
+// Compute the size of a stack frame in [low..high). We assume that low < high.
+// Return size of kUnknownFrameSize.
+template <typename T>
+static inline uintptr_t ComputeStackFrameSize(const T *low, const T *high) {
+ const char *low_char_ptr = reinterpret_cast<const char *>(low);
+ const char *high_char_ptr = reinterpret_cast<const char *>(high);
+ return low < high ? high_char_ptr - low_char_ptr : kUnknownFrameSize;
+}
+
+// Given a pointer to a stack frame, locate and return the calling stackframe,
+// or return null if no stackframe can be found. Perform sanity checks (the
+// strictness of which is controlled by the boolean parameter
+// "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned.
+template <bool STRICT_UNWINDING, bool WITH_CONTEXT>
+ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack.
+ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack.
+static void ** NextStackFrame(void **old_frame_pointer, const void *uc) {
+ // .
+ // .
+ // .
+ // +-> +----------------+
+ // | | return address |
+ // | | previous fp |
+ // | | ... |
+ // | +----------------+ <-+
+ // | | return address | |
+ // +---|- previous fp | |
+ // | ... | |
+ // $fp ->|----------------+ |
+ // | return address | |
+ // | previous fp -|---+
+ // $sp ->| ... |
+ // +----------------+
+ void **new_frame_pointer = reinterpret_cast<void **>(old_frame_pointer[-2]);
+ bool check_frame_size = true;
+
+#if defined(__linux__)
+ if (WITH_CONTEXT && uc != nullptr) {
+ // Check to see if next frame's return address is __kernel_rt_sigreturn.
+ if (old_frame_pointer[-1] == GetKernelRtSigreturnAddress()) {
+ const ucontext_t *ucv = static_cast<const ucontext_t *>(uc);
+ // old_frame_pointer is not suitable for unwinding, look at ucontext to
+ // discover frame pointer before signal.
+ //
+ // RISCV ELF psABI has the frame pointer at x8/fp/s0.
+ // -- RISCV psABI Table 18.2
+ void **const pre_signal_frame_pointer =
+ reinterpret_cast<void **>(ucv->uc_mcontext.__gregs[8]);
+
+ // Check the alleged frame pointer is actually readable. This is to
+ // prevent "double fault" in case we hit the first fault due to stack
+ // corruption.
+ if (!absl::debugging_internal::AddressIsReadable(
+ pre_signal_frame_pointer))
+ return nullptr;
+
+ // Alleged frame pointer is readable, use it for further unwinding.
+ new_frame_pointer = pre_signal_frame_pointer;
+
+ // Skip frame size check if we return from a signal. We may be using an
+ // alterate stack for signals.
+ check_frame_size = false;
+ }
+ }
+#endif
+
+ // The RISCV ELF psABI mandates that the stack pointer is always 16-byte
+ // aligned.
+ // FIXME(abdulras) this doesn't hold for ILP32E which only mandates a 4-byte
+ // alignment.
+ if ((reinterpret_cast<uintptr_t>(new_frame_pointer) & 15) != 0)
+ return nullptr;
+
+ // Check frame size. In strict mode, we assume frames to be under 100,000
+ // bytes. In non-strict mode, we relax the limit to 1MB.
+ if (check_frame_size) {
+ const uintptr_t max_size = STRICT_UNWINDING ? 100000 : 1000000;
+ const uintptr_t frame_size =
+ ComputeStackFrameSize(old_frame_pointer, new_frame_pointer);
+ if (frame_size == kUnknownFrameSize || frame_size > max_size)
+ return nullptr;
+ }
+
+ return new_frame_pointer;
+}
+
+template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
+ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack.
+ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack.
+static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count,
+ const void *ucp, int *min_dropped_frames) {
+ // The `frame_pointer` that is computed here points to the top of the frame.
+ // The two words preceding the address are the return address and the previous
+ // frame pointer.
+#if defined(__GNUC__)
+ void **frame_pointer = reinterpret_cast<void **>(__builtin_frame_address(0));
+#else
+#error reading stack pointer not yet supported on this platform
+#endif
+
+ int n = 0;
+ void *return_address = nullptr;
+ while (frame_pointer && n < max_depth) {
+ return_address = frame_pointer[-1];
+
+ // The absl::GetStackFrames routine is called when we are in some
+ // informational context (the failure signal handler for example). Use the
+ // non-strict unwinding rules to produce a stack trace that is as complete
+ // as possible (even if it contains a few bogus entries in some rare cases).
+ void **next_frame_pointer =
+ NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp);
+
+ if (skip_count > 0) {
+ skip_count--;
+ } else {
+ result[n] = return_address;
+ if (IS_STACK_FRAMES) {
+ sizes[n] = ComputeStackFrameSize(frame_pointer, next_frame_pointer);
+ }
+ n++;
+ }
+
+ frame_pointer = next_frame_pointer;
+ }
+
+ if (min_dropped_frames != nullptr) {
+ // Implementation detail: we clamp the max of frames we are willing to
+ // count, so as not to spend too much time in the loop below.
+ const int kMaxUnwind = 200;
+ int num_dropped_frames = 0;
+ for (int j = 0; frame_pointer != nullptr && j < kMaxUnwind; j++) {
+ if (skip_count > 0) {
+ skip_count--;
+ } else {
+ num_dropped_frames++;
+ }
+ frame_pointer =
+ NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp);
+ }
+ *min_dropped_frames = num_dropped_frames;
+ }
+
+ return n;
+}
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace debugging_internal {
+bool StackTraceWorksForTest() { return true; }
+} // namespace debugging_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif
diff --git a/absl/debugging/internal/stacktrace_x86-inl.inc b/absl/debugging/internal/stacktrace_x86-inl.inc
index bc320ff7..1b5d8235 100644
--- a/absl/debugging/internal/stacktrace_x86-inl.inc
+++ b/absl/debugging/internal/stacktrace_x86-inl.inc
@@ -27,6 +27,7 @@
#include <cassert>
#include <cstdint>
+#include <limits>
#include "absl/base/macros.h"
#include "absl/base/port.h"
@@ -132,9 +133,8 @@ static uintptr_t GetFP(const void *vuc) {
const uintptr_t bp = 0;
const uintptr_t sp = 0;
#endif
- // Sanity-check that the base pointer is valid. It should be as long as
- // SHRINK_WRAP_FRAME_POINTER is not set, but it's possible that some code in
- // the process is compiled with --copt=-fomit-frame-pointer or
+ // Sanity-check that the base pointer is valid. It's possible that some
+ // code in the process is compiled with --copt=-fomit-frame-pointer or
// --copt=-momit-leaf-frame-pointer.
//
// TODO(bcmills): -momit-leaf-frame-pointer is currently the default
@@ -159,7 +159,8 @@ static uintptr_t GetFP(const void *vuc) {
template <bool STRICT_UNWINDING, bool WITH_CONTEXT>
ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack.
ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack.
-static void **NextStackFrame(void **old_fp, const void *uc) {
+static void **NextStackFrame(void **old_fp, const void *uc,
+ size_t stack_low, size_t stack_high) {
void **new_fp = (void **)*old_fp;
#if defined(__linux__) && defined(__i386__)
@@ -247,7 +248,7 @@ static void **NextStackFrame(void **old_fp, const void *uc) {
// using an alternate signal stack.
//
// TODO(bcmills): The GetFP call should be completely unnecessary when
- // SHRINK_WRAP_FRAME_POINTER is set (because we should be back in the thread's
+ // ENABLE_COMBINED_UNWINDER is set (because we should be back in the thread's
// stack by this point), but it is empirically still needed (e.g. when the
// stack includes a call to abort). unw_get_reg returns UNW_EBADREG for some
// frames. Figure out why GetValidFrameAddr and/or libunwind isn't doing what
@@ -258,6 +259,18 @@ static void **NextStackFrame(void **old_fp, const void *uc) {
// at a greater address that the current one.
if (new_fp_u <= old_fp_u) return nullptr;
if (new_fp_u - old_fp_u > kMaxFrameBytes) return nullptr;
+
+ if (stack_low < old_fp_u && old_fp_u <= stack_high) {
+ // Old BP was in the expected stack region...
+ if (!(stack_low < new_fp_u && new_fp_u <= stack_high)) {
+ // ... but new BP is outside of expected stack region.
+ // It is most likely bogus.
+ return nullptr;
+ }
+ } else {
+ // We may be here if we are executing in a co-routine with a
+ // separate stack. We can't do safety checks in this case.
+ }
} else {
if (new_fp == nullptr) return nullptr; // skip AddressIsReadable() below
// In the non-strict mode, allow discontiguous stack frames.
@@ -297,13 +310,17 @@ static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count,
int n = 0;
void **fp = reinterpret_cast<void **>(__builtin_frame_address(0));
+ size_t stack_low = getpagesize(); // Assume that the first page is not stack.
+ size_t stack_high = std::numeric_limits<size_t>::max() - sizeof(void *);
+
while (fp && n < max_depth) {
if (*(fp + 1) == reinterpret_cast<void *>(0)) {
// In 64-bit code, we often see a frame that
// points to itself and has a return address of 0.
break;
}
- void **next_fp = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(fp, ucp);
+ void **next_fp = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(
+ fp, ucp, stack_low, stack_high);
if (skip_count > 0) {
skip_count--;
} else {
@@ -324,11 +341,17 @@ static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count,
// Implementation detail: we clamp the max of frames we are willing to
// count, so as not to spend too much time in the loop below.
const int kMaxUnwind = 1000;
- int j = 0;
- for (; fp != nullptr && j < kMaxUnwind; j++) {
- fp = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(fp, ucp);
+ int num_dropped_frames = 0;
+ for (int j = 0; fp != nullptr && j < kMaxUnwind; j++) {
+ if (skip_count > 0) {
+ skip_count--;
+ } else {
+ num_dropped_frames++;
+ }
+ fp = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(fp, ucp, stack_low,
+ stack_high);
}
- *min_dropped_frames = j;
+ *min_dropped_frames = num_dropped_frames;
}
return n;
}
diff --git a/absl/debugging/internal/symbolize.h b/absl/debugging/internal/symbolize.h
index 4f26130f..27d5e652 100644
--- a/absl/debugging/internal/symbolize.h
+++ b/absl/debugging/internal/symbolize.h
@@ -28,8 +28,8 @@
#ifdef ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE
#error ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE cannot be directly set
-#elif defined(__ELF__) && defined(__GLIBC__) && !defined(__native_client__) && \
- !defined(__asmjs__) && !defined(__wasm__)
+#elif defined(__ELF__) && defined(__GLIBC__) && !defined(__native_client__) \
+ && !defined(__asmjs__) && !defined(__wasm__)
#define ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE 1
#include <elf.h>
@@ -68,6 +68,12 @@ ABSL_NAMESPACE_END
#define ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE 1
#endif
+#ifdef ABSL_INTERNAL_HAVE_EMSCRIPTEN_SYMBOLIZE
+#error ABSL_INTERNAL_HAVE_EMSCRIPTEN_SYMBOLIZE cannot be directly set
+#elif defined(__EMSCRIPTEN__)
+#define ABSL_INTERNAL_HAVE_EMSCRIPTEN_SYMBOLIZE 1
+#endif
+
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
diff --git a/absl/debugging/internal/vdso_support.cc b/absl/debugging/internal/vdso_support.cc
index 6be16d90..40eb055f 100644
--- a/absl/debugging/internal/vdso_support.cc
+++ b/absl/debugging/internal/vdso_support.cc
@@ -20,12 +20,25 @@
#ifdef ABSL_HAVE_VDSO_SUPPORT // defined in vdso_support.h
+#if !defined(__has_include)
+#define __has_include(header) 0
+#endif
+
#include <errno.h>
#include <fcntl.h>
+#if __has_include(<syscall.h>)
+#include <syscall.h>
+#elif __has_include(<sys/syscall.h>)
#include <sys/syscall.h>
+#endif
#include <unistd.h>
-#if __GLIBC_PREREQ(2, 16) // GLIBC-2.16 implements getauxval.
+#if !defined(__UCLIBC__) && defined(__GLIBC__) && \
+ (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 16))
+#define ABSL_HAVE_GETAUXVAL
+#endif
+
+#ifdef ABSL_HAVE_GETAUXVAL
#include <sys/auxv.h>
#endif
@@ -37,6 +50,17 @@
#define AT_SYSINFO_EHDR 33 // for crosstoolv10
#endif
+#if defined(__NetBSD__)
+using Elf32_auxv_t = Aux32Info;
+using Elf64_auxv_t = Aux64Info;
+#endif
+#if defined(__FreeBSD__)
+#if defined(__ELF_WORD_SIZE) && __ELF_WORD_SIZE == 64
+using Elf64_auxv_t = Elf64_Auxinfo;
+#endif
+using Elf32_auxv_t = Elf32_Auxinfo;
+#endif
+
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
@@ -45,7 +69,9 @@ ABSL_CONST_INIT
std::atomic<const void *> VDSOSupport::vdso_base_(
debugging_internal::ElfMemImage::kInvalidBase);
-std::atomic<VDSOSupport::GetCpuFn> VDSOSupport::getcpu_fn_(&InitAndGetCPU);
+ABSL_CONST_INIT std::atomic<VDSOSupport::GetCpuFn> VDSOSupport::getcpu_fn_(
+ &InitAndGetCPU);
+
VDSOSupport::VDSOSupport()
// If vdso_base_ is still set to kInvalidBase, we got here
// before VDSOSupport::Init has been called. Call it now.
@@ -65,7 +91,7 @@ VDSOSupport::VDSOSupport()
// the operation should be idempotent.
const void *VDSOSupport::Init() {
const auto kInvalidBase = debugging_internal::ElfMemImage::kInvalidBase;
-#if __GLIBC_PREREQ(2, 16)
+#ifdef ABSL_HAVE_GETAUXVAL
if (vdso_base_.load(std::memory_order_relaxed) == kInvalidBase) {
errno = 0;
const void *const sysinfo_ehdr =
@@ -74,7 +100,7 @@ const void *VDSOSupport::Init() {
vdso_base_.store(sysinfo_ehdr, std::memory_order_relaxed);
}
}
-#endif // __GLIBC_PREREQ(2, 16)
+#endif // ABSL_HAVE_GETAUXVAL
if (vdso_base_.load(std::memory_order_relaxed) == kInvalidBase) {
int fd = open("/proc/self/auxv", O_RDONLY);
if (fd == -1) {
@@ -86,8 +112,13 @@ const void *VDSOSupport::Init() {
ElfW(auxv_t) aux;
while (read(fd, &aux, sizeof(aux)) == sizeof(aux)) {
if (aux.a_type == AT_SYSINFO_EHDR) {
+#if defined(__NetBSD__)
+ vdso_base_.store(reinterpret_cast<void *>(aux.a_v),
+ std::memory_order_relaxed);
+#else
vdso_base_.store(reinterpret_cast<void *>(aux.a_un.a_val),
std::memory_order_relaxed);
+#endif
break;
}
}