Merge pull request #1822 from hokein/crashpad-mac

Crashpad!
This commit is contained in:
Cheng Zhao 2015-06-04 22:11:14 +08:00
commit f54506acc0
12 changed files with 155 additions and 61 deletions

1
.gitignore vendored
View file

@ -6,6 +6,7 @@
/vendor/brightray/vendor/download/ /vendor/brightray/vendor/download/
/vendor/python_26/ /vendor/python_26/
/vendor/npm/ /vendor/npm/
/vendor/.gclient
node_modules/ node_modules/
*.xcodeproj *.xcodeproj
*.swp *.swp

3
.gitmodules vendored
View file

@ -13,3 +13,6 @@
[submodule "vendor/native_mate"] [submodule "vendor/native_mate"]
path = vendor/native_mate path = vendor/native_mate
url = https://github.com/zcbenz/native-mate.git url = https://github.com/zcbenz/native-mate.git
[submodule "vendor/crashpad"]
path = vendor/crashpad
url = https://github.com/atom/crashpad.git

View file

@ -275,7 +275,8 @@
}], # OS=="win" }], # OS=="win"
['OS=="mac"', { ['OS=="mac"', {
'dependencies': [ 'dependencies': [
'vendor/breakpad/breakpad.gyp:breakpad', 'vendor/crashpad/client/client.gyp:crashpad_client',
'vendor/crashpad/handler/handler.gyp:crashpad_handler',
], ],
}], # OS=="mac" }], # OS=="mac"
['OS=="linux"', { ['OS=="linux"', {
@ -430,8 +431,7 @@
{ {
'destination': '<(PRODUCT_DIR)/<(product_name) Framework.framework/Versions/A/Resources', 'destination': '<(PRODUCT_DIR)/<(product_name) Framework.framework/Versions/A/Resources',
'files': [ 'files': [
'<(PRODUCT_DIR)/Inspector', '<(PRODUCT_DIR)/crashpad_handler',
'<(PRODUCT_DIR)/crash_report_sender.app',
], ],
}, },
], ],

View file

@ -11,6 +11,8 @@
#include "atom/common/node_includes.h" #include "atom/common/node_includes.h"
using crash_reporter::CrashReporter;
namespace mate { namespace mate {
template<> template<>
@ -31,17 +33,30 @@ struct Converter<std::map<std::string, std::string> > {
} }
}; };
template<>
struct Converter<CrashReporter::UploadReportResult> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const CrashReporter::UploadReportResult& reports) {
mate::Dictionary dict(isolate, v8::Object::New(isolate));
dict.Set("date", v8::Date::New(isolate, reports.first*1000.0));
dict.Set("id", reports.second);
return dict.GetHandle();
}
};
} // namespace mate } // namespace mate
namespace { namespace {
void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused, void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
v8::Local<v8::Context> context, void* priv) { v8::Local<v8::Context> context, void* priv) {
using crash_reporter::CrashReporter;
mate::Dictionary dict(context->GetIsolate(), exports); mate::Dictionary dict(context->GetIsolate(), exports);
auto report = base::Unretained(CrashReporter::GetInstance());
dict.SetMethod("start", dict.SetMethod("start",
base::Bind(&CrashReporter::Start, base::Bind(&CrashReporter::Start, report));
base::Unretained(CrashReporter::GetInstance()))); dict.SetMethod("_getUploadedReports",
base::Bind(&CrashReporter::GetUploadedReports, report));
} }
} // namespace } // namespace

View file

@ -41,6 +41,10 @@ class CrashReporter
start() start()
getLastCrashReport: -> getLastCrashReport: ->
if process.platform is 'darwin'
reports = binding._getUploadedReports()
return if reports.length > 0 then reports[0] else null
tmpdir = tmpdir =
if process.platform is 'win32' if process.platform is 'win32'
os.tmpdir() os.tmpdir()

View file

@ -39,4 +39,9 @@ void CrashReporter::SetUploadParameters(const StringMap& parameters) {
SetUploadParameters(); SetUploadParameters();
} }
std::vector<CrashReporter::UploadReportResult>
CrashReporter::GetUploadedReports() {
return std::vector<CrashReporter::UploadReportResult>();
}
} // namespace crash_reporter } // namespace crash_reporter

View file

@ -7,6 +7,8 @@
#include <map> #include <map>
#include <string> #include <string>
#include <utility>
#include <vector>
#include "base/basictypes.h" #include "base/basictypes.h"
@ -15,6 +17,7 @@ namespace crash_reporter {
class CrashReporter { class CrashReporter {
public: public:
typedef std::map<std::string, std::string> StringMap; typedef std::map<std::string, std::string> StringMap;
typedef std::pair<int, std::string> UploadReportResult; // upload-date, id
static CrashReporter* GetInstance(); static CrashReporter* GetInstance();
@ -25,6 +28,8 @@ class CrashReporter {
bool skip_system_crash_handler, bool skip_system_crash_handler,
const StringMap& extra_parameters); const StringMap& extra_parameters);
virtual std::vector<CrashReporter::UploadReportResult> GetUploadedReports();
protected: protected:
CrashReporter(); CrashReporter();
virtual ~CrashReporter(); virtual ~CrashReporter();

View file

@ -6,13 +6,20 @@
#define ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_MAC_H_ #define ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_MAC_H_
#include <string> #include <string>
#include <vector>
#include "atom/common/crash_reporter/crash_reporter.h" #include "atom/common/crash_reporter/crash_reporter.h"
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#import "vendor/breakpad/src/client/mac/Framework/Breakpad.h" #include "base/memory/scoped_ptr.h"
#include "base/strings/string_piece.h"
#include "vendor/crashpad/client/simple_string_dictionary.h"
template <typename T> struct DefaultSingletonTraits; template <typename T> struct DefaultSingletonTraits;
namespace crashpad {
class CrashReportDatabase;
}
namespace crash_reporter { namespace crash_reporter {
class CrashReporterMac : public CrashReporter { class CrashReporterMac : public CrashReporter {
@ -33,7 +40,14 @@ class CrashReporterMac : public CrashReporter {
CrashReporterMac(); CrashReporterMac();
virtual ~CrashReporterMac(); virtual ~CrashReporterMac();
BreakpadRef breakpad_; void SetUploadsEnabled(bool enable_uploads);
void SetCrashKeyValue(const base::StringPiece& key,
const base::StringPiece& value);
std::vector<UploadReportResult> GetUploadedReports() override;
scoped_ptr<crashpad::SimpleStringDictionary> simple_string_dictionary_;
scoped_ptr<crashpad::CrashReportDatabase> crash_report_database_;
DISALLOW_COPY_AND_ASSIGN(CrashReporterMac); DISALLOW_COPY_AND_ASSIGN(CrashReporterMac);
}; };

View file

@ -4,20 +4,24 @@
#include "atom/common/crash_reporter/crash_reporter_mac.h" #include "atom/common/crash_reporter/crash_reporter_mac.h"
#include "base/files/file_path.h"
#include "base/mac/bundle_locations.h"
#include "base/mac/mac_util.h" #include "base/mac/mac_util.h"
#include "base/memory/singleton.h" #include "base/memory/singleton.h"
#include "base/strings/string_piece.h"
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h" #include "base/strings/sys_string_conversions.h"
#import "vendor/breakpad/src/client/apple/Framework/BreakpadDefines.h" #include "vendor/crashpad/client/crash_report_database.h"
#include "vendor/crashpad/client/crashpad_client.h"
#include "vendor/crashpad/client/crashpad_info.h"
#include "vendor/crashpad/client/settings.h"
namespace crash_reporter { namespace crash_reporter {
CrashReporterMac::CrashReporterMac() CrashReporterMac::CrashReporterMac() {
: breakpad_(NULL) {
} }
CrashReporterMac::~CrashReporterMac() { CrashReporterMac::~CrashReporterMac() {
if (breakpad_ != NULL)
BreakpadRelease(breakpad_);
} }
void CrashReporterMac::InitBreakpad(const std::string& product_name, void CrashReporterMac::InitBreakpad(const std::string& product_name,
@ -26,54 +30,50 @@ void CrashReporterMac::InitBreakpad(const std::string& product_name,
const std::string& submit_url, const std::string& submit_url,
bool auto_submit, bool auto_submit,
bool skip_system_crash_handler) { bool skip_system_crash_handler) {
if (breakpad_ != NULL) // check whether crashpad has been initilized.
BreakpadRelease(breakpad_); // Only need to initilize once.
if (simple_string_dictionary_)
NSMutableDictionary* parameters =
[NSMutableDictionary dictionaryWithCapacity:4];
[parameters setValue:@ATOM_PRODUCT_NAME
forKey:@BREAKPAD_PRODUCT];
[parameters setValue:base::SysUTF8ToNSString(product_name)
forKey:@BREAKPAD_PRODUCT_DISPLAY];
[parameters setValue:base::SysUTF8ToNSString(version)
forKey:@BREAKPAD_VERSION];
[parameters setValue:base::SysUTF8ToNSString(company_name)
forKey:@BREAKPAD_VENDOR];
[parameters setValue:base::SysUTF8ToNSString(submit_url)
forKey:@BREAKPAD_URL];
[parameters setValue:(auto_submit ? @"YES" : @"NO")
forKey:@BREAKPAD_SKIP_CONFIRM];
[parameters setValue:(skip_system_crash_handler ? @"YES" : @"NO")
forKey:@BREAKPAD_SEND_AND_EXIT];
// Report all crashes (important for testing the crash reporter).
[parameters setValue:@"0" forKey:@BREAKPAD_REPORT_INTERVAL];
// Put dump files under "/tmp/ProductName Crashes".
std::string dump_dir = "/tmp/" + product_name + " Crashes";
[parameters setValue:base::SysUTF8ToNSString(dump_dir)
forKey:@BREAKPAD_DUMP_DIRECTORY];
// Temporarily run Breakpad in-process on 10.10 and later because APIs that
// it depends on got broken (http://crbug.com/386208).
// This can catch crashes in the browser process only.
if (base::mac::IsOSYosemiteOrLater()) {
[parameters setObject:[NSNumber numberWithBool:YES]
forKey:@BREAKPAD_IN_PROCESS];
}
breakpad_ = BreakpadCreate(parameters);
if (!breakpad_) {
LOG(ERROR) << "Failed to initialize breakpad";
return; return;
std::string dump_dir = "/tmp/" + product_name + " Crashes";
base::FilePath database_path(dump_dir);
if (is_browser_) {
@autoreleasepool {
base::FilePath framework_bundle_path = base::mac::FrameworkBundlePath();
base::FilePath handler_path =
framework_bundle_path.Append("Resources").Append("crashpad_handler");
crashpad::CrashpadClient crashpad_client;
if (crashpad_client.StartHandler(handler_path, database_path,
submit_url,
StringMap(),
std::vector<std::string>())) {
crashpad_client.UseHandler();
}
} // @autoreleasepool
} }
for (StringMap::const_iterator iter = upload_parameters_.begin(); crashpad::CrashpadInfo* crashpad_info =
iter != upload_parameters_.end(); ++iter) { crashpad::CrashpadInfo::GetCrashpadInfo();
BreakpadAddUploadParameter(breakpad_, if (skip_system_crash_handler) {
base::SysUTF8ToNSString(iter->first), crashpad_info->set_system_crash_reporter_forwarding(
base::SysUTF8ToNSString(iter->second)); crashpad::TriState::kDisabled);
}
simple_string_dictionary_.reset(new crashpad::SimpleStringDictionary());
crashpad_info->set_simple_annotations(simple_string_dictionary_.get());
SetCrashKeyValue("prod", ATOM_PRODUCT_NAME);
SetCrashKeyValue("process_type", is_browser_ ? "browser" : "renderer");
SetCrashKeyValue("ver", version);
for (const auto& upload_parameter: upload_parameters_) {
SetCrashKeyValue(upload_parameter.first, upload_parameter.second);
}
if (is_browser_) {
crash_report_database_ = crashpad::CrashReportDatabase::Initialize(
database_path);
SetUploadsEnabled(auto_submit);
} }
} }
@ -81,6 +81,48 @@ void CrashReporterMac::SetUploadParameters() {
upload_parameters_["platform"] = "darwin"; upload_parameters_["platform"] = "darwin";
} }
void CrashReporterMac::SetUploadsEnabled(bool enable_uploads) {
if (crash_report_database_) {
crashpad::Settings* settings = crash_report_database_->GetSettings();
settings->SetUploadsEnabled(enable_uploads);
}
}
void CrashReporterMac::SetCrashKeyValue(const base::StringPiece& key,
const base::StringPiece& value) {
simple_string_dictionary_->SetKeyValue(key.data(), value.data());
}
std::vector<CrashReporter::UploadReportResult>
CrashReporterMac::GetUploadedReports() {
std::vector<CrashReporter::UploadReportResult> uploaded_reports;
if (!crash_report_database_) {
return uploaded_reports;
}
std::vector<crashpad::CrashReportDatabase::Report> completed_reports;
crashpad::CrashReportDatabase::OperationStatus status =
crash_report_database_->GetCompletedReports(&completed_reports);
if (status != crashpad::CrashReportDatabase::kNoError) {
return uploaded_reports;
}
for (const crashpad::CrashReportDatabase::Report& completed_report :
completed_reports) {
if (completed_report.uploaded) {
uploaded_reports.push_back(
UploadReportResult(static_cast<int>(completed_report.creation_time),
completed_report.id));
}
}
auto sort_by_time = [](const UploadReportResult& a,
const UploadReportResult& b) {return a.first >= b.first;};
std::sort(uploaded_reports.begin(), uploaded_reports.end(), sort_by_time);
return uploaded_reports;
}
// static // static
CrashReporterMac* CrashReporterMac::GetInstance() { CrashReporterMac* CrashReporterMac::GetInstance() {
return Singleton<CrashReporterMac>::get(); return Singleton<CrashReporterMac>::get();

View file

@ -27,6 +27,11 @@ crashReporter.start({
* Only string properties are send correctly. * Only string properties are send correctly.
* Nested objects are not supported. * Nested objects are not supported.
**Note:** On OS X, electron uses a new `crashpad` client, which is different
with the `breakpad` on Windows and Linux. To enable crash collection feature,
you are required to call `crashReporter.start` API to initiliaze `crashpad` in
main process, even you only collect crash report in renderer process.
## crashReporter.getLastCrashReport() ## crashReporter.getLastCrashReport()
Returns the date and ID of last crash report, when there was no crash report Returns the date and ID of last crash report, when there was no crash report

View file

@ -5,12 +5,10 @@ url = require 'url'
remote = require 'remote' remote = require 'remote'
formidable = require 'formidable' formidable = require 'formidable'
crashReporter = remote.require 'crash-reporter'
BrowserWindow = remote.require 'browser-window' BrowserWindow = remote.require 'browser-window'
describe 'crash-reporter module', -> describe 'crash-reporter module', ->
# We have trouble makeing crash reporter work on Yosemite.
return if process.platform is 'darwin'
fixtures = path.resolve __dirname, 'fixtures' fixtures = path.resolve __dirname, 'fixtures'
w = null w = null
@ -47,4 +45,5 @@ describe 'crash-reporter module', ->
protocol: 'file' protocol: 'file'
pathname: path.join fixtures, 'api', 'crash.html' pathname: path.join fixtures, 'api', 'crash.html'
search: "?port=#{port}" search: "?port=#{port}"
crashReporter.start {'submitUrl': 'http://127.0.0.1:' + port}
w.loadUrl url w.loadUrl url

1
vendor/crashpad vendored Submodule

@ -0,0 +1 @@
Subproject commit e6a0d433b0ee399eecce2bef671794771052ffdb