Improve stacktraces on Linux/Android standalone debug builds
Improves stacktraces on Linux/Android using libbacktrace.
The library is used only in standalone builds and not shipped
in any official (Chrome or Android) build.
libbacktrace has the advantage of using dwarf info for
symbolization and not requiring to export the dynamic symbol
table (which doesn't work because we have to build with
-fvisibility=hidden because of protobuf)
Bug: 76169489
Change-Id: I308050947c2c57fec630ea94712ee81a5a6543b3
diff --git a/.travis.yml b/.travis.yml
index 69f9cdb..9e94c5a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -106,6 +106,7 @@
- buildtools/benchmark
- buildtools/emulator
- buildtools/googletest
+ - buildtools/libbacktrace
- buildtools/libcxx
- buildtools/libcxxabi
- buildtools/libunwind
diff --git a/buildtools/.gitignore b/buildtools/.gitignore
index ca745de..6640fa0 100644
--- a/buildtools/.gitignore
+++ b/buildtools/.gitignore
@@ -5,6 +5,7 @@
clang_format/
emulator/
googletest/
+libbacktrace/
libcxx/
libcxxabi/
libunwind/
diff --git a/buildtools/BUILD.gn b/buildtools/BUILD.gn
index 5d4351b..836a8a3 100644
--- a/buildtools/BUILD.gn
+++ b/buildtools/BUILD.gn
@@ -538,3 +538,36 @@
all_dependent_configs = [ ":benchmark_config" ]
configs -= [ "//gn/standalone:extra_warnings" ]
}
+
+# On Linux/Android use libbacktrace in debug builds for better stacktraces.
+if (is_linux || is_android) {
+ config("libbacktrace_config") {
+ visibility = [ ":*" ]
+ include_dirs = [
+ "libbacktrace_config",
+ "libbacktrace",
+ ]
+ cflags = [
+ "-include",
+ rebase_path("libbacktrace_config/config.h", root_build_dir),
+ ]
+ }
+
+ source_set("libbacktrace") {
+ sources = [
+ "libbacktrace/backtrace.c",
+ "libbacktrace/dwarf.c",
+ "libbacktrace/elf.c",
+ "libbacktrace/fileline.c",
+ "libbacktrace/mmap.c",
+ "libbacktrace/mmapio.c",
+ "libbacktrace/posix.c",
+ "libbacktrace/print.c",
+ "libbacktrace/simple.c",
+ "libbacktrace/sort.c",
+ "libbacktrace/state.c",
+ ]
+ configs -= [ "//gn/standalone:extra_warnings" ]
+ public_configs = [ ":libbacktrace_config" ]
+ }
+}
diff --git a/buildtools/libbacktrace_config/backtrace-supported.h b/buildtools/libbacktrace_config/backtrace-supported.h
new file mode 100644
index 0000000..bf2d8ac
--- /dev/null
+++ b/buildtools/libbacktrace_config/backtrace-supported.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * 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
+ *
+ * http://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 BUILDTOOLS_LIBBACKTRACE_CONFIG_BACKTRACE_SUPPORTED_H_
+#define BUILDTOOLS_LIBBACKTRACE_CONFIG_BACKTRACE_SUPPORTED_H_
+
+#define BACKTRACE_SUPPORTED 1
+#define BACKTRACE_USES_MALLOC 0
+#define BACKTRACE_SUPPORTS_THREADS 1
+#define BACKTRACE_SUPPORTS_DATA 1
+
+#endif // BUILDTOOLS_LIBBACKTRACE_CONFIG_BACKTRACE_SUPPORTED_H_
diff --git a/buildtools/libbacktrace_config/config.h b/buildtools/libbacktrace_config/config.h
new file mode 100644
index 0000000..0ced426
--- /dev/null
+++ b/buildtools/libbacktrace_config/config.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * 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
+ *
+ * http://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 BUILDTOOLS_LIBBACKTRACE_CONFIG_CONFIG_H_
+#define BUILDTOOLS_LIBBACKTRACE_CONFIG_CONFIG_H_
+
+#define BACKTRACE_ELF_SIZE 64
+#define BACKTRACE_XCOFF_SIZE unused
+#define HAVE_ATOMIC_FUNCTIONS 1
+#define HAVE_CLOCK_GETTIME 1
+#define HAVE_DECL_STRNLEN 1
+#define HAVE_DLFCN_H 1
+#define HAVE_FCNTL 1
+#define HAVE_GETIPINFO 1
+#define HAVE_INTTYPES_H 1
+#define HAVE_LSTAT 1
+#define HAVE_MEMORY_H 1
+#define HAVE_READLINK 1
+#define HAVE_STDINT_H 1
+#define HAVE_STDLIB_H 1
+#define HAVE_STRINGS_H 1
+#define HAVE_STRING_H 1
+#define HAVE_SYNC_FUNCTIONS 1
+#define HAVE_SYS_MMAN_H 1
+#define HAVE_SYS_STAT_H 1
+#define HAVE_SYS_TYPES_H 1
+#define HAVE_UNISTD_H 1
+#define HAVE_LINK_H 1
+#define STDC_HEADERS 1
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+
+#endif // BUILDTOOLS_LIBBACKTRACE_CONFIG_CONFIG_H_
diff --git a/gn/standalone/BUILD.gn b/gn/standalone/BUILD.gn
index ce1088c..7c8d470 100644
--- a/gn/standalone/BUILD.gn
+++ b/gn/standalone/BUILD.gn
@@ -159,7 +159,6 @@
cflags = [ "-O0" ]
if (is_android || is_linux) {
cflags += [ "-funwind-tables" ]
- ldflags = [ "-rdynamic" ]
}
}
diff --git a/src/base/BUILD.gn b/src/base/BUILD.gn
index 9d01949..2e14cf0 100644
--- a/src/base/BUILD.gn
+++ b/src/base/BUILD.gn
@@ -13,6 +13,7 @@
# limitations under the License.
import("//build_overrides/build.gni")
+import("../../gn/perfetto.gni")
source_set("base") {
deps = [
@@ -34,7 +35,7 @@
} else {
sources += [ "watchdog_noop.cc" ]
}
- if (is_debug) {
+ if (is_debug && !build_with_chromium && !build_with_android) {
deps += [ ":debug_crash_stack_trace" ]
}
}
@@ -59,7 +60,7 @@
}
}
-if (is_debug) {
+if (is_debug && !build_with_chromium && !build_with_android) {
source_set("debug_crash_stack_trace") {
sources = [
"debug_crash_stack_trace.cc",
@@ -71,6 +72,9 @@
deps = [
"../../gn:default_deps",
]
+ if (is_linux || is_android) {
+ deps += [ "../../buildtools:libbacktrace" ]
+ }
}
}
diff --git a/src/base/debug_crash_stack_trace.cc b/src/base/debug_crash_stack_trace.cc
index 3b57a34..d32f941 100644
--- a/src/base/debug_crash_stack_trace.cc
+++ b/src/base/debug_crash_stack_trace.cc
@@ -32,6 +32,16 @@
#error This translation unit should not be used in release builds
#endif
+#if PERFETTO_BUILDFLAG(PERFETTO_CHROMIUM_BUILD) || \
+ PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
+#error This translation unit should not be used in non-standalone builds
+#endif
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+#include <backtrace.h>
+#endif
+
namespace {
constexpr size_t kDemangledNameLen = 4096;
@@ -125,32 +135,64 @@
StackCrawlState unwind_state(frames, kMaxFrames);
_Unwind_Backtrace(&TraceStackFrame, &unwind_state);
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+ auto bt_error = [](void* data, const char* msg, int errnum) { Print(msg); };
+ struct backtrace_state* bt_state =
+ backtrace_create_state(nullptr, 0, bt_error, nullptr);
+#endif
+
for (uint8_t i = 0; i < unwind_state.frame_count; i++) {
- Dl_info sym_info = {};
- int res = dladdr(reinterpret_cast<void*>(frames[i]), &sym_info);
Print("\n#");
PrintHex(i);
Print(" ");
- const char* sym_name = res ? sym_info.dli_sname : nullptr;
- if (sym_name) {
+ struct SymbolInfo {
+ char sym_name[255];
+ char file_name[255];
+ };
+ SymbolInfo sym{{}, {}};
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+ auto symbolize_callback = [](void* data, uintptr_t pc, const char* filename,
+ int lineno, const char* function) -> int {
+ SymbolInfo* psym = reinterpret_cast<SymbolInfo*>(data);
+ if (function)
+ strncpy(psym->sym_name, function, sizeof(psym->sym_name));
+ if (filename) {
+ snprintf(psym->file_name, sizeof(psym->file_name), "%s:%d", filename,
+ lineno);
+ }
+ return 0;
+ };
+ backtrace_pcinfo(bt_state, frames[i], symbolize_callback, bt_error, &sym);
+#else
+ Dl_info dl_info = {};
+ int res = dladdr(reinterpret_cast<void*>(frames[i]), &dl_info);
+ if (res && dl_info.dli_sname)
+ strncpy(sym.sym_name, dl_info.dli_sname, sizeof(sym.sym_name));
+#endif
+
+ if (sym.sym_name[0]) {
int ignored;
size_t len = kDemangledNameLen;
- char* demangled = abi::__cxa_demangle(sym_info.dli_sname,
- g_demangled_name, &len, &ignored);
+ char* demangled =
+ abi::__cxa_demangle(sym.sym_name, g_demangled_name, &len, &ignored);
if (demangled) {
- sym_name = demangled;
+ strncpy(sym.sym_name, demangled, sizeof(sym.sym_name));
// In the exceptional case of demangling something > kDemangledNameLen,
// __cxa_demangle will realloc(). In that case the malloc()-ed pointer
// might be moved.
g_demangled_name = demangled;
}
- write(STDERR_FILENO, sym_name, strlen(sym_name));
+ write(STDERR_FILENO, sym.sym_name, strlen(sym.sym_name));
} else {
- if (res && sym_info.dli_fname) {
- write(STDERR_FILENO, sym_info.dli_fname, strlen(sym_info.dli_fname));
- Print(" ");
- }
- PrintHex(frames[i] - reinterpret_cast<uintptr_t>(sym_info.dli_fbase));
+ Print("0x");
+ PrintHex(frames[i]);
+ }
+ if (sym.file_name[0]) {
+ Print("\n ");
+ write(STDERR_FILENO, sym.file_name, strlen(sym.file_name));
}
Print("\n");
}
@@ -159,10 +201,10 @@
// info->si_code <= 0 iff SI_FROMUSER (SI_FROMKERNEL otherwise).
if (info->si_code <= 0 || sig_num == SIGABRT) {
- // This signal was triggered by somebody sending us the signal with kill().
- // In order to retrigger it, we have to queue a new signal by calling
- // kill() ourselves. The special case (si_pid == 0 && sig == SIGABRT) is
- // due to the kernel sending a SIGABRT from a user request via SysRQ.
+// This signal was triggered by somebody sending us the signal with kill().
+// In order to retrigger it, we have to queue a new signal by calling
+// kill() ourselves. The special case (si_pid == 0 && sig == SIGABRT) is
+// due to the kernel sending a SIGABRT from a user request via SysRQ.
#if PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX)
if (kill(getpid(), sig_num) < 0) {
#else
diff --git a/tools/install-build-deps b/tools/install-build-deps
index 957f5b0..b7c1fc8 100755
--- a/tools/install-build-deps
+++ b/tools/install-build-deps
@@ -130,6 +130,13 @@
'f387e0df37d54bfd5be239e8d0d3ea2e2c3e34f4',
'all'
),
+
+ # Libbacktrace, for stacktraces in Linux/Android debug builds.
+ ('buildtools/libbacktrace.zip',
+ 'https://github.com/ianlancetaylor/libbacktrace/archive/177940370e4a6b2509e92a0aaa9749184e64af43.zip',
+ 'b723fe9d671d1ab54df1297f6afbf2893a41c3ea',
+ 'all'
+ ),
]
# Dependencies required to build Android code.