Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Gather demangled stack traces and report the same to console on crashes. #16450

Merged
merged 1 commit into from
Mar 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions benchmarking/benchmarking.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@

#include "benchmarking.h"

#include "flutter/fml/backtrace.h"
#include "flutter/fml/icu_util.h"

namespace benchmarking {

int Main(int argc, char** argv) {
fml::InstallCrashHandler();
benchmark::Initialize(&argc, argv);
fml::icu::InitializeICU("icudtl.dat");
::benchmark::RunSpecifiedBenchmarks();
Expand Down
4 changes: 4 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ FILE: ../../../flutter/flow/view_holder.cc
FILE: ../../../flutter/flow/view_holder.h
FILE: ../../../flutter/flutter_frontend_server/bin/starter.dart
FILE: ../../../flutter/flutter_frontend_server/lib/server.dart
FILE: ../../../flutter/fml/backtrace.cc
FILE: ../../../flutter/fml/backtrace.h
FILE: ../../../flutter/fml/backtrace_stub.cc
FILE: ../../../flutter/fml/backtrace_unittests.cc
FILE: ../../../flutter/fml/base32.cc
FILE: ../../../flutter/fml/base32.h
FILE: ../../../flutter/fml/base32_unittest.cc
Expand Down
8 changes: 8 additions & 0 deletions fml/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import("//flutter/testing/testing.gni")

source_set("fml") {
sources = [
"backtrace.h",
"base32.cc",
"base32.h",
"build_config.h",
Expand Down Expand Up @@ -86,6 +87,12 @@ source_set("fml") {
"wakeable.h",
]

if (is_mac || is_ios || is_linux) {
sources += [ "backtrace.cc" ]
} else {
sources += [ "backtrace_stub.cc" ]
}

public_deps = []

deps = [
Expand Down Expand Up @@ -229,6 +236,7 @@ executable("fml_unittests") {
testonly = true

sources = [
"backtrace_unittests.cc",
"base32_unittest.cc",
"command_line_unittest.cc",
"file_unittest.cc",
Expand Down
136 changes: 136 additions & 0 deletions fml/backtrace.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "flutter/fml/backtrace.h"

#include <cxxabi.h>
#include <sstream>

#include <dlfcn.h>
#include <execinfo.h>
#include <signal.h>

#include "flutter/fml/logging.h"

namespace fml {

static std::string kKUnknownFrameName = "Unknown";

static std::string DemangleSymbolName(const std::string& mangled) {
if (mangled == kKUnknownFrameName) {
return kKUnknownFrameName;
}

int status = 0;
size_t length = 0;
char* demangled = __cxxabiv1::__cxa_demangle(
mangled.data(), // mangled name
nullptr, // output buffer (malloc-ed if nullptr)
&length, // demangled length
&status);

if (demangled == nullptr || status != 0) {
return mangled;
}

auto demangled_string = std::string{demangled, length};
free(demangled);
return demangled_string;
}

static std::string GetSymbolName(void* symbol) {
Dl_info info = {};

if (::dladdr(symbol, &info) == 0) {
return kKUnknownFrameName;
}

return DemangleSymbolName({info.dli_sname});
}

std::string BacktraceHere(size_t offset) {
constexpr size_t kMaxFrames = 256;
void* symbols[kMaxFrames];
const auto available_frames = ::backtrace(symbols, kMaxFrames);
if (available_frames <= 0) {
return "";
}

std::stringstream stream;
for (int i = 1 + offset; i < available_frames; ++i) {
stream << "Frame " << i - 1 - offset << ": " << symbols[i] << " "
<< GetSymbolName(symbols[i]) << std::endl;
}
return stream.str();
}

static size_t kKnownSignalHandlers[] = {
SIGABRT, // abort program
SIGFPE, // floating-point exception
SIGBUS, // bus error
SIGSEGV, // segmentation violation
SIGSYS, // non-existent system call invoked
SIGPIPE, // write on a pipe with no reader
SIGALRM, // real-time timer expired
SIGTERM, // software termination signal
};

static std::string SignalNameToString(int signal) {
switch (signal) {
case SIGABRT:
return "SIGABRT";
case SIGFPE:
return "SIGFPE";
case SIGBUS:
return "SIGBUS";
case SIGSEGV:
return "SIGSEGV";
case SIGSYS:
return "SIGSYS";
case SIGPIPE:
return "SIGPIPE";
case SIGALRM:
return "SIGALRM";
case SIGTERM:
return "SIGTERM";
};
return std::to_string(signal);
}

static void ToggleSignalHandlers(bool set);

static void SignalHandler(int signal) {
// We are a crash signal handler. This can only happen once. Since we don't
// want to catch crashes while we are generating the crash reports, disable
// all set signal handlers to their default values before reporting the crash
// and re-raising the signal.
ToggleSignalHandlers(false);

FML_LOG(ERROR) << "Caught signal " << SignalNameToString(signal)
<< " during program execution." << std::endl
<< BacktraceHere(3);

::raise(signal);
}

static void ToggleSignalHandlers(bool set) {
for (size_t i = 0; i < sizeof(kKnownSignalHandlers) / sizeof(size_t); ++i) {
auto signal_name = kKnownSignalHandlers[i];
auto handler = set ? &SignalHandler : SIG_DFL;

if (::signal(signal_name, handler) == SIG_ERR) {
FML_LOG(ERROR) << "Could not attach signal handler for " << signal_name;
}
}
}

void InstallCrashHandler() {
ToggleSignalHandlers(true);
}

bool IsCrashHandlingSupported() {
return true;
}

} // namespace fml
22 changes: 22 additions & 0 deletions fml/backtrace.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef FLUTTER_FML_BACKTRACE_H_
#define FLUTTER_FML_BACKTRACE_H_

#include <string>

#include "flutter/fml/macros.h"

namespace fml {

std::string BacktraceHere(size_t offset = 0);

void InstallCrashHandler();

bool IsCrashHandlingSupported();

} // namespace fml

#endif // FLUTTER_FML_BACKTRACE_H_
23 changes: 23 additions & 0 deletions fml/backtrace_stub.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "flutter/fml/backtrace.h"

namespace fml {

static std::string kKUnknownFrameName = "Unknown";

std::string BacktraceHere(size_t offset) {
return "";
}

void InstallCrashHandler() {
// Not supported.
}

bool IsCrashHandlingSupported() {
return false;
}

} // namespace fml
37 changes: 37 additions & 0 deletions fml/backtrace_unittests.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "backtrace.h"
#include "gtest/gtest.h"
#include "logging.h"

namespace fml {
namespace testing {

TEST(BacktraceTest, CanGatherBacktrace) {
if (!IsCrashHandlingSupported()) {
GTEST_SKIP();
return;
}
{
auto trace = BacktraceHere(0);
ASSERT_GT(trace.size(), 0u);
ASSERT_NE(trace.find("Frame 0"), std::string::npos);
}

{
auto trace = BacktraceHere(1);
ASSERT_GT(trace.size(), 0u);
ASSERT_NE(trace.find("Frame 0"), std::string::npos);
}

{
auto trace = BacktraceHere(2);
ASSERT_GT(trace.size(), 0u);
ASSERT_NE(trace.find("Frame 0"), std::string::npos);
}
}

} // namespace testing
} // namespace fml
2 changes: 2 additions & 0 deletions testing/run_all_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <optional>
#include <string>

#include "flutter/fml/backtrace.h"
#include "flutter/fml/build_config.h"
#include "flutter/fml/command_line.h"
#include "flutter/testing/debugger_detection.h"
Expand Down Expand Up @@ -35,6 +36,7 @@ std::optional<fml::TimeDelta> GetTestTimeoutFromArgs(int argc, char** argv) {
}

int main(int argc, char** argv) {
fml::InstallCrashHandler();
#ifdef OS_IOS
asl_log_descriptor(NULL, NULL, ASL_LEVEL_NOTICE, STDOUT_FILENO,
ASL_LOG_DESCRIPTOR_WRITE);
Expand Down