aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Mike Klein <mtklein@chromium.org>2017-03-23 18:36:39 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-03-24 15:55:50 +0000
commit61e990297a5c47a82321994e8a34ea56e3137f24 (patch)
tree09b907929bbc6c2879e682dd7ec790d89a51fcb8
parente5f5bf5175e426ebb6aa234f4387831c898f20ad (diff)
ok: basic crash handling and stack trace dumps
This seems to work pretty nicely for each engine in the expected case of few crashes. The serial and thread engines just dump the first crash stack to stderr before dying, while the fork engine saves all crashes to a temporary file, then prints that to stderr once everything's finished. I'm not sold on this TLS solution as being the best way to know what was running when we crashed, but it's better than printing nothing. Change-Id: I0aca66529301b1ad9bd51ec728848817586c606d Reviewed-on: https://skia-review.googlesource.com/10102 Reviewed-by: Herb Derby <herb@google.com> Commit-Queue: Mike Klein <mtklein@chromium.org>
-rw-r--r--tools/ok.cpp69
1 files changed, 65 insertions, 4 deletions
diff --git a/tools/ok.cpp b/tools/ok.cpp
index d0d3960afe..06c417a2de 100644
--- a/tools/ok.cpp
+++ b/tools/ok.cpp
@@ -26,6 +26,65 @@
#include <stdlib.h>
#include <thread>
+#if !defined(__has_include)
+ #define __has_include(x) 0
+#endif
+
+static thread_local const char* tls_name = "";
+
+#if __has_include(<execinfo.h>) && __has_include(<fcntl.h>) && __has_include(<signal.h>)
+ #include <execinfo.h>
+ #include <fcntl.h>
+ #include <signal.h>
+
+ static int crash_stacktrace_fd = 2/*stderr*/;
+
+ static void setup_crash_handler() {
+ static void (*original_handlers[32])(int);
+
+ for (int sig : std::vector<int>{ SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV }) {
+ original_handlers[sig] = signal(sig, [](int sig) {
+ auto ez_write = [](const char* str) {
+ write(crash_stacktrace_fd, str, strlen(str));
+ };
+ ez_write("\ncaught signal ");
+ switch (sig) {
+ #define CASE(s) case s: ez_write(#s); break
+ CASE(SIGABRT);
+ CASE(SIGBUS);
+ CASE(SIGFPE);
+ CASE(SIGILL);
+ CASE(SIGSEGV);
+ #undef CASE
+ }
+ ez_write(" while running '");
+ ez_write(tls_name);
+ ez_write("'\n");
+
+ void* stack[128];
+ int frames = backtrace(stack, sizeof(stack)/sizeof(*stack));
+ backtrace_symbols_fd(stack, frames, crash_stacktrace_fd);
+ signal(sig, original_handlers[sig]);
+ raise(sig);
+ });
+ }
+ }
+
+ static void defer_crash_stacktraces() {
+ crash_stacktrace_fd = fileno(tmpfile());
+ atexit([] {
+ lseek(crash_stacktrace_fd, 0, SEEK_SET);
+ char buf[1024];
+ while (size_t bytes = read(crash_stacktrace_fd, buf, sizeof(buf))) {
+ write(2, buf, bytes);
+ }
+ });
+ }
+#else
+ static void setup_crash_handler() {}
+ static void defer_crash_stacktraces() {}
+#endif
+
enum class Status { OK, Failed, Crashed, Skipped, None };
struct Engine {
@@ -83,7 +142,7 @@ struct ThreadEngine : Engine {
struct ForkEngine : Engine {
bool spawn(std::function<Status(void)> fn) override {
switch (fork()) {
- case 0: exit((int)fn());
+ case 0: _exit((int)fn());
case -1: return false;
default: return true;
}
@@ -227,6 +286,7 @@ struct {
int main(int argc, char** argv) {
SkGraphics::Init();
+ setup_crash_handler();
int jobs {1};
std::regex match {".*"};
@@ -279,9 +339,9 @@ int main(int argc, char** argv) {
if (!stream) { return help(); }
std::unique_ptr<Engine> engine;
- if (jobs == 0) { engine.reset(new SerialEngine); }
- if (jobs > 0) { engine.reset(new ForkEngine); }
- if (jobs < 0) { engine.reset(new ThreadEngine); jobs = -jobs; }
+ if (jobs == 0) { engine.reset(new SerialEngine); }
+ if (jobs > 0) { engine.reset(new ForkEngine); defer_crash_stacktraces(); }
+ if (jobs < 0) { engine.reset(new ThreadEngine); jobs = -jobs; }
if (jobs == 1) { jobs = std::thread::hardware_concurrency(); }
@@ -328,6 +388,7 @@ int main(int argc, char** argv) {
std::unique_ptr<Src> src{raw};
auto name = src->name();
+ tls_name = name.c_str();
if (!std::regex_match (name, match) ||
!std::regex_search(name, search)) {
return Status::Skipped;