dump stack trace in FM on failure

In CrashHandler...
  - make CrashHandler a non-noop... don't know why it's disabled
  - fix bitrot since we last built it
  - update to demangle symbols on Linux too, not just Mac
  - catch SIGTRAP, which will catch SK_ABORT / SkASSERT,
    unless otherwise hooked (e.g. by a debugger)

In fm...
  - use CrashHandler
  - convert exit_with_failure to SK_ABORT so they'll also dump a trace
  - flush stdout after printing what's running

Change-Id: Ib20d0e4f442d73c28e193396dc6e85935fc58544
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/208151
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Mike Klein <mtklein@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 9faf14d..61715a7 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1654,7 +1654,10 @@
     if (is_ios) {
       sources += [ "tools/ios_utils.m" ]
       libs += [ "Foundation.framework" ]
+    } else if (is_win) {
+      libs += [ "DbgHelp.lib" ]
     }
+
     defines = []
     if (skia_tools_require_resources) {
       defines += [ "SK_TOOLS_REQUIRE_RESOURCES" ]
diff --git a/tools/CrashHandler.cpp b/tools/CrashHandler.cpp
index 5fa6a10..fa322e3 100644
--- a/tools/CrashHandler.cpp
+++ b/tools/CrashHandler.cpp
@@ -11,18 +11,13 @@
 
 #include <stdlib.h>
 
-// Disable SetupCrashHandler() unless SK_CRASH_HANDLER is defined.
-#ifndef SK_CRASH_HANDLER
-    void SetupCrashHandler() { }
-
-#elif defined(SK_BUILD_FOR_GOOGLE3)
+#if defined(SK_BUILD_FOR_GOOGLE3)
     #include "base/process_state.h"
     void SetupCrashHandler() { InstallSignalHandlers(); }
 
 #else
 
     #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>
@@ -55,18 +50,34 @@
         }
 
     #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 <cxxabi.h>
+        #include <dlfcn.h>
         #include <execinfo.h>
+        #include <string.h>
 
         static void handler(int sig) {
-            static const int kMax = 64;
-            void* stack[kMax];
-            const int count = backtrace(stack, kMax);
+            void* stack[64];
+            const int count = backtrace(stack, SK_ARRAY_COUNT(stack));
+            char** symbols = backtrace_symbols(stack, count);
 
             SkDebugf("\nSignal %d [%s]:\n", sig, strsignal(sig));
-            backtrace_symbols_fd(stack, count, 2/*stderr*/);
+            for (int i = 0; i < count; i++) {
+                Dl_info info;
+                if (dladdr(stack[i], &info) && info.dli_sname) {
+                    char demangled[256];
+                    size_t len = SK_ARRAY_COUNT(demangled);
+                    int ok;
+
+                    abi::__cxa_demangle(info.dli_sname, demangled, &len, &ok);
+                    if (ok == 0) {
+                        SkDebugf("    %s\n", demangled);
+                        continue;
+                    }
+                }
+                SkDebugf("    %s\n", symbols[i]);
+            }
 
             // Exit NOW.  Don't notify other threads, don't call anything registered with atexit().
             _Exit(sig);
@@ -84,6 +95,7 @@
                 SIGFPE,
                 SIGILL,
                 SIGSEGV,
+                SIGTRAP,
             };
 
             for (size_t i = 0; i < sizeof(kSignals) / sizeof(kSignals[0]); i++) {
@@ -95,9 +107,10 @@
             }
         }
 
-    #elif defined(SK_CRASH_HANDLER) && defined(SK_BUILD_FOR_WIN)
+    #elif defined(SK_BUILD_FOR_WIN)
 
         #include <DbgHelp.h>
+        #include "SkMalloc.h"
 
         static const struct {
             const char* name;
@@ -184,9 +197,9 @@
             SetUnhandledExceptionFilter(handler);
         }
 
-    #else  // We asked for SK_CRASH_HANDLER, but it's not Mac, Linux, or Windows.  Sorry!
+    #else
 
         void SetupCrashHandler() { }
 
     #endif
-#endif // SK_CRASH_HANDLER
+#endif // SK_BUILD_FOR_GOOGLE3?
diff --git a/tools/fm/fm.cpp b/tools/fm/fm.cpp
index 2a918ab..67e2e6e 100644
--- a/tools/fm/fm.cpp
+++ b/tools/fm/fm.cpp
@@ -3,6 +3,7 @@
 
 #include "CommandLineFlags.h"
 #include "CommonFlags.h"
+#include "CrashHandler.h"
 #include "EventTracingPriv.h"
 #include "GrContextFactory.h"
 #include "GrContextOptions.h"
@@ -96,11 +97,6 @@
     return false;
 }
 
-static void exit_with_failure() {
-    // TODO: dump stack trace, debug trap, print currently running job, etc?
-    exit(1);
-}
-
 struct Result {
     enum { Ok, Skip, Fail} status;
     SkString               failure;
@@ -350,6 +346,7 @@
 
 int main(int argc, char** argv) {
     CommandLineFlags::Parse(argc, argv);
+    SetupCrashHandler();
 
     if (FLAGS_cpuDetect) {
         SkGraphics::Init();
@@ -503,6 +500,7 @@
     for (auto source : sources) {
         const auto start = std::chrono::steady_clock::now();
         fprintf(stdout, "%50s", source.name.c_str());
+        fflush(stdout);
 
         const SkImageInfo info = unsized_info.makeWH(source.size.width(),
                                                      source.size.height());
@@ -513,8 +511,7 @@
                 case Result::Ok:   break;
                 case Result::Skip: return false;
                 case Result::Fail:
-                    fprintf(stderr, "%s failed: %s\n", source.name.c_str(), result.failure.c_str());
-                    exit_with_failure();
+                    SK_ABORT(result.failure.c_str());
             }
             return true;
         };
@@ -550,8 +547,7 @@
 
         SkBitmap bitmap;
         if (image && !image->asLegacyBitmap(&bitmap)) {
-            fprintf(stderr, "SkImage::asLegacyBitmap() failed.\n");
-            exit_with_failure();
+            SK_ABORT("SkImage::asLegacyBitmap() failed.");
         }
 
         HashAndEncode hashAndEncode{bitmap};
@@ -577,8 +573,7 @@
             if (image) {
                 if (!hashAndEncode.writePngTo(path.c_str(), md5.c_str(),
                                               FLAGS_key, FLAGS_properties)) {
-                    fprintf(stderr, "Could not write to %s.\n", path.c_str());
-                    exit_with_failure();
+                    SK_ABORT("Could not write .png.");
                 }
             } else {
                 SkFILEWStream file(path.c_str());