aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Mike Klein <mtklein@chromium.org>2017-08-30 10:23:01 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-08-30 17:50:07 +0000
commit1b4602bd9b29feaecc6255db5ddc384835d7ee0a (patch)
treeb1bbe6fb104924a20294593e934b67034d15b9d5
parentf3b46e5193da843cac07d42fdc36c76c05f7fa77 (diff)
ok, backtrace support on Android
This adds a fallback backtracer for use on Android where <execinfo.h> ins't present, instead using <unwind.h> to unwind and <dlfcn.h> to lookup function names and addresses. lockf() wasn't available until NDK API 24, so I've just no-op'd file locking on older targets. I tried switching from lockf() to flock(), but flock() didn't see to _do_ anything, neither on Android nor on my Mac laptop. I think I should be able to use the lower-level fcntl() APIs to restore file locking uniformly in a follow-up. The upshot is until then, we'll have interlaced logs and stack traces on Android devices unless you set ndk_api=24 in GN. We need to add a couple build flags to make backtraces useful: * -funwind-tables makes the call to _Unwind_Backtrace() actually traverse the call stack. This is a small extra binary size cost. * -rdynamic makes symbols linked into the main executable visible to dladdr(). We do this on Linux already for the same reason. Here's an example where I made aaxfermodes call SK_ABORT(): 650 ok, 1 crashed caught signal SIGABRT while running 'aaxfermodes' 0x76ed936288 [unknown]+308 0x76eec014e0 [unknown]+510811706592 0x76ed367b2c tgkill+8 0x76ed364f50 pthread_kill+68 0x76ed31ff5c raise+28 0x76ed318814 abort+56 0x76edebd070 sk_out_of_memory()+12 0x76ed99f664 AAXfermodesGM::draw_pass(SkCanvas*, AAXfermodesGM::DrawingPass)+96 0x76ed99f4e4 AAXfermodesGM::onDraw(SkCanvas*)+36 0x76ed9e8550 skiagm::GM::drawContent(SkCanvas*)+224 0x76ed9e82ac skiagm::GM::draw(SkCanvas*)+288 0x76ed93b10c GMStream::GMSrc::draw(SkCanvas*)+96 0x76ed937b08 SWDst::draw(Src*)+284 0x76ed936ca0 [unknown]+112 0x76ed939b4c ForkEngine::spawn(std::function<Status ()>)+88 0x76ed934d00 main+2200 0x76ed316598 __libc_init+92 0x76ed93434c [unknown]+510791992140 Change-Id: Ica4849d99a3b97f48d778f4c15a7fa36275b8133 Reviewed-on: https://skia-review.googlesource.com/40802 Reviewed-by: Herb Derby <herb@google.com> Commit-Queue: Mike Klein <mtklein@chromium.org>
-rw-r--r--gn/BUILD.gn10
-rw-r--r--tools/ok.cpp67
2 files changed, 66 insertions, 11 deletions
diff --git a/gn/BUILD.gn b/gn/BUILD.gn
index 7be1617d23..b2013c571b 100644
--- a/gn/BUILD.gn
+++ b/gn/BUILD.gn
@@ -387,7 +387,10 @@ config("debug_symbols") {
# It's annoying to wait for full debug symbols to push over
# to Android devices. -gline-tables-only is a lot slimmer.
if (is_android) {
- cflags = [ "-gline-tables-only" ]
+ cflags = [
+ "-gline-tables-only",
+ "-funwind-tables", # Helps make in-process backtraces fuller.
+ ]
} else if (is_win) {
cflags = [ "/Zi" ]
ldflags = [ "/DEBUG" ]
@@ -434,7 +437,10 @@ config("release") {
config("executable") {
if (is_android) {
- ldflags = [ "-pie" ]
+ ldflags = [
+ "-pie",
+ "-rdynamic",
+ ]
} else if (is_mac) {
ldflags = [ "-Wl,-rpath,@loader_path/." ]
} else if (is_linux) {
diff --git a/tools/ok.cpp b/tools/ok.cpp
index ca99f33a16..eb33439248 100644
--- a/tools/ok.cpp
+++ b/tools/ok.cpp
@@ -24,11 +24,63 @@
static thread_local const char* tls_currently_running = "";
-#if __has_include(<execinfo.h>) && __has_include(<fcntl.h>) && __has_include(<signal.h>)
+#if __has_include(<execinfo.h>)
#include <execinfo.h>
+
+ #define CAN_BACKTRACE
+ static void backtrace(int fd) {
+ void* stack[128];
+ int frames = backtrace(stack, sizeof(stack)/sizeof(*stack));
+ backtrace_symbols_fd(stack, frames, fd);
+ }
+
+#elif __has_include(<dlfcn.h>) && __has_include(<unwind.h>)
+ #include <cxxabi.h>
+ #include <dlfcn.h>
+ #include <unwind.h>
+
+ #define CAN_BACKTRACE
+ static void backtrace(int fd) {
+ FILE* file = fdopen(fd, "a");
+ _Unwind_Backtrace([](_Unwind_Context* ctx, void* arg) {
+ auto file = (FILE*)arg;
+ if (auto ip = (void*)_Unwind_GetIP(ctx)) {
+ const char* name = "[unknown]";
+ void* addr = nullptr;
+ Dl_info info;
+ if (dladdr(ip, &info) && info.dli_sname && info.dli_saddr) {
+ name = info.dli_sname;
+ addr = info.dli_saddr;
+ }
+
+ int ok;
+ char* demangled = abi::__cxa_demangle(name, nullptr,0, &ok);
+ if (ok == 0 && demangled) {
+ name = demangled;
+ }
+
+ fprintf(file, "\t%p %s+%zu\n", ip, name, (size_t)ip - (size_t)addr);
+ free(demangled);
+ }
+ return _URC_NO_REASON;
+ }, file);
+ fflush(file);
+ }
+#endif
+
+#if defined(CAN_BACKTRACE) && __has_include(<fcntl.h>) && __has_include(<signal.h>)
#include <fcntl.h>
#include <signal.h>
+ #if defined(__ANDROID_API__) && __ANDROID_API__ < 24
+ // TODO: do all locking manually with fcntl() so we can lock on older Android NDK APIs?
+ static void lock_fd(int) {}
+ static void unlock_fd(int) {}
+ #else
+ static void lock_fd(int fd) { lockf(fd, F_LOCK, 0); }
+ static void unlock_fd(int fd) { lockf(fd, F_ULOCK, 0); }
+ #endif
+
static int log_fd = 2/*stderr*/;
static void log(const char* msg) {
@@ -39,7 +91,7 @@ static thread_local const char* tls_currently_running = "";
static void (*original_handlers[32])(int);
for (int sig : std::vector<int>{ SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV }) {
original_handlers[sig] = signal(sig, [](int sig) {
- lockf(log_fd, F_LOCK, 0);
+ lock_fd(log_fd);
log("\ncaught signal ");
switch (sig) {
#define CASE(s) case s: log(#s); break
@@ -53,11 +105,8 @@ static thread_local const char* tls_currently_running = "";
log(" while running '");
log(tls_currently_running);
log("'\n");
-
- void* stack[128];
- int frames = backtrace(stack, sizeof(stack)/sizeof(*stack));
- backtrace_symbols_fd(stack, frames, log_fd);
- lockf(log_fd, F_ULOCK, 0);
+ backtrace(log_fd);
+ unlock_fd(log_fd);
signal(sig, original_handlers[sig]);
raise(sig);
@@ -77,13 +126,13 @@ static thread_local const char* tls_currently_running = "";
}
void ok_log(const char* msg) {
- lockf(log_fd, F_LOCK, 0);
+ lock_fd(log_fd);
log("[");
log(tls_currently_running);
log("]\t");
log(msg);
log("\n");
- lockf(log_fd, F_ULOCK, 0);
+ unlock_fd(log_fd);
}
#else