Add basic stacktrace handler using libunwind.

This means we will all have to apt-get install libunwind8-dev on Linux.  Mac comes with everything we need already.

BUG=skia:
R=reed@google.com, mtklein@google.com

Author: mtklein@chromium.org

Review URL: https://codereview.chromium.org/343583005
diff --git a/bench/benchmain.cpp b/bench/benchmain.cpp
index 6e18a8a..078a2e8 100644
--- a/bench/benchmain.cpp
+++ b/bench/benchmain.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "BenchTimer.h"
+#include "CrashHandler.h"
 #include "ResultsWriter.h"
 #include "SkBenchLogger.h"
 #include "SkBenchmark.h"
@@ -271,6 +272,7 @@
 
 int tool_main(int argc, char** argv);
 int tool_main(int argc, char** argv) {
+    SetupCrashHandler();
     SkCommandLineFlags::Parse(argc, argv);
 #if SK_ENABLE_INST_COUNT
     if (FLAGS_leaks) {
diff --git a/dm/DM.cpp b/dm/DM.cpp
index 776b642..0de6616 100644
--- a/dm/DM.cpp
+++ b/dm/DM.cpp
@@ -9,6 +9,7 @@
 #include "SkString.h"
 #include "Test.h"
 #include "gm.h"
+#include "CrashHandler.h"
 
 #include "DMBenchTask.h"
 #include "DMCpuGMTask.h"
@@ -215,6 +216,7 @@
 
 int tool_main(int argc, char** argv);
 int tool_main(int argc, char** argv) {
+    SetupCrashHandler();
     SkAutoGraphics ag;
     SkCommandLineFlags::Parse(argc, argv);
 
diff --git a/gm/gmmain.cpp b/gm/gmmain.cpp
index 0be3454..42aed54 100644
--- a/gm/gmmain.cpp
+++ b/gm/gmmain.cpp
@@ -17,6 +17,7 @@
 #include "gm_error.h"
 #include "gm_expectations.h"
 #include "system_preferences.h"
+#include "CrashHandler.h"
 #include "SkBitmap.h"
 #include "SkColorPriv.h"
 #include "SkCommandLineFlags.h"
@@ -2219,6 +2220,7 @@
 
 int tool_main(int argc, char** argv);
 int tool_main(int argc, char** argv) {
+    SetupCrashHandler();
 
     SkString usage;
     usage.printf("Run the golden master tests.\n");
diff --git a/gyp/bench.gyp b/gyp/bench.gyp
index 8c81991..a5e0cd6 100644
--- a/gyp/bench.gyp
+++ b/gyp/bench.gyp
@@ -9,11 +9,12 @@
       'target_name': 'bench',
       'type': 'executable',
       'dependencies': [
-        'skia_lib.gyp:skia_lib',
         'bench_timer',
+        'crash_handler.gyp:CrashHandler',
+        'etc1.gyp:libetc1',
         'flags.gyp:flags',
         'jsoncpp.gyp:jsoncpp',
-        'etc1.gyp:libetc1',
+        'skia_lib.gyp:skia_lib',
       ],
       'sources': [
         '../bench/ResultsWriter.cpp',
diff --git a/gyp/crash_handler.gyp b/gyp/crash_handler.gyp
new file mode 100644
index 0000000..aa5ad94
--- /dev/null
+++ b/gyp/crash_handler.gyp
@@ -0,0 +1,11 @@
+{
+  'targets': [{
+      'target_name': 'CrashHandler',
+          'type': 'static_library',
+          'sources': [ '../tools/CrashHandler.cpp' ],
+          'dependencies': [ 'skia_lib.gyp:skia_lib' ],
+          'direct_dependent_settings': {
+              'include_dirs': [ '../tools' ],
+          },
+  }]
+}
diff --git a/gyp/dm.gyp b/gyp/dm.gyp
index a849e54..f69ffe5 100644
--- a/gyp/dm.gyp
+++ b/gyp/dm.gyp
@@ -55,11 +55,12 @@
             '../src/utils/debugger/SkObjectParser.cpp',
         ],
         'dependencies': [
-            'skia_lib.gyp:skia_lib',
-            'flags.gyp:flags',
-            'jsoncpp.gyp:jsoncpp',
-            'gputest.gyp:skgputest',
+            'crash_handler.gyp:CrashHandler',
             'etc1.gyp:libetc1',
+            'flags.gyp:flags',
+            'gputest.gyp:skgputest',
+            'jsoncpp.gyp:jsoncpp',
+            'skia_lib.gyp:skia_lib',
         ],
         'conditions': [
           ['skia_android_framework', {
diff --git a/gyp/gm.gyp b/gyp/gm.gyp
index 9b74e74..fd8f74d 100644
--- a/gyp/gm.gyp
+++ b/gyp/gm.gyp
@@ -16,8 +16,9 @@
         '../tools/sk_tool_utils.cpp',
       ],
       'dependencies': [
-        'skia_lib.gyp:skia_lib',
+        'crash_handler.gyp:CrashHandler',
         'jsoncpp.gyp:jsoncpp',
+        'skia_lib.gyp:skia_lib',
       ],
       'direct_dependent_settings': {
         'include_dirs': [
diff --git a/gyp/tests.gyp b/gyp/tests.gyp
index 9210bf6..80768ef 100644
--- a/gyp/tests.gyp
+++ b/gyp/tests.gyp
@@ -11,6 +11,7 @@
         'pathops_unittest.gypi',
         'tests.gypi',
       ],
+      'dependencies': [ 'crash_handler.gyp:CrashHandler' ],
       'sources': [
         '../tests/skia_test.cpp',
       ],
diff --git a/gyp/tools.gyp b/gyp/tools.gyp
index 9e1cf05..9cea461 100644
--- a/gyp/tools.gyp
+++ b/gyp/tools.gyp
@@ -300,11 +300,12 @@
       ],
       'dependencies': [
         'bench.gyp:bench_timer',
+        'crash_handler.gyp:CrashHandler',
         'flags.gyp:flags',
         'jsoncpp.gyp:jsoncpp',
         'skia_lib.gyp:skia_lib',
-        'tools.gyp:picture_utils',
         'tools.gyp:picture_renderer',
+        'tools.gyp:picture_utils',
         'tools.gyp:timer_data',
       ],
     },
diff --git a/tests/skia_test.cpp b/tests/skia_test.cpp
index b7fbfc6..f70a7fa 100644
--- a/tests/skia_test.cpp
+++ b/tests/skia_test.cpp
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 
+#include "CrashHandler.h"
 #include "OverwriteLine.h"
 #include "SkCommandLineFlags.h"
 #include "SkGraphics.h"
@@ -132,6 +133,7 @@
 
 int tool_main(int argc, char** argv);
 int tool_main(int argc, char** argv) {
+    SetupCrashHandler();
     SkCommandLineFlags::SetUsage("");
     SkCommandLineFlags::Parse(argc, argv);
     Test::SetResourcePath(FLAGS_resourcePath[0]);
diff --git a/tools/CrashHandler.cpp b/tools/CrashHandler.cpp
new file mode 100644
index 0000000..193e1af
--- /dev/null
+++ b/tools/CrashHandler.cpp
@@ -0,0 +1,88 @@
+#include "CrashHandler.h"
+
+#include "SkTypes.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+
+#if defined(SK_BUILD_FOR_MAC)
+
+// We only use local unwinding, so we can define this to select a faster implementation.
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+#include <cxxabi.h>
+
+static void handler(int sig) {
+    unw_context_t context;
+    unw_getcontext(&context);
+
+    unw_cursor_t cursor;
+    unw_init_local(&cursor, &context);
+
+    fprintf(stderr, "\nSignal %d:\n", sig);
+    while (unw_step(&cursor) > 0) {
+        static const size_t kMax = 256;
+        char mangled[kMax], demangled[kMax];
+        unw_word_t offset;
+        unw_get_proc_name(&cursor, mangled, kMax, &offset);
+
+        int ok;
+        size_t len = kMax;
+        abi::__cxa_demangle(mangled, demangled, &len, &ok);
+
+        fprintf(stderr, "%s (+0x%zx)\n", ok == 0 ? demangled : mangled, (size_t)offset);
+    }
+    fprintf(stderr, "\n");
+
+    // Exit NOW.  Don't notify other threads, don't call anything registered with atexit().
+    _Exit(sig);
+}
+
+#elif defined(SK_BUILD_FOR_UNIX)
+
+// We'd use libunwind here too, but it's a pain to get installed for both 32 and 64 bit on bots.
+// Doesn't matter much: catchsegv is best anyway.
+#include <execinfo.h>
+
+static void handler(int sig) {
+    static const int kMax = 64;
+    void* stack[kMax];
+    const int count = backtrace(stack, kMax);
+
+    fprintf(stderr, "\nSignal %d:\n", sig);
+    backtrace_symbols_fd(stack, count, 2/*stderr*/);
+
+    // Exit NOW.  Don't notify other threads, don't call anything registered with atexit().
+    _Exit(sig);
+}
+
+#endif
+
+#if defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_MAC)
+
+void SetupCrashHandler() {
+    static const int kSignals[] = {
+        SIGABRT,
+        SIGBUS,
+        SIGFPE,
+        SIGILL,
+        SIGSEGV,
+    };
+
+    for (size_t i = 0; i < sizeof(kSignals) / sizeof(kSignals[0]); i++) {
+        // Register our signal handler unless something's already done so (e.g. catchsegv).
+        void (*prev)(int) = signal(kSignals[i], handler);
+        if (prev != SIG_DFL) {
+            signal(kSignals[i], prev);
+        }
+    }
+}
+
+// TODO: #elif defined(SK_BUILD_FOR_WIN) when I find a Windows machine to work from.
+
+#else
+
+void SetupCrashHandler() { }
+
+#endif
diff --git a/tools/CrashHandler.h b/tools/CrashHandler.h
new file mode 100644
index 0000000..6c22c8e
--- /dev/null
+++ b/tools/CrashHandler.h
@@ -0,0 +1,9 @@
+#ifndef CrashHandler_DEFINED
+#define CrashHandler_DEFINED
+
+// If possible (and not already done) register a handler for a stack trace when we crash.
+// Currently this works on Linux and Mac, hopefully Windows soon.
+// On Linux, you will get much better results than we can deliver by running "catchsegv <program>".
+void SetupCrashHandler();
+
+#endif//CrashHandler_DEFINED
diff --git a/tools/bench_pictures_main.cpp b/tools/bench_pictures_main.cpp
index 6b76bfc..c63ffff 100644
--- a/tools/bench_pictures_main.cpp
+++ b/tools/bench_pictures_main.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "BenchTimer.h"
+#include "CrashHandler.h"
 #include "CopyTilesRenderer.h"
 #include "LazyDecodeBitmap.h"
 #include "PictureBenchmark.h"
@@ -391,6 +392,7 @@
 
 int tool_main(int argc, char** argv);
 int tool_main(int argc, char** argv) {
+    SetupCrashHandler();
     SkString usage;
     usage.printf("Time drawing .skp files.\n"
                  "\tPossible arguments for --filter: [%s]\n\t\t[%s]",