folly: symbolizer: small terse write fix + colorize signal handler output if printing...
authorLucian Grijincu <lucian@fb.com>
Tue, 14 Jan 2014 23:38:21 +0000 (15:38 -0800)
committerJordan DeLong <jdelong@fb.com>
Thu, 16 Jan 2014 19:21:37 +0000 (11:21 -0800)
Summary:
Detailed output is a bit hard to parse visually. Add some colors for
clarity (clownyness?). Enabled only when printing stack traces from a
signal-handler to TTY.

Also:
- terse output printed empty lines if it could not find a symbol for an address; fixed by printing "(unknown)".
- added a dummy ##Crash## program to test colorization easily

Test Plan: n/a

Reviewed By: tudorb@fb.com

FB internal diff: D1128303

folly/experimental/exception_tracer/ExceptionTracer.cpp
folly/experimental/symbolizer/SignalHandler.cpp
folly/experimental/symbolizer/Symbolizer.cpp
folly/experimental/symbolizer/Symbolizer.h
folly/experimental/symbolizer/test/Crash.cpp [new file with mode: 0644]

index c2fd2b9f566d2d6df997c0e4f297324a7014c382..04d3b55ea0efb77b8be75831c1db435068657e34 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2013 Facebook, Inc.
+ * Copyright 2014 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-
 #include "folly/experimental/exception_tracer/ExceptionTracer.h"
 
 #include <dlfcn.h>
@@ -69,7 +68,7 @@ std::ostream& operator<<(std::ostream& out, const ExceptionInfo& info) {
       Symbolizer symbolizer;
       symbolizer.symbolize(addresses, frames.data(), frameCount);
 
-      OStreamSymbolizePrinter osp(out);
+      OStreamSymbolizePrinter osp(out, SymbolizePrinter::COLOR_IF_TTY);
       osp.println(addresses, frames.data(), frameCount);
     }
   } catch (const std::exception& e) {
@@ -206,4 +205,3 @@ void installHandlers() {
 
 }  // namespace exception_tracer
 }  // namespace folly
-
index 545380f7d066d2d3136c29223e9a6e9710c1fe42..89a58d1c85e0af513bd2144a9be501189213a545 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2013 Facebook, Inc.
+ * Copyright 2014 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -201,7 +201,7 @@ void dumpStackTrace() {
     Symbolizer symbolizer;
     symbolizer.symbolize(addresses);
 
-    FDSymbolizePrinter printer(STDERR_FILENO);
+    FDSymbolizePrinter printer(STDERR_FILENO, SymbolizePrinter::COLOR_IF_TTY);
     printer.println(addresses);
   }
 }
@@ -286,4 +286,3 @@ void installFatalSignalHandler() {
 }
 
 }}  // namespaces
-
index d0f0c1650435f52c02de280b945a754735777c01..d7f4db96898eca2463630171be651e6bbc8193c2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2013 Facebook, Inc.
+ * Copyright 2014 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include "folly/experimental/symbolizer/Symbolizer.h"
 
 #include <limits.h>
+#include <cstdio>
+#include <iostream>
+#include <map>
+
+#ifdef __GNUC__
+#include <ext/stdio_filebuf.h>
+#include <ext/stdio_sync_filebuf.h>
+#endif
 
 #include "folly/Conv.h"
 #include "folly/FileUtil.h"
+#include "folly/ScopeGuard.h"
 #include "folly/String.h"
 
 #include "folly/experimental/symbolizer/Elf.h"
 #include "folly/experimental/symbolizer/Dwarf.h"
 #include "folly/experimental/symbolizer/LineReader.h"
 
+
 namespace folly {
 namespace symbolizer {
 
@@ -244,6 +254,9 @@ void Symbolizer::symbolize(const uintptr_t* addresses,
 
 namespace {
 const char kHexChars[] = "0123456789abcdef";
+const SymbolizePrinter::Color kAddressColor = SymbolizePrinter::Color::BLUE;
+const SymbolizePrinter::Color kFunctionColor = SymbolizePrinter::Color::PURPLE;
+const SymbolizePrinter::Color kFileColor = SymbolizePrinter::Color::DEFAULT;
 }  // namespace
 
 void SymbolizePrinter::print(uintptr_t address, const SymbolizedFrame& frame) {
@@ -252,6 +265,9 @@ void SymbolizePrinter::print(uintptr_t address, const SymbolizedFrame& frame) {
     return;
   }
 
+  SCOPE_EXIT { color(Color::DEFAULT); };
+
+  color(kAddressColor);
   // Can't use sprintf, not async-signal-safe
   static_assert(sizeof(uintptr_t) <= 8, "huge uintptr_t?");
   char buf[] = "    @ 0000000000000000";
@@ -267,6 +283,7 @@ void SymbolizePrinter::print(uintptr_t address, const SymbolizedFrame& frame) {
   }
   doPrint(folly::StringPiece(buf, end));
 
+  color(kFunctionColor);
   char mangledBuf[1024];
   if (!frame.found) {
     doPrint(" (not found)");
@@ -289,6 +306,7 @@ void SymbolizePrinter::print(uintptr_t address, const SymbolizedFrame& frame) {
   }
 
   if (!(options_ & NO_FILE_AND_LINE)) {
+    color(kFileColor);
     char fileBuf[PATH_MAX];
     fileBuf[0] = '\0';
     if (frame.location.hasFileAndLine) {
@@ -317,6 +335,33 @@ void SymbolizePrinter::print(uintptr_t address, const SymbolizedFrame& frame) {
   }
 }
 
+namespace {
+
+const std::map<SymbolizePrinter::Color, std::string> kColorMap = {
+  { SymbolizePrinter::Color::DEFAULT,  "\x1B[0m" },
+  { SymbolizePrinter::Color::RED,  "\x1B[31m" },
+  { SymbolizePrinter::Color::GREEN,  "\x1B[32m" },
+  { SymbolizePrinter::Color::YELLOW,  "\x1B[33m" },
+  { SymbolizePrinter::Color::BLUE,  "\x1B[34m" },
+  { SymbolizePrinter::Color::CYAN,  "\x1B[36m" },
+  { SymbolizePrinter::Color::WHITE,  "\x1B[37m" },
+  { SymbolizePrinter::Color::PURPLE,  "\x1B[35m" },
+};
+
+}
+
+void SymbolizePrinter::color(SymbolizePrinter::Color color) {
+  if ((options_ & COLOR) == 0 &&
+      ((options_ & COLOR_IF_TTY) == 0 || !isTty_)) {
+    return;
+  }
+  auto it = kColorMap.find(color);
+  if (it == kColorMap.end()) {
+    return;
+  }
+  doPrint(it->second);
+}
+
 void SymbolizePrinter::println(uintptr_t address,
                                const SymbolizedFrame& frame) {
   print(address, frame);
@@ -330,9 +375,9 @@ void SymbolizePrinter::printTerse(uintptr_t address,
     memcpy(mangledBuf, frame.name.data(), frame.name.size());
     mangledBuf[frame.name.size()] = '\0';
 
-    char demangledBuf[1024];
+    char demangledBuf[1024] = {0};
     demangle(mangledBuf, demangledBuf, sizeof(demangledBuf));
-    doPrint(demangledBuf);
+    doPrint(strlen(demangledBuf) == 0 ? "(unknown)" : demangledBuf);
   } else {
     // Can't use sprintf, not async-signal-safe
     static_assert(sizeof(uintptr_t) <= 8, "huge uintptr_t?");
@@ -356,14 +401,60 @@ void SymbolizePrinter::println(const uintptr_t* addresses,
   }
 }
 
+namespace {
+
+int getFD(const std::ios& stream) {
+#ifdef __GNUC__
+  std::streambuf* buf = stream.rdbuf();
+  using namespace __gnu_cxx;
+
+  {
+    auto sbuf = dynamic_cast<stdio_sync_filebuf<char>*>(buf);
+    if (sbuf) {
+      return fileno(sbuf->file());
+    }
+  }
+  {
+    auto sbuf = dynamic_cast<stdio_filebuf<char>*>(buf);
+    if (sbuf) {
+      return sbuf->fd();
+    }
+  }
+#endif  // __GNUC__
+  return -1;
+}
+
+bool isTty(int options, int fd) {
+  return ((options & SymbolizePrinter::TERSE) == 0 &&
+          (options & SymbolizePrinter::COLOR_IF_TTY) != 0 &&
+          fd >= 0 && ::isatty(fd));
+}
+
+}  // anonymous namespace
+
+OStreamSymbolizePrinter::OStreamSymbolizePrinter(std::ostream& out, int options)
+  : SymbolizePrinter(options, isTty(options, getFD(out))),
+    out_(out) {
+}
+
 void OStreamSymbolizePrinter::doPrint(StringPiece sp) {
   out_ << sp;
 }
 
+FDSymbolizePrinter::FDSymbolizePrinter(int fd, int options)
+  : SymbolizePrinter(options, isTty(options, fd)),
+    fd_(fd) {
+}
+
 void FDSymbolizePrinter::doPrint(StringPiece sp) {
   writeFull(fd_, sp.data(), sp.size());
 }
 
+FILESymbolizePrinter::FILESymbolizePrinter(FILE* file, int options)
+  : SymbolizePrinter(options, isTty(options, fileno(file))),
+    file_(file) {
+}
+
 void FILESymbolizePrinter::doPrint(StringPiece sp) {
   fwrite(sp.data(), 1, sp.size(), file_);
 }
index 026554920481b59d0c77f18db00583d550653c4c..0c87ed77ae09ee1a3b4e46e037dce34dfcfa9371 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2013 Facebook, Inc.
+ * Copyright 2014 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-
 #ifndef FOLLY_EXPERIMENTAL_SYMBOLIZER_SYMBOLIZER_H_
 #define FOLLY_EXPERIMENTAL_SYMBOLIZER_SYMBOLIZER_H_
 
@@ -158,11 +157,25 @@ class SymbolizePrinter {
 
     // As terse as it gets: function name if found, address otherwise
     TERSE = 1 << 1,
+
+    // Always colorize output (ANSI escape code)
+    COLOR = 1 << 2,
+
+    // Colorize output only if output is printed to a TTY (ANSI escape code)
+    COLOR_IF_TTY = 1 << 3,
   };
 
+  enum Color { DEFAULT, RED, GREEN, YELLOW, BLUE, CYAN, WHITE, PURPLE };
+  void color(Color c);
+
  protected:
-  explicit SymbolizePrinter(int options) : options_(options) { }
+  explicit SymbolizePrinter(int options, bool isTty = false)
+    : options_(options),
+      isTty_(isTty) {
+  }
+
   const int options_;
+  const bool isTty_;
 
  private:
   void printTerse(uintptr_t address, const SymbolizedFrame& frame);
@@ -175,9 +188,7 @@ class SymbolizePrinter {
  */
 class OStreamSymbolizePrinter : public SymbolizePrinter {
  public:
-  explicit OStreamSymbolizePrinter(std::ostream& out, int options=0)
-    : SymbolizePrinter(options),
-      out_(out) { }
+  explicit OStreamSymbolizePrinter(std::ostream& out, int options=0);
  private:
   void doPrint(StringPiece sp) override;
   std::ostream& out_;
@@ -189,9 +200,7 @@ class OStreamSymbolizePrinter : public SymbolizePrinter {
  */
 class FDSymbolizePrinter : public SymbolizePrinter {
  public:
-  explicit FDSymbolizePrinter(int fd, int options=0)
-    : SymbolizePrinter(options),
-      fd_(fd) { }
+  explicit FDSymbolizePrinter(int fd, int options=0);
  private:
   void doPrint(StringPiece sp) override;
   int fd_;
@@ -203,9 +212,7 @@ class FDSymbolizePrinter : public SymbolizePrinter {
  */
 class FILESymbolizePrinter : public SymbolizePrinter {
  public:
-  explicit FILESymbolizePrinter(FILE* file, int options=0)
-    : SymbolizePrinter(options),
-      file_(file) { }
+  explicit FILESymbolizePrinter(FILE* file, int options=0);
  private:
   void doPrint(StringPiece sp) override;
   FILE* file_;
@@ -232,4 +239,3 @@ class StringSymbolizePrinter : public SymbolizePrinter {
 }  // namespace folly
 
 #endif /* FOLLY_EXPERIMENTAL_SYMBOLIZER_SYMBOLIZER_H_ */
-
diff --git a/folly/experimental/symbolizer/test/Crash.cpp b/folly/experimental/symbolizer/test/Crash.cpp
new file mode 100644 (file)
index 0000000..74eedad
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * 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.
+ */
+
+#include "folly/experimental/symbolizer/SignalHandler.h"
+
+int main() {
+  folly::symbolizer::installFatalSignalHandler();
+  *(int*) nullptr = 1;
+  return 0;
+}