mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-12-12 14:46:08 +00:00
Update vendored deps
This commit is contained in:
120
third_party/abseil-cpp/absl/debugging/BUILD.bazel
vendored
120
third_party/abseil-cpp/absl/debugging/BUILD.bazel
vendored
@@ -14,7 +14,6 @@
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
|
||||
load(
|
||||
"//absl:copts/configure_copts.bzl",
|
||||
"ABSL_DEFAULT_COPTS",
|
||||
@@ -122,7 +121,7 @@ cc_library(
|
||||
],
|
||||
copts = ABSL_DEFAULT_COPTS,
|
||||
linkopts = ABSL_DEFAULT_LINKOPTS,
|
||||
visibility = ["//visibility:private"],
|
||||
visibility = ["//absl/log/internal:__pkg__"],
|
||||
deps = [
|
||||
":stacktrace",
|
||||
":symbolize",
|
||||
@@ -144,7 +143,6 @@ cc_library(
|
||||
"//absl/base",
|
||||
"//absl/base:config",
|
||||
"//absl/base:core_headers",
|
||||
"//absl/base:errno_saver",
|
||||
"//absl/base:raw_logging_internal",
|
||||
],
|
||||
)
|
||||
@@ -183,6 +181,7 @@ cc_library(
|
||||
],
|
||||
copts = ABSL_DEFAULT_COPTS,
|
||||
linkopts = ABSL_DEFAULT_LINKOPTS,
|
||||
visibility = ["//visibility:private"],
|
||||
deps = [
|
||||
"//absl/base:config",
|
||||
"//absl/base:core_headers",
|
||||
@@ -197,6 +196,8 @@ cc_library(
|
||||
srcs = ["internal/demangle.cc"],
|
||||
hdrs = ["internal/demangle.h"],
|
||||
copts = ABSL_DEFAULT_COPTS,
|
||||
linkopts = ABSL_DEFAULT_LINKOPTS,
|
||||
visibility = ["//visibility:private"],
|
||||
deps = [
|
||||
"//absl/base",
|
||||
"//absl/base:config",
|
||||
@@ -224,6 +225,7 @@ cc_library(
|
||||
name = "leak_check",
|
||||
srcs = ["leak_check.cc"],
|
||||
hdrs = ["leak_check.h"],
|
||||
copts = ABSL_DEFAULT_COPTS,
|
||||
linkopts = ABSL_DEFAULT_LINKOPTS,
|
||||
deps = [
|
||||
"//absl/base:config",
|
||||
@@ -231,98 +233,33 @@ cc_library(
|
||||
],
|
||||
)
|
||||
|
||||
# Adding a dependency to leak_check_disable will disable
|
||||
# sanitizer leak checking (asan/lsan) in a test without
|
||||
# the need to mess around with build features.
|
||||
cc_library(
|
||||
name = "leak_check_disable",
|
||||
srcs = ["leak_check_disable.cc"],
|
||||
linkopts = ABSL_DEFAULT_LINKOPTS,
|
||||
linkstatic = 1,
|
||||
deps = ["//absl/base:config"],
|
||||
alwayslink = 1,
|
||||
)
|
||||
|
||||
# These targets exists for use in tests only, explicitly configuring the
|
||||
# LEAK_SANITIZER macro. It must be linked with -fsanitize=leak for lsan.
|
||||
ABSL_LSAN_LINKOPTS = select({
|
||||
"//absl:clang_compiler": ["-fsanitize=leak"],
|
||||
"//conditions:default": [],
|
||||
})
|
||||
|
||||
cc_library(
|
||||
name = "leak_check_api_enabled_for_testing",
|
||||
testonly = 1,
|
||||
srcs = ["leak_check.cc"],
|
||||
hdrs = ["leak_check.h"],
|
||||
copts = select({
|
||||
"//absl:clang_compiler": ["-DLEAK_SANITIZER"],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
linkopts = ABSL_DEFAULT_LINKOPTS,
|
||||
visibility = ["//visibility:private"],
|
||||
deps = [
|
||||
"//absl/base:config",
|
||||
"//absl/base:core_headers",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "leak_check_api_disabled_for_testing",
|
||||
testonly = 1,
|
||||
srcs = ["leak_check.cc"],
|
||||
hdrs = ["leak_check.h"],
|
||||
copts = ["-ULEAK_SANITIZER"],
|
||||
linkopts = ABSL_DEFAULT_LINKOPTS,
|
||||
visibility = ["//visibility:private"],
|
||||
deps = [
|
||||
"//absl/base:config",
|
||||
"//absl/base:core_headers",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "leak_check_test",
|
||||
srcs = ["leak_check_test.cc"],
|
||||
copts = select({
|
||||
"//absl:clang_compiler": ["-DABSL_EXPECT_LEAK_SANITIZER"],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
linkopts = ABSL_LSAN_LINKOPTS + ABSL_DEFAULT_LINKOPTS,
|
||||
tags = ["notsan"],
|
||||
deps = [
|
||||
":leak_check_api_enabled_for_testing",
|
||||
"//absl/base",
|
||||
"@com_google_googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "leak_check_no_lsan_test",
|
||||
srcs = ["leak_check_test.cc"],
|
||||
copts = ["-UABSL_EXPECT_LEAK_SANITIZER"],
|
||||
copts = ABSL_TEST_COPTS,
|
||||
linkopts = ABSL_DEFAULT_LINKOPTS,
|
||||
tags = ["noasan"],
|
||||
tags = ["notsan"],
|
||||
deps = [
|
||||
":leak_check_api_disabled_for_testing",
|
||||
"//absl/base", # for raw_logging
|
||||
":leak_check",
|
||||
"//absl/base:config",
|
||||
"//absl/base:raw_logging_internal",
|
||||
"@com_google_googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
|
||||
# Test that leak checking is skipped when lsan is enabled but
|
||||
# ":leak_check_disable" is linked in.
|
||||
#
|
||||
# This test should fail in the absence of a dependency on ":leak_check_disable"
|
||||
cc_test(
|
||||
name = "disabled_leak_check_test",
|
||||
# Binary that leaks memory and expects to fail on exit. This isn't a
|
||||
# test that expected to pass on its own; it exists to be called by a
|
||||
# script that checks exit status and output.
|
||||
# TODO(absl-team): Write a test to run this with a script that
|
||||
# verifies that it correctly fails.
|
||||
cc_binary(
|
||||
name = "leak_check_fail_test_binary",
|
||||
srcs = ["leak_check_fail_test.cc"],
|
||||
linkopts = ABSL_LSAN_LINKOPTS + ABSL_DEFAULT_LINKOPTS,
|
||||
tags = ["notsan"],
|
||||
copts = ABSL_TEST_COPTS,
|
||||
linkopts = ABSL_DEFAULT_LINKOPTS,
|
||||
deps = [
|
||||
":leak_check_api_enabled_for_testing",
|
||||
":leak_check_disable",
|
||||
"//absl/base",
|
||||
":leak_check",
|
||||
"//absl/base:raw_logging_internal",
|
||||
"@com_google_googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
@@ -355,3 +292,18 @@ cc_test(
|
||||
"@com_google_googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "stacktrace_benchmark",
|
||||
testonly = 1,
|
||||
srcs = ["stacktrace_benchmark.cc"],
|
||||
copts = ABSL_TEST_COPTS,
|
||||
linkopts = ABSL_DEFAULT_LINKOPTS,
|
||||
tags = ["benchmark"],
|
||||
deps = [
|
||||
":stacktrace",
|
||||
"//absl/base:config",
|
||||
"//absl/base:core_headers",
|
||||
"@com_github_google_benchmark//:benchmark_main",
|
||||
],
|
||||
)
|
||||
|
||||
37
third_party/abseil-cpp/absl/debugging/BUILD.gn
vendored
37
third_party/abseil-cpp/absl/debugging/BUILD.gn
vendored
@@ -61,7 +61,10 @@ absl_source_set("symbolize") {
|
||||
absl_source_set("examine_stack") {
|
||||
sources = [ "internal/examine_stack.cc" ]
|
||||
public = [ "internal/examine_stack.h" ]
|
||||
visibility = [ ":*" ]
|
||||
visibility = [
|
||||
":*",
|
||||
"//third_party/abseil-cpp/absl/log/internal:*",
|
||||
]
|
||||
deps = [
|
||||
":stacktrace",
|
||||
":symbolize",
|
||||
@@ -80,7 +83,6 @@ absl_source_set("failure_signal_handler") {
|
||||
"//third_party/abseil-cpp/absl/base",
|
||||
"//third_party/abseil-cpp/absl/base:config",
|
||||
"//third_party/abseil-cpp/absl/base:core_headers",
|
||||
"//third_party/abseil-cpp/absl/base:errno_saver",
|
||||
"//third_party/abseil-cpp/absl/base:raw_logging_internal",
|
||||
]
|
||||
}
|
||||
@@ -96,6 +98,7 @@ absl_source_set("debugging_internal") {
|
||||
"internal/elf_mem_image.h",
|
||||
"internal/vdso_support.h",
|
||||
]
|
||||
visibility = [ ":*" ]
|
||||
deps = [
|
||||
"//third_party/abseil-cpp/absl/base:config",
|
||||
"//third_party/abseil-cpp/absl/base:core_headers",
|
||||
@@ -108,6 +111,7 @@ absl_source_set("debugging_internal") {
|
||||
absl_source_set("demangle_internal") {
|
||||
sources = [ "internal/demangle.cc" ]
|
||||
public = [ "internal/demangle.h" ]
|
||||
visibility = [ ":*" ]
|
||||
deps = [
|
||||
"//third_party/abseil-cpp/absl/base",
|
||||
"//third_party/abseil-cpp/absl/base:config",
|
||||
@@ -129,35 +133,6 @@ absl_source_set("leak_check") {
|
||||
]
|
||||
}
|
||||
|
||||
absl_source_set("leak_check_disable") {
|
||||
sources = [ "leak_check_disable.cc" ]
|
||||
deps = [ "//third_party/abseil-cpp/absl/base:config" ]
|
||||
}
|
||||
|
||||
if (is_lsan) {
|
||||
absl_source_set("leak_check_api_enabled_for_testing") {
|
||||
testonly = true
|
||||
sources = [ "leak_check.cc" ]
|
||||
public = [ "leak_check.h" ]
|
||||
visibility = [ ":*" ]
|
||||
deps = [
|
||||
"//third_party/abseil-cpp/absl/base:config",
|
||||
"//third_party/abseil-cpp/absl/base:core_headers",
|
||||
]
|
||||
}
|
||||
} else {
|
||||
absl_source_set("leak_check_api_disabled_for_testing") {
|
||||
testonly = true
|
||||
sources = [ "leak_check.cc" ]
|
||||
public = [ "leak_check.h" ]
|
||||
visibility = [ ":*" ]
|
||||
deps = [
|
||||
"//third_party/abseil-cpp/absl/base:config",
|
||||
"//third_party/abseil-cpp/absl/base:core_headers",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
absl_source_set("stack_consumption") {
|
||||
testonly = true
|
||||
sources = [ "internal/stack_consumption.cc" ]
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
find_library(EXECINFO_LIBRARY execinfo)
|
||||
|
||||
absl_cc_library(
|
||||
NAME
|
||||
stacktrace
|
||||
@@ -33,6 +35,8 @@ absl_cc_library(
|
||||
"stacktrace.cc"
|
||||
COPTS
|
||||
${ABSL_DEFAULT_COPTS}
|
||||
LINKOPTS
|
||||
$<$<BOOL:${EXECINFO_LIBRARY}>:${EXECINFO_LIBRARY}>
|
||||
DEPS
|
||||
absl::debugging_internal
|
||||
absl::config
|
||||
@@ -93,6 +97,7 @@ absl_cc_test(
|
||||
GTest::gmock
|
||||
)
|
||||
|
||||
# Internal-only target, do not depend on directly.
|
||||
absl_cc_library(
|
||||
NAME
|
||||
examine_stack
|
||||
@@ -125,7 +130,6 @@ absl_cc_library(
|
||||
absl::base
|
||||
absl::config
|
||||
absl::core_headers
|
||||
absl::errno_saver
|
||||
absl::raw_logging_internal
|
||||
PUBLIC
|
||||
)
|
||||
@@ -147,6 +151,7 @@ absl_cc_test(
|
||||
GTest::gmock
|
||||
)
|
||||
|
||||
# Internal-only target, do not depend on directly.
|
||||
absl_cc_library(
|
||||
NAME
|
||||
debugging_internal
|
||||
@@ -168,6 +173,7 @@ absl_cc_library(
|
||||
absl::raw_logging_internal
|
||||
)
|
||||
|
||||
# Internal-only target, do not depend on directly.
|
||||
absl_cc_library(
|
||||
NAME
|
||||
demangle_internal
|
||||
@@ -215,42 +221,6 @@ absl_cc_library(
|
||||
PUBLIC
|
||||
)
|
||||
|
||||
absl_cc_library(
|
||||
NAME
|
||||
leak_check_disable
|
||||
SRCS
|
||||
"leak_check_disable.cc"
|
||||
COPTS
|
||||
${ABSL_DEFAULT_COPTS}
|
||||
PUBLIC
|
||||
)
|
||||
|
||||
absl_cc_library(
|
||||
NAME
|
||||
leak_check_api_enabled_for_testing
|
||||
HDRS
|
||||
"leak_check.h"
|
||||
SRCS
|
||||
"leak_check.cc"
|
||||
COPTS
|
||||
${ABSL_DEFAULT_COPTS}
|
||||
$<$<BOOL:${ABSL_HAVE_LSAN}>:-DLEAK_SANITIZER>
|
||||
TESTONLY
|
||||
)
|
||||
|
||||
absl_cc_library(
|
||||
NAME
|
||||
leak_check_api_disabled_for_testing
|
||||
HDRS
|
||||
"leak_check.h"
|
||||
SRCS
|
||||
"leak_check.cc"
|
||||
COPTS
|
||||
${ABSL_DEFAULT_COPTS}
|
||||
"-ULEAK_SANITIZER"
|
||||
TESTONLY
|
||||
)
|
||||
|
||||
absl_cc_test(
|
||||
NAME
|
||||
leak_check_test
|
||||
@@ -258,46 +228,15 @@ absl_cc_test(
|
||||
"leak_check_test.cc"
|
||||
COPTS
|
||||
${ABSL_TEST_COPTS}
|
||||
"$<$<BOOL:${ABSL_HAVE_LSAN}>:-DABSL_EXPECT_LEAK_SANITIZER>"
|
||||
LINKOPTS
|
||||
"${ABSL_LSAN_LINKOPTS}"
|
||||
${ABSL_DEFAULT_LINKOPTS}
|
||||
DEPS
|
||||
absl::leak_check_api_enabled_for_testing
|
||||
absl::leak_check
|
||||
absl::base
|
||||
GTest::gmock_main
|
||||
)
|
||||
|
||||
absl_cc_test(
|
||||
NAME
|
||||
leak_check_no_lsan_test
|
||||
SRCS
|
||||
"leak_check_test.cc"
|
||||
COPTS
|
||||
${ABSL_TEST_COPTS}
|
||||
"-UABSL_EXPECT_LEAK_SANITIZER"
|
||||
DEPS
|
||||
absl::leak_check_api_disabled_for_testing
|
||||
absl::base
|
||||
GTest::gmock_main
|
||||
)
|
||||
|
||||
absl_cc_test(
|
||||
NAME
|
||||
disabled_leak_check_test
|
||||
SRCS
|
||||
"leak_check_fail_test.cc"
|
||||
COPTS
|
||||
${ABSL_TEST_COPTS}
|
||||
LINKOPTS
|
||||
"${ABSL_LSAN_LINKOPTS}"
|
||||
DEPS
|
||||
absl::leak_check_api_enabled_for_testing
|
||||
absl::leak_check_disable
|
||||
absl::base
|
||||
absl::raw_logging_internal
|
||||
GTest::gmock_main
|
||||
)
|
||||
|
||||
# Internal-only target, do not depend on directly.
|
||||
absl_cc_library(
|
||||
NAME
|
||||
stack_consumption
|
||||
|
||||
@@ -42,7 +42,6 @@
|
||||
#include <ctime>
|
||||
|
||||
#include "absl/base/attributes.h"
|
||||
#include "absl/base/internal/errno_saver.h"
|
||||
#include "absl/base/internal/raw_logging.h"
|
||||
#include "absl/base/internal/sysinfo.h"
|
||||
#include "absl/debugging/internal/examine_stack.h"
|
||||
@@ -52,7 +51,7 @@
|
||||
#define ABSL_HAVE_SIGACTION
|
||||
// Apple WatchOS and TVOS don't allow sigaltstack
|
||||
#if !(defined(TARGET_OS_WATCH) && TARGET_OS_WATCH) && \
|
||||
!(defined(TARGET_OS_TV) && TARGET_OS_TV)
|
||||
!(defined(TARGET_OS_TV) && TARGET_OS_TV) && !defined(__QNX__)
|
||||
#define ABSL_HAVE_SIGALTSTACK
|
||||
#endif
|
||||
#endif
|
||||
@@ -217,8 +216,7 @@ static void InstallOneFailureHandler(FailureSignalData* data,
|
||||
#endif
|
||||
|
||||
static void WriteToStderr(const char* data) {
|
||||
absl::base_internal::ErrnoSaver errno_saver;
|
||||
absl::raw_logging_internal::SafeWriteToStderr(data, strlen(data));
|
||||
absl::raw_logging_internal::AsyncSignalSafeWriteToStderr(data, strlen(data));
|
||||
}
|
||||
|
||||
static void WriteSignalMessage(int signo, int cpu,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
// 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);
|
||||
|
||||
// rt_sigprocmask below will succeed for this input.
|
||||
if (addr == nullptr) return false;
|
||||
|
||||
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__
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,22 +253,63 @@ 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
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -35,9 +35,10 @@
|
||||
// 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)
|
||||
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
// 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"
|
||||
|
||||
@@ -55,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"
|
||||
@@ -73,9 +74,10 @@
|
||||
// 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
|
||||
#endif // __has_include(<execinfo.h>)
|
||||
#endif // defined(__has_include)
|
||||
|
||||
#endif // defined(__linux__) && !defined(__ANDROID__)
|
||||
|
||||
// Fallback to the empty implementation.
|
||||
#if !defined(ABSL_STACKTRACE_INL_HEADER)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ static const unsigned char *GetKernelRtSigreturnAddress() {
|
||||
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("__kernel_rt_sigreturn", "LINUX_4.15", type,
|
||||
return vdso.LookupSymbol("__vdso_rt_sigreturn", "LINUX_4.15", type,
|
||||
&symbol_info);
|
||||
};
|
||||
if ((!lookup(STT_FUNC) && !lookup(STT_NOTYPE)) ||
|
||||
@@ -159,6 +159,21 @@ static void ** NextStackFrame(void **old_frame_pointer, const void *uc) {
|
||||
const uintptr_t max_size = STRICT_UNWINDING ? 100000 : 1000000;
|
||||
const uintptr_t frame_size =
|
||||
ComputeStackFrameSize(old_frame_pointer, new_frame_pointer);
|
||||
|
||||
// If we have a alternate signal stack, the stack pointer may not be
|
||||
// contiguous. In such a case, we can simply skip the check and assume that
|
||||
// the non-contiguity is permissible.
|
||||
if (frame_size == kUnknownFrameSize) {
|
||||
assert(old_frame_pointer >= new_frame_pointer);
|
||||
|
||||
stack_t ss{};
|
||||
if (sigaltstack(nullptr, &ss) == 0) {
|
||||
if (ss.ss_flags & SS_DISABLE)
|
||||
return nullptr;
|
||||
return new_frame_pointer;
|
||||
}
|
||||
}
|
||||
|
||||
if (frame_size == kUnknownFrameSize || frame_size > max_size)
|
||||
return nullptr;
|
||||
}
|
||||
@@ -171,26 +186,21 @@ 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
|
||||
|
||||
skip_count++; // Skip the frame for this function.
|
||||
int n = 0;
|
||||
|
||||
// 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. To find a PC value associated with the current frame, we
|
||||
// need to go down a level in the call chain. So we remember the return
|
||||
// address of the last frame seen. This does not work for the first stack
|
||||
// frame, which belongs to `UnwindImp()` but we skip the frame for
|
||||
// `UnwindImp()` anyway.
|
||||
void *prev_return_address = nullptr;
|
||||
|
||||
void *return_address = nullptr;
|
||||
while (frame_pointer && n < max_depth) {
|
||||
// The absl::GetStackFrames routine si called when we are in some
|
||||
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).
|
||||
@@ -200,26 +210,33 @@ static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count,
|
||||
if (skip_count > 0) {
|
||||
skip_count--;
|
||||
} else {
|
||||
result[n] = prev_return_address;
|
||||
result[n] = return_address;
|
||||
if (IS_STACK_FRAMES) {
|
||||
sizes[n] = ComputeStackFrameSize(frame_pointer, next_frame_pointer);
|
||||
}
|
||||
n++;
|
||||
}
|
||||
prev_return_address = frame_pointer[-1];
|
||||
|
||||
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 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
|
||||
#include "absl/base/macros.h"
|
||||
#include "absl/base/port.h"
|
||||
@@ -158,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__)
|
||||
@@ -257,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.
|
||||
@@ -296,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 {
|
||||
@@ -323,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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,29 +11,19 @@
|
||||
// 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.
|
||||
|
||||
//
|
||||
// Wrappers around lsan_interface functions.
|
||||
// When lsan is not linked in, these functions are not available,
|
||||
// therefore Abseil code which depends on these functions is conditioned on the
|
||||
// definition of LEAK_SANITIZER.
|
||||
#include "absl/base/attributes.h"
|
||||
//
|
||||
// These are always-available run-time functions manipulating the LeakSanitizer,
|
||||
// even when the lsan_interface (and LeakSanitizer) is not available. When
|
||||
// LeakSanitizer is not linked in, these functions become no-op stubs.
|
||||
|
||||
#include "absl/debugging/leak_check.h"
|
||||
|
||||
#ifndef LEAK_SANITIZER
|
||||
#include "absl/base/attributes.h"
|
||||
#include "absl/base/config.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
bool HaveLeakSanitizer() { return false; }
|
||||
bool LeakCheckerIsActive() { return false; }
|
||||
void DoIgnoreLeak(const void*) { }
|
||||
void RegisterLivePointers(const void*, size_t) { }
|
||||
void UnRegisterLivePointers(const void*, size_t) { }
|
||||
LeakCheckDisabler::LeakCheckDisabler() { }
|
||||
LeakCheckDisabler::~LeakCheckDisabler() { }
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#else
|
||||
#if defined(ABSL_HAVE_LEAK_SANITIZER)
|
||||
|
||||
#include <sanitizer/lsan_interface.h>
|
||||
|
||||
@@ -66,4 +56,18 @@ LeakCheckDisabler::~LeakCheckDisabler() { __lsan_enable(); }
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // LEAK_SANITIZER
|
||||
#else // defined(ABSL_HAVE_LEAK_SANITIZER)
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
bool HaveLeakSanitizer() { return false; }
|
||||
bool LeakCheckerIsActive() { return false; }
|
||||
void DoIgnoreLeak(const void*) { }
|
||||
void RegisterLivePointers(const void*, size_t) { }
|
||||
void UnRegisterLivePointers(const void*, size_t) { }
|
||||
LeakCheckDisabler::LeakCheckDisabler() { }
|
||||
LeakCheckDisabler::~LeakCheckDisabler() { }
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // defined(ABSL_HAVE_LEAK_SANITIZER)
|
||||
|
||||
@@ -24,7 +24,24 @@
|
||||
// Note: this leak checking API is not yet supported in MSVC.
|
||||
// Leak checking is enabled by default in all ASan builds.
|
||||
//
|
||||
// See https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer
|
||||
// https://clang.llvm.org/docs/LeakSanitizer.html
|
||||
// https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer
|
||||
//
|
||||
// GCC and Clang both automatically enable LeakSanitizer when AddressSanitizer
|
||||
// is enabled. To use the mode, simply pass `-fsanitize=address` to both the
|
||||
// compiler and linker. An example Bazel command could be
|
||||
//
|
||||
// $ bazel test --copt=-fsanitize=address --linkopt=-fsanitize=address ...
|
||||
//
|
||||
// GCC and Clang auto support a standalone LeakSanitizer mode (a mode which does
|
||||
// not also use AddressSanitizer). To use the mode, simply pass
|
||||
// `-fsanitize=leak` to both the compiler and linker. Since GCC does not
|
||||
// currently provide a way of detecting this mode at compile-time, GCC users
|
||||
// must also pass -DLEAK_SANIITIZER to the compiler. An example Bazel command
|
||||
// could be
|
||||
//
|
||||
// $ bazel test --copt=-DLEAK_SANITIZER --copt=-fsanitize=leak
|
||||
// --linkopt=-fsanitize=leak ...
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
#ifndef ABSL_DEBUGGING_LEAK_CHECK_H_
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
// 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.
|
||||
|
||||
// Disable LeakSanitizer when this file is linked in.
|
||||
// This function overrides __lsan_is_turned_off from sanitizer/lsan_interface.h
|
||||
extern "C" int __lsan_is_turned_off();
|
||||
extern "C" int __lsan_is_turned_off() {
|
||||
return 1;
|
||||
}
|
||||
@@ -15,27 +15,24 @@
|
||||
#include <string>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/base/internal/raw_logging.h"
|
||||
#include "absl/debugging/leak_check.h"
|
||||
|
||||
namespace {
|
||||
|
||||
TEST(LeakCheckTest, DetectLeakSanitizer) {
|
||||
#ifdef ABSL_EXPECT_LEAK_SANITIZER
|
||||
EXPECT_TRUE(absl::HaveLeakSanitizer());
|
||||
EXPECT_TRUE(absl::LeakCheckerIsActive());
|
||||
#else
|
||||
EXPECT_FALSE(absl::HaveLeakSanitizer());
|
||||
EXPECT_FALSE(absl::LeakCheckerIsActive());
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(LeakCheckTest, IgnoreLeakSuppressesLeakedMemoryErrors) {
|
||||
if (!absl::LeakCheckerIsActive()) {
|
||||
GTEST_SKIP() << "LeakChecker is not active";
|
||||
}
|
||||
auto foo = absl::IgnoreLeak(new std::string("some ignored leaked string"));
|
||||
ABSL_RAW_LOG(INFO, "Ignoring leaked string %s", foo->c_str());
|
||||
}
|
||||
|
||||
TEST(LeakCheckTest, LeakCheckDisablerIgnoresLeak) {
|
||||
if (!absl::LeakCheckerIsActive()) {
|
||||
GTEST_SKIP() << "LeakChecker is not active";
|
||||
}
|
||||
absl::LeakCheckDisabler disabler;
|
||||
auto foo = new std::string("some string leaked while checks are disabled");
|
||||
ABSL_RAW_LOG(INFO, "Ignoring leaked string %s", foo->c_str());
|
||||
|
||||
55
third_party/abseil-cpp/absl/debugging/stacktrace_benchmark.cc
vendored
Normal file
55
third_party/abseil-cpp/absl/debugging/stacktrace_benchmark.cc
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright 2022 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.
|
||||
|
||||
#include "absl/base/attributes.h"
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/base/optimization.h"
|
||||
#include "absl/debugging/stacktrace.h"
|
||||
#include "benchmark/benchmark.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace {
|
||||
|
||||
static constexpr int kMaxStackDepth = 100;
|
||||
static constexpr int kCacheSize = (1 << 16);
|
||||
void* pcs[kMaxStackDepth];
|
||||
|
||||
ABSL_ATTRIBUTE_NOINLINE void func(benchmark::State& state, int x, int depth) {
|
||||
if (x <= 0) {
|
||||
// Touch a significant amount of memory so that the stack is likely to be
|
||||
// not cached in the L1 cache.
|
||||
state.PauseTiming();
|
||||
int* arr = new int[kCacheSize];
|
||||
for (int i = 0; i < kCacheSize; ++i) benchmark::DoNotOptimize(arr[i] = 100);
|
||||
delete[] arr;
|
||||
state.ResumeTiming();
|
||||
benchmark::DoNotOptimize(absl::GetStackTrace(pcs, depth, 0));
|
||||
return;
|
||||
}
|
||||
ABSL_BLOCK_TAIL_CALL_OPTIMIZATION();
|
||||
func(state, --x, depth);
|
||||
}
|
||||
|
||||
void BM_GetStackTrace(benchmark::State& state) {
|
||||
int depth = state.range(0);
|
||||
for (auto s : state) {
|
||||
func(state, depth, depth);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK(BM_GetStackTrace)->DenseRange(10, kMaxStackDepth, 10);
|
||||
} // namespace
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
@@ -23,6 +23,11 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Emscripten symbolization relies on JS. Do not use them in standalone mode.
|
||||
#if defined(__EMSCRIPTEN__) && !defined(STANDALONE_WASM)
|
||||
#define ABSL_INTERNAL_HAVE_SYMBOLIZE_WASM
|
||||
#endif
|
||||
|
||||
#if defined(ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE)
|
||||
#include "absl/debugging/symbolize_elf.inc"
|
||||
#elif defined(ABSL_INTERNAL_HAVE_SYMBOLIZE_WIN32)
|
||||
@@ -31,7 +36,7 @@
|
||||
#include "absl/debugging/symbolize_win32.inc"
|
||||
#elif defined(__APPLE__)
|
||||
#include "absl/debugging/symbolize_darwin.inc"
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
#elif defined(ABSL_INTERNAL_HAVE_SYMBOLIZE_WASM)
|
||||
#include "absl/debugging/symbolize_emscripten.inc"
|
||||
#else
|
||||
#include "absl/debugging/symbolize_unimplemented.inc"
|
||||
|
||||
@@ -77,6 +77,10 @@
|
||||
#include "absl/debugging/internal/vdso_support.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
#if defined(__FreeBSD__) && !defined(ElfW)
|
||||
#define ElfW(x) __ElfN(x)
|
||||
#endif
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
|
||||
@@ -319,6 +323,7 @@ class Symbolizer {
|
||||
const ptrdiff_t relocation,
|
||||
char *out, int out_size,
|
||||
char *tmp_buf, int tmp_buf_size);
|
||||
const char *GetUncachedSymbol(const void *pc);
|
||||
|
||||
enum {
|
||||
SYMBOL_BUF_SIZE = 3072,
|
||||
@@ -1141,6 +1146,14 @@ bool Symbolizer::RegisterObjFile(const char *filename,
|
||||
reinterpret_cast<uintptr_t>(old->end_addr), old->filename);
|
||||
}
|
||||
return true;
|
||||
} else if (old->end_addr == start_addr &&
|
||||
reinterpret_cast<uintptr_t>(old->start_addr) - old->offset ==
|
||||
reinterpret_cast<uintptr_t>(start_addr) - offset &&
|
||||
strcmp(old->filename, filename) == 0) {
|
||||
// Two contiguous map entries that span a contiguous region of the file,
|
||||
// perhaps because some part of the file was mlock()ed. Combine them.
|
||||
old->end_addr = end_addr;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
ObjFile *obj = impl->addr_map_.Add();
|
||||
@@ -1329,13 +1342,7 @@ static bool MaybeInitializeObjFile(ObjFile *obj) {
|
||||
// they are called here as well.
|
||||
// To keep stack consumption low, we would like this function to not
|
||||
// get inlined.
|
||||
const char *Symbolizer::GetSymbol(const void *const pc) {
|
||||
const char *entry = FindSymbolInCache(pc);
|
||||
if (entry != nullptr) {
|
||||
return entry;
|
||||
}
|
||||
symbol_buf_[0] = '\0';
|
||||
|
||||
const char *Symbolizer::GetUncachedSymbol(const void *pc) {
|
||||
ObjFile *const obj = FindObjFile(pc, 1);
|
||||
ptrdiff_t relocation = 0;
|
||||
int fd = -1;
|
||||
@@ -1423,6 +1430,42 @@ const char *Symbolizer::GetSymbol(const void *const pc) {
|
||||
return InsertSymbolInCache(pc, symbol_buf_);
|
||||
}
|
||||
|
||||
const char *Symbolizer::GetSymbol(const void *pc) {
|
||||
const char *entry = FindSymbolInCache(pc);
|
||||
if (entry != nullptr) {
|
||||
return entry;
|
||||
}
|
||||
symbol_buf_[0] = '\0';
|
||||
|
||||
#ifdef __hppa__
|
||||
{
|
||||
// In some contexts (e.g., return addresses), PA-RISC uses the lowest two
|
||||
// bits of the address to indicate the privilege level. Clear those bits
|
||||
// before trying to symbolize.
|
||||
const auto pc_bits = reinterpret_cast<uintptr_t>(pc);
|
||||
const auto address = pc_bits & ~0x3;
|
||||
entry = GetUncachedSymbol(reinterpret_cast<const void *>(address));
|
||||
if (entry != nullptr) {
|
||||
return entry;
|
||||
}
|
||||
|
||||
// In some contexts, PA-RISC also uses bit 1 of the address to indicate that
|
||||
// this is a cross-DSO function pointer. Such function pointers actually
|
||||
// point to a procedure label, a struct whose first 32-bit (pointer) element
|
||||
// actually points to the function text. With no symbol found for this
|
||||
// address so far, try interpreting it as a cross-DSO function pointer and
|
||||
// see how that goes.
|
||||
if (pc_bits & 0x2) {
|
||||
return GetUncachedSymbol(*reinterpret_cast<const void *const *>(address));
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
#else
|
||||
return GetUncachedSymbol(pc);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool RemoveAllSymbolDecorators(void) {
|
||||
if (!g_decorators_mu.TryLock()) {
|
||||
// Someone else is using decorators. Get out.
|
||||
|
||||
@@ -392,12 +392,14 @@ TEST(Symbolize, InstallAndRemoveSymbolDecorators) {
|
||||
DummySymbolDecorator, &c_message),
|
||||
0);
|
||||
|
||||
char *address = reinterpret_cast<char *>(1);
|
||||
EXPECT_STREQ("abc", TrySymbolize(address++));
|
||||
// Use addresses 4 and 8 here to ensure that we always use valid addresses
|
||||
// even on systems that require instructions to be 32-bit aligned.
|
||||
char *address = reinterpret_cast<char *>(4);
|
||||
EXPECT_STREQ("abc", TrySymbolize(address));
|
||||
|
||||
EXPECT_TRUE(absl::debugging_internal::RemoveSymbolDecorator(ticket_b));
|
||||
|
||||
EXPECT_STREQ("ac", TrySymbolize(address++));
|
||||
EXPECT_STREQ("ac", TrySymbolize(address + 4));
|
||||
|
||||
// Cleanup: remove all remaining decorators so other stack traces don't
|
||||
// get mystery "ac" decoration.
|
||||
@@ -481,7 +483,8 @@ void ABSL_ATTRIBUTE_NOINLINE TestWithPCInsideInlineFunction() {
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__arm__) && ABSL_HAVE_ATTRIBUTE(target)
|
||||
#if defined(__arm__) && ABSL_HAVE_ATTRIBUTE(target) && \
|
||||
((__ARM_ARCH >= 7) || !defined(__ARM_PCS_VFP))
|
||||
// Test that we correctly identify bounds of Thumb functions on ARM.
|
||||
//
|
||||
// Thumb functions have the lowest-order bit set in their addresses in the ELF
|
||||
@@ -500,6 +503,10 @@ void ABSL_ATTRIBUTE_NOINLINE TestWithPCInsideInlineFunction() {
|
||||
// bit in the Thumb function's entry point. It will correctly compute the end of
|
||||
// the Thumb function, it will find no overlap between the Thumb and ARM
|
||||
// functions, and it will return the name of the ARM function.
|
||||
//
|
||||
// Unfortunately we cannot perform this test on armv6 or lower systems that use
|
||||
// the hard float ABI because gcc refuses to compile thumb functions on such
|
||||
// systems with a "sorry, unimplemented: Thumb-1 hard-float VFP ABI" error.
|
||||
|
||||
__attribute__((target("thumb"))) int ArmThumbOverlapThumb(int x) {
|
||||
return x * x * x;
|
||||
@@ -519,7 +526,8 @@ void ABSL_ATTRIBUTE_NOINLINE TestArmThumbOverlap() {
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // defined(__arm__) && ABSL_HAVE_ATTRIBUTE(target)
|
||||
#endif // defined(__arm__) && ABSL_HAVE_ATTRIBUTE(target) && ((__ARM_ARCH >= 7)
|
||||
// || !defined(__ARM_PCS_VFP))
|
||||
|
||||
#elif defined(_WIN32)
|
||||
#if !defined(ABSL_CONSUME_DLL)
|
||||
@@ -594,7 +602,8 @@ int main(int argc, char **argv) {
|
||||
TestWithPCInsideInlineFunction();
|
||||
TestWithPCInsideNonInlineFunction();
|
||||
TestWithReturnAddress();
|
||||
#if defined(__arm__) && ABSL_HAVE_ATTRIBUTE(target)
|
||||
#if defined(__arm__) && ABSL_HAVE_ATTRIBUTE(target) && \
|
||||
((__ARM_ARCH >= 7) || !defined(__ARM_PCS_VFP))
|
||||
TestArmThumbOverlap();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user