Send crash notification when flipper cpp exceptions are suppressed

Summary: This diff adds support to send crash notification whenever the cpp exception of Flipper is suppressed. Also updated the tests regarding the same

Reviewed By: jknoxville

Differential Revision: D13635822

fbshipit-source-id: 01e4a57c391476e5b044e64946d337cb4582a527
This commit is contained in:
Pritesh Nandgaonkar
2019-01-17 16:08:49 -08:00
committed by Facebook Github Bot
parent 944a197cf6
commit 87f64c4535
7 changed files with 164 additions and 22 deletions

View File

@@ -9,8 +9,8 @@ Pod::Spec.new do |spec|
spec.source = { :git => 'https://github.com/facebook/Sonar.git',
:tag => 'v'+flipperkit_version }
spec.module_name = 'Flipper'
spec.public_header_files = 'xplat/Flipper/*.h'
spec.source_files = 'xplat/Flipper/*.{h,cpp,m,mm}'
spec.public_header_files = 'xplat/Flipper/*.h','xplat/utils/*.h'
spec.source_files = 'xplat/Flipper/*.{h,cpp,m,mm}','xplat/Flipper/utils/*.{h,cpp,m,mm}'
spec.libraries = "stdc++"
spec.dependency 'Folly', '~>1.1'
spec.dependency 'RSocket', '~>0.10'

View File

@@ -1,9 +1,8 @@
/*
* Copyright (c) Facebook, Inc.
*
* This source code is licensed under the MIT license found in the LICENSE
* file in the root directory of this source tree.
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the LICENSE
* file in the root directory of this source tree.
*/
#include "FlipperClient.h"
#include <fstream>
@@ -17,6 +16,12 @@
#include "FlipperState.h"
#include "FlipperStep.h"
#include "Log.h"
#if __ANDROID__
#include "utils/CallstackHelper.h"
#endif
#if __APPLE__
#include <execinfo.h>
#endif
#if FB_SONARKIT_ENABLED
@@ -222,6 +227,31 @@ void FlipperClient::onMessageReceived(const dynamic& message) {
responder->error(response);
});
}
std::string FlipperClient::callstack() {
#if __APPLE__
// For some iOS apps, __Unwind_Backtrace symbol wasn't found in sandcastle
// builds, thus, for iOS apps, using backtrace c function.
void* callstack[2048];
int frames = backtrace(callstack, 2048);
char** strs = backtrace_symbols(callstack, frames);
std::string output = "";
for (int i = 0; i < frames; ++i) {
output.append(strs[i]);
output.append("\n");
}
return output;
#elif __ANDROID__
const size_t max = 2048;
void* buffer[max];
std::ostringstream oss;
dumpBacktrace(oss, buffer, captureBacktrace(buffer, max));
std::string output = std::string(oss.str().c_str());
return output;
#else
return "";
#endif
}
void FlipperClient::performAndReportError(const std::function<void()>& func) {
#if FLIPPER_ENABLE_CRASH
// To debug the stack trace and an exception turn on the compiler flag
@@ -231,9 +261,12 @@ void FlipperClient::performAndReportError(const std::function<void()>& func) {
try {
func();
} catch (std::exception& e) {
dynamic message = dynamic::object(
"error", dynamic::object("message", e.what())("stacktrace", "<none>"));
if (connected_) {
std::string callstack = this->callstack();
dynamic message = dynamic::object(
"error",
dynamic::object("message", e.what())("stacktrace", callstack)(
"name", e.what()));
socket_->sendMessage(message);
} else {
log("Error: " + std::string(e.what()));

View File

@@ -1,9 +1,8 @@
/*
* Copyright (c) Facebook, Inc.
*
* This source code is licensed under the MIT license found in the LICENSE
* file in the root directory of this source tree.
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the LICENSE
* file in the root directory of this source tree.
*/
#pragma once
@@ -46,6 +45,9 @@ class FlipperClient : public FlipperConnectionManager::Callbacks {
: socket_(std::move(socket)), flipperState_(state) {
auto step = flipperState_->start("Create client");
socket_->setCallbacks(this);
auto& conn = connections_["flipper-crash-report"];
conn = std::make_shared<FlipperConnectionImpl>(
socket_.get(), "flipper-crash-report");
step->complete();
}
@@ -105,6 +107,7 @@ class FlipperClient : public FlipperConnectionManager::Callbacks {
void disconnect(std::shared_ptr<FlipperPlugin> plugin);
void startBackgroundPlugins();
std::string callstack();
};
} // namespace flipper

View File

@@ -0,0 +1,63 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the LICENSE
* file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#include <dlfcn.h>
#include <unwind.h>
#include <iomanip>
namespace facebook {
namespace flipper {
// TODO: T39093653, Replace the backtrace implementation with folly
// implementation. Didn't use the backtrace() c function as it was not found in
// NDK.
struct BacktraceState {
void** current;
void** end;
};
static _Unwind_Reason_Code unwindCallback(
struct _Unwind_Context* context,
void* arg) {
BacktraceState* state = static_cast<BacktraceState*>(arg);
uintptr_t pc = _Unwind_GetIP(context);
if (pc) {
if (state->current == state->end) {
return _URC_END_OF_STACK;
} else {
*state->current++ = reinterpret_cast<void*>(pc);
}
}
return _URC_NO_REASON;
}
static size_t captureBacktrace(void** buffer, size_t max) {
BacktraceState state = {buffer, buffer + max};
_Unwind_Backtrace(unwindCallback, &state);
return state.current - buffer;
}
static void dumpBacktrace(std::ostream& os, void** buffer, size_t count) {
for (size_t idx = 0; idx < count; ++idx) {
const void* addr = buffer[idx];
const char* symbol = "";
Dl_info info;
if (dladdr(addr, &info) && info.dli_sname) {
symbol = info.dli_sname;
}
os << " #" << std::setw(2) << idx << ": " << addr << " " << symbol
<< "\n";
}
}
} // namespace flipper
} // namespace facebook
#endif

View File

@@ -1,9 +1,8 @@
/*
* Copyright (c) Facebook, Inc.
*
* This source code is licensed under the MIT license found in the LICENSE
* file in the root directory of this source tree.
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the LICENSE
* file in the root directory of this source tree.
*/
#include <Flipper/FlipperClient.h>
#include <FlipperTestLib/FlipperConnectionManagerMock.h>
@@ -282,10 +281,12 @@ TEST(FlipperClientTests, testExceptionUnknownPlugin) {
dynamic messageInit = dynamic::object("method", "init")(
"params", dynamic::object("plugin", "Unknown"));
socket->callbacks->onMessageReceived(messageInit);
EXPECT_EQ(
socket->messages.back()["error"]["message"],
"plugin Unknown not found for method init");
EXPECT_EQ(
socket->messages.back()["error"]["name"],
"plugin Unknown not found for method init");
}
TEST(FlipperClientTests, testExceptionUnknownApi) {
@@ -297,10 +298,12 @@ TEST(FlipperClientTests, testExceptionUnknownApi) {
dynamic messageInit = dynamic::object("method", "execute")(
"params", dynamic::object("api", "Unknown"));
socket->callbacks->onMessageReceived(messageInit);
EXPECT_EQ(
socket->messages.back()["error"]["message"],
"connection Unknown not found for method execute");
EXPECT_EQ(
socket->messages.back()["error"]["name"],
"connection Unknown not found for method execute");
}
TEST(FlipperClientTests, testBackgroundPluginActivated) {