Merge pull request #124 from atom/breakpad
Use breakpad for crash reporting
This commit is contained in:
commit
bf77fcc03e
45 changed files with 1960 additions and 201 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -13,3 +13,6 @@
|
|||
[submodule "vendor/apm"]
|
||||
path = vendor/apm
|
||||
url = https://github.com/atom/apm.git
|
||||
[submodule "vendor/breakpad"]
|
||||
path = vendor/breakpad
|
||||
url = https://github.com/atom/chromium-breakpad.git
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include "app/atom_main_delegate.h"
|
||||
#include "base/environment.h"
|
||||
#include "common/crash_reporter/win/crash_service_main.h"
|
||||
#include "content/public/app/startup_helper_win.h"
|
||||
#include "sandbox/win/src/sandbox_types.h"
|
||||
#else // defined(OS_WIN)
|
||||
|
@ -34,16 +35,20 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t* cmd, int) {
|
|||
int argc = 0;
|
||||
wchar_t** wargv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
|
||||
|
||||
// Attach to the parent console if we've got one so that stdio works
|
||||
AttachConsole(ATTACH_PARENT_PROCESS);
|
||||
|
||||
FILE* dontcare;
|
||||
freopen_s(&dontcare, "CON", "w", stdout);
|
||||
freopen_s(&dontcare, "CON", "w", stderr);
|
||||
freopen_s(&dontcare, "CON", "r", stdin);
|
||||
|
||||
scoped_ptr<base::Environment> env(base::Environment::Create());
|
||||
std::string node_indicator;
|
||||
|
||||
// Make output work in console if we are not in cygiwn.
|
||||
std::string os;
|
||||
if (env->GetVar("OS", &os) && os != "cygwin") {
|
||||
AttachConsole(ATTACH_PARENT_PROCESS);
|
||||
|
||||
FILE* dontcare;
|
||||
freopen_s(&dontcare, "CON", "w", stdout);
|
||||
freopen_s(&dontcare, "CON", "w", stderr);
|
||||
freopen_s(&dontcare, "CON", "r", stdin);
|
||||
}
|
||||
|
||||
std::string node_indicator, crash_service_indicator;
|
||||
if (env->GetVar("ATOM_SHELL_INTERNAL_RUN_AS_NODE", &node_indicator) &&
|
||||
node_indicator == "1") {
|
||||
// Convert argv to to UTF8
|
||||
|
@ -81,6 +86,10 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t* cmd, int) {
|
|||
}
|
||||
// Now that conversion is done, we can finally start.
|
||||
return node::Start(argc, argv);
|
||||
} else if (env->GetVar("ATOM_SHELL_INTERNAL_CRASH_SERVICE",
|
||||
&crash_service_indicator) &&
|
||||
crash_service_indicator == "1") {
|
||||
return crash_service::Main(cmd);
|
||||
}
|
||||
|
||||
sandbox::SandboxInterfaceInfo sandbox_info = {0};
|
||||
|
|
|
@ -34,7 +34,7 @@ base::FilePath AtomMainDelegate::GetResourcesPakFilePath() {
|
|||
|
||||
void AtomMainDelegate::OverrideFrameworkBundlePath() {
|
||||
base::mac::SetOverrideFrameworkBundlePath(
|
||||
GetFrameworksPath().Append("Atom.framework"));
|
||||
GetFrameworksPath().Append("Atom Framework.framework"));
|
||||
}
|
||||
|
||||
void AtomMainDelegate::OverrideChildProcessPath() {
|
||||
|
|
111
atom.gyp
111
atom.gyp
|
@ -2,6 +2,7 @@
|
|||
'variables': {
|
||||
'project_name': 'atom',
|
||||
'product_name': 'Atom',
|
||||
'framework_name': 'Atom Framework',
|
||||
'app_sources': [
|
||||
'app/atom_main.cc',
|
||||
'app/atom_main.h',
|
||||
|
@ -14,7 +15,6 @@
|
|||
'browser/api/lib/atom-delegate.coffee',
|
||||
'browser/api/lib/auto-updater.coffee',
|
||||
'browser/api/lib/browser-window.coffee',
|
||||
'browser/api/lib/crash-reporter.coffee',
|
||||
'browser/api/lib/dialog.coffee',
|
||||
'browser/api/lib/ipc.coffee',
|
||||
'browser/api/lib/menu.coffee',
|
||||
|
@ -26,6 +26,7 @@
|
|||
'browser/atom/rpc-server.coffee',
|
||||
'common/api/lib/callbacks-registry.coffee',
|
||||
'common/api/lib/clipboard.coffee',
|
||||
'common/api/lib/crash-reporter.coffee',
|
||||
'common/api/lib/id-weak-map.coffee',
|
||||
'common/api/lib/shell.coffee',
|
||||
'renderer/api/lib/ipc.coffee',
|
||||
|
@ -41,8 +42,6 @@
|
|||
'browser/api/atom_api_auto_updater.h',
|
||||
'browser/api/atom_api_browser_ipc.cc',
|
||||
'browser/api/atom_api_browser_ipc.h',
|
||||
'browser/api/atom_api_crash_reporter.h',
|
||||
'browser/api/atom_api_crash_reporter.cc',
|
||||
'browser/api/atom_api_dialog.cc',
|
||||
'browser/api/atom_api_dialog.h',
|
||||
'browser/api/atom_api_event.cc',
|
||||
|
@ -87,9 +86,6 @@
|
|||
'browser/browser_mac.mm',
|
||||
'browser/browser_win.cc',
|
||||
'browser/browser_observer.h',
|
||||
'browser/crash_reporter.h',
|
||||
'browser/crash_reporter_mac.mm',
|
||||
'browser/crash_reporter_win.cc',
|
||||
'browser/native_window.cc',
|
||||
'browser/native_window.h',
|
||||
'browser/native_window_mac.h',
|
||||
|
@ -132,6 +128,8 @@
|
|||
'common/api/api_messages.h',
|
||||
'common/api/atom_api_clipboard.cc',
|
||||
'common/api/atom_api_clipboard.h',
|
||||
'common/api/atom_api_crash_reporter.cc',
|
||||
'common/api/atom_api_crash_reporter.h',
|
||||
'common/api/atom_api_id_weak_map.cc',
|
||||
'common/api/atom_api_id_weak_map.h',
|
||||
'common/api/atom_api_shell.cc',
|
||||
|
@ -143,6 +141,16 @@
|
|||
'common/api/atom_extensions.h',
|
||||
'common/api/object_life_monitor.cc',
|
||||
'common/api/object_life_monitor.h',
|
||||
'common/crash_reporter/crash_reporter.cc',
|
||||
'common/crash_reporter/crash_reporter.h',
|
||||
'common/crash_reporter/crash_reporter_mac.h',
|
||||
'common/crash_reporter/crash_reporter_mac.mm',
|
||||
'common/crash_reporter/crash_reporter_win.cc',
|
||||
'common/crash_reporter/crash_reporter_win.h',
|
||||
'common/crash_reporter/win/crash_service.cc',
|
||||
'common/crash_reporter/win/crash_service.h',
|
||||
'common/crash_reporter/win/crash_service_main.cc',
|
||||
'common/crash_reporter/win/crash_service_main.h',
|
||||
'common/draggable_region.cc',
|
||||
'common/draggable_region.h',
|
||||
'common/node_bindings.cc',
|
||||
|
@ -187,9 +195,6 @@
|
|||
'-change',
|
||||
'@loader_path/../Frameworks/Sparkle.framework/Versions/A/Sparkle',
|
||||
'@rpath/Sparkle.framework/Versions/A/Sparkle',
|
||||
'-change',
|
||||
'@executable_path/../Frameworks/Quincy.framework/Versions/A/Quincy',
|
||||
'@rpath/Quincy.framework/Versions/A/Quincy',
|
||||
'${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}'
|
||||
],
|
||||
'atom_source_root': '<!(python tools/atom_source_root.py)',
|
||||
|
@ -249,9 +254,8 @@
|
|||
'destination': '<(PRODUCT_DIR)/<(product_name).app/Contents/Frameworks',
|
||||
'files': [
|
||||
'<(PRODUCT_DIR)/<(product_name) Helper.app',
|
||||
'<(PRODUCT_DIR)/<(product_name).framework',
|
||||
'<(PRODUCT_DIR)/<(framework_name).framework',
|
||||
'frameworks/Sparkle.framework',
|
||||
'frameworks/Quincy.framework',
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -307,7 +311,7 @@
|
|||
],
|
||||
}], # OS=="win"
|
||||
],
|
||||
},
|
||||
}, # target <(project_name)
|
||||
{
|
||||
'target_name': '<(project_name)_lib',
|
||||
'type': 'static_library',
|
||||
|
@ -337,12 +341,22 @@
|
|||
'-limm32.lib',
|
||||
'-loleacc.lib',
|
||||
'-lComdlg32.lib',
|
||||
'-lWininet.lib',
|
||||
'<(atom_source_root)/<(libchromiumcontent_library_dir)/chromiumviews.lib',
|
||||
],
|
||||
},
|
||||
'dependencies': [
|
||||
'vendor/breakpad/breakpad.gyp:breakpad_handler',
|
||||
'vendor/breakpad/breakpad.gyp:breakpad_sender',
|
||||
],
|
||||
}],
|
||||
['OS=="mac"', {
|
||||
'dependencies': [
|
||||
'vendor/breakpad/breakpad.gyp:breakpad',
|
||||
],
|
||||
}],
|
||||
],
|
||||
},
|
||||
}, # target <(product_name)_lib
|
||||
{
|
||||
'target_name': 'generated_sources',
|
||||
'type': 'none',
|
||||
|
@ -382,14 +396,67 @@
|
|||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}, # target generated_sources
|
||||
{
|
||||
'target_name': '<(project_name)_dump_symbols',
|
||||
'type': 'none',
|
||||
'dependencies': [
|
||||
'<(project_name)',
|
||||
],
|
||||
'conditions': [
|
||||
['OS=="mac"', {
|
||||
'dependencies': [
|
||||
'vendor/breakpad/breakpad.gyp:dump_syms',
|
||||
],
|
||||
'actions': [
|
||||
{
|
||||
'action_name': 'Dump Symbols',
|
||||
'inputs': [
|
||||
'<(PRODUCT_DIR)/<(product_name).app/Contents/MacOS/<(product_name)',
|
||||
],
|
||||
'outputs': [
|
||||
'<(PRODUCT_DIR)/Atom-Shell.breakpad.syms',
|
||||
],
|
||||
'action': [
|
||||
'tools/mac/generate_breakpad_symbols.py',
|
||||
'--build-dir=<(PRODUCT_DIR)',
|
||||
'--binary=<(PRODUCT_DIR)/<(product_name).app/Contents/MacOS/<(product_name)',
|
||||
'--symbols-dir=<(PRODUCT_DIR)/Atom-Shell.breakpad.syms',
|
||||
'--clear',
|
||||
'--jobs=16',
|
||||
],
|
||||
},
|
||||
],
|
||||
}], # OS=="mac"
|
||||
['OS=="win"', {
|
||||
'actions': [
|
||||
{
|
||||
'action_name': 'Dump Symbols',
|
||||
'inputs': [
|
||||
'<(PRODUCT_DIR)/<(project_name).exe',
|
||||
],
|
||||
'outputs': [
|
||||
'<(PRODUCT_DIR)/Atom-Shell.breakpad.syms',
|
||||
],
|
||||
'action': [
|
||||
'tools/win/generate_breakpad_symbols.py',
|
||||
'--symbols-dir=<(PRODUCT_DIR)/Atom-Shell.breakpad.syms',
|
||||
'--jobs=16',
|
||||
'<(PRODUCT_DIR)',
|
||||
'<(libchromiumcontent_library_dir)',
|
||||
],
|
||||
},
|
||||
],
|
||||
}], # OS=="win"
|
||||
],
|
||||
}, # target <(project_name>_dump_symbols
|
||||
],
|
||||
'conditions': [
|
||||
['OS=="mac"', {
|
||||
'targets': [
|
||||
{
|
||||
'target_name': '<(project_name)_framework',
|
||||
'product_name': '<(product_name)',
|
||||
'product_name': '<(framework_name)',
|
||||
'type': 'shared_library',
|
||||
'dependencies': [
|
||||
'<(project_name)_lib',
|
||||
|
@ -409,7 +476,6 @@
|
|||
'libraries': [
|
||||
'$(SDKROOT)/System/Library/Frameworks/Carbon.framework',
|
||||
'frameworks/Sparkle.framework',
|
||||
'frameworks/Quincy.framework',
|
||||
],
|
||||
},
|
||||
'mac_bundle': 1,
|
||||
|
@ -421,7 +487,7 @@
|
|||
'LIBRARY_SEARCH_PATHS': [
|
||||
'<(libchromiumcontent_library_dir)',
|
||||
],
|
||||
'LD_DYLIB_INSTALL_NAME': '@rpath/<(product_name).framework/<(product_name)',
|
||||
'LD_DYLIB_INSTALL_NAME': '@rpath/<(framework_name).framework/<(framework_name)',
|
||||
'LD_RUNPATH_SEARCH_PATHS': [
|
||||
'@loader_path/Libraries',
|
||||
],
|
||||
|
@ -431,12 +497,19 @@
|
|||
},
|
||||
'copies': [
|
||||
{
|
||||
'destination': '<(PRODUCT_DIR)/<(product_name).framework/Versions/A/Libraries',
|
||||
'destination': '<(PRODUCT_DIR)/<(framework_name).framework/Versions/A/Libraries',
|
||||
'files': [
|
||||
'<(libchromiumcontent_library_dir)/ffmpegsumo.so',
|
||||
'<(libchromiumcontent_library_dir)/libchromiumcontent.dylib',
|
||||
],
|
||||
},
|
||||
{
|
||||
'destination': '<(PRODUCT_DIR)/<(framework_name).framework/Versions/A/Resources',
|
||||
'files': [
|
||||
'<(PRODUCT_DIR)/Inspector',
|
||||
'<(PRODUCT_DIR)/crash_report_sender.app',
|
||||
],
|
||||
},
|
||||
],
|
||||
'postbuilds': [
|
||||
{
|
||||
|
@ -449,7 +522,7 @@
|
|||
'postbuild_name': 'Add symlinks for framework subdirectories',
|
||||
'action': [
|
||||
'tools/mac/create-framework-subdir-symlinks.sh',
|
||||
'<(product_name)',
|
||||
'<(framework_name)',
|
||||
'Libraries',
|
||||
'Frameworks',
|
||||
],
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "browser/api/atom_api_crash_reporter.h"
|
||||
|
||||
#include "browser/crash_reporter.h"
|
||||
#include "common/v8_conversions.h"
|
||||
#include "vendor/node/src/node.h"
|
||||
#include "vendor/node/src/node_internals.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace api {
|
||||
|
||||
// static
|
||||
v8::Handle<v8::Value> CrashReporter::SetCompanyName(const v8::Arguments &args) {
|
||||
crash_reporter::CrashReporter::SetCompanyName(FromV8Value(args[0]));
|
||||
return v8::Undefined();
|
||||
}
|
||||
|
||||
// static
|
||||
v8::Handle<v8::Value> CrashReporter::SetSubmissionURL(
|
||||
const v8::Arguments &args) {
|
||||
crash_reporter::CrashReporter::SetSubmissionURL(FromV8Value(args[0]));
|
||||
return v8::Undefined();
|
||||
}
|
||||
|
||||
// static
|
||||
v8::Handle<v8::Value> CrashReporter::SetAutoSubmit(const v8::Arguments &args) {
|
||||
crash_reporter::CrashReporter::SetAutoSubmit(FromV8Value(args[0]));
|
||||
return v8::Undefined();
|
||||
}
|
||||
|
||||
// static
|
||||
void CrashReporter::Initialize(v8::Handle<v8::Object> target) {
|
||||
node::SetMethod(target, "setCompanyName", SetCompanyName);
|
||||
node::SetMethod(target, "setSubmissionUrl", SetSubmissionURL);
|
||||
node::SetMethod(target, "setAutoSubmit", SetAutoSubmit);
|
||||
}
|
||||
|
||||
} // namespace api
|
||||
|
||||
} // namespace atom
|
||||
|
||||
NODE_MODULE(atom_browser_crash_reporter, atom::api::CrashReporter::Initialize)
|
|
@ -1 +0,0 @@
|
|||
module.exports = process.atomBinding 'crash_reporter'
|
|
@ -20,7 +20,9 @@ void AtomBrowserMainParts::PreMainMessageLoopStart() {
|
|||
[NSApp setDelegate:delegate];
|
||||
|
||||
base::FilePath frameworkPath = brightray::MainApplicationBundlePath()
|
||||
.Append("Contents").Append("Frameworks").Append("Atom.framework");
|
||||
.Append("Contents")
|
||||
.Append("Frameworks")
|
||||
.Append("Atom Framework.framework");
|
||||
NSBundle* frameworkBundle = [NSBundle
|
||||
bundleWithPath:base::mac::FilePathToNSString(frameworkPath)];
|
||||
NSNib* mainNib = [[NSNib alloc] initWithNibNamed:@"MainMenu"
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_CRASH_REPORTER_H_
|
||||
#define ATOM_BROWSER_CRASH_REPORTER_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/basictypes.h"
|
||||
|
||||
namespace crash_reporter {
|
||||
|
||||
class CrashReporter {
|
||||
public:
|
||||
static void SetCompanyName(const std::string& name);
|
||||
static void SetSubmissionURL(const std::string& url);
|
||||
static void SetAutoSubmit(bool yes);
|
||||
|
||||
private:
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(CrashReporter);
|
||||
};
|
||||
|
||||
} // namespace crash_reporter
|
||||
|
||||
#endif // ATOM_BROWSER_CRASH_REPORTER_H_
|
|
@ -1,31 +0,0 @@
|
|||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "browser/crash_reporter.h"
|
||||
|
||||
#import <Quincy/BWQuincyManager.h>
|
||||
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
|
||||
namespace crash_reporter {
|
||||
|
||||
// static
|
||||
void CrashReporter::SetCompanyName(const std::string& name) {
|
||||
BWQuincyManager *manager = [BWQuincyManager sharedQuincyManager];
|
||||
[manager setCompanyName:base::SysUTF8ToNSString(name)];
|
||||
}
|
||||
|
||||
// static
|
||||
void CrashReporter::SetSubmissionURL(const std::string& url) {
|
||||
BWQuincyManager *manager = [BWQuincyManager sharedQuincyManager];
|
||||
[manager setSubmissionURL:base::SysUTF8ToNSString(url)];
|
||||
}
|
||||
|
||||
// static
|
||||
void CrashReporter::SetAutoSubmit(bool yes) {
|
||||
BWQuincyManager *manager = [BWQuincyManager sharedQuincyManager];
|
||||
[manager setAutoSubmitCrashReport:yes];
|
||||
}
|
||||
|
||||
} // namespace crash_reporter
|
|
@ -1,22 +0,0 @@
|
|||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "browser/crash_reporter.h"
|
||||
|
||||
namespace crash_reporter {
|
||||
|
||||
// static
|
||||
void CrashReporter::SetCompanyName(const std::string& name) {
|
||||
}
|
||||
|
||||
// static
|
||||
void CrashReporter::SetSubmissionURL(const std::string& url) {
|
||||
}
|
||||
|
||||
// static
|
||||
void CrashReporter::SetAutoSubmit(bool yes) {
|
||||
}
|
||||
|
||||
} // namespace crash_reporter
|
||||
|
35
common.gypi
35
common.gypi
|
@ -59,7 +59,17 @@
|
|||
},
|
||||
},
|
||||
'xcode_settings': {
|
||||
'GCC_TREAT_WARNINGS_AS_ERRORS': 'NO'
|
||||
'GCC_TREAT_WARNINGS_AS_ERRORS': 'NO',
|
||||
'WARNING_CFLAGS': [
|
||||
'-Wno-parentheses-equality',
|
||||
'-Wno-unused-function',
|
||||
'-Wno-sometimes-uninitialized',
|
||||
'-Wno-pointer-sign',
|
||||
'-Wno-string-plus-int',
|
||||
'-Wno-unused-variable',
|
||||
'-Wno-deprecated-declarations',
|
||||
'-Wno-return-type',
|
||||
],
|
||||
},
|
||||
}],
|
||||
['_target_name=="libuv"', {
|
||||
|
@ -72,6 +82,15 @@
|
|||
}], # OS=="win"
|
||||
],
|
||||
}],
|
||||
['_target_name.startswith("breakpad") or _target_name in ["crash_report_sender", "dump_syms"]', {
|
||||
'xcode_settings': {
|
||||
'WARNING_CFLAGS': [
|
||||
'-Wno-deprecated-declarations',
|
||||
'-Wno-unused-private-field',
|
||||
'-Wno-unused-function',
|
||||
],
|
||||
},
|
||||
}],
|
||||
],
|
||||
'msvs_cygwin_shell': 0, # Strangely setting it to 1 would make building under cygwin fail.
|
||||
'msvs_disabled_warnings': [
|
||||
|
@ -107,6 +126,9 @@
|
|||
],
|
||||
},
|
||||
},
|
||||
'xcode_settings': {
|
||||
'DEBUG_INFORMATION_FORMAT': 'dwarf-with-dsym',
|
||||
},
|
||||
},
|
||||
'conditions': [
|
||||
# Settings to compile with clang under OS X.
|
||||
|
@ -164,5 +186,16 @@
|
|||
],
|
||||
},
|
||||
}], # msvs_express==1
|
||||
# The breakdpad on Windows assumes Debug_x64 and Release_x64 configurations.
|
||||
['OS=="win"', {
|
||||
'target_defaults': {
|
||||
'configurations': {
|
||||
'Debug_x64': {
|
||||
},
|
||||
'Release_x64': {
|
||||
},
|
||||
},
|
||||
},
|
||||
}], # OS=="win"
|
||||
],
|
||||
}
|
||||
|
|
40
common/api/atom_api_crash_reporter.cc
Normal file
40
common/api/atom_api_crash_reporter.cc
Normal file
|
@ -0,0 +1,40 @@
|
|||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "common/api/atom_api_crash_reporter.h"
|
||||
|
||||
#include "common/crash_reporter/crash_reporter.h"
|
||||
#include "common/v8_conversions.h"
|
||||
#include "vendor/node/src/node.h"
|
||||
#include "vendor/node/src/node_internals.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace api {
|
||||
|
||||
// static
|
||||
v8::Handle<v8::Value> CrashReporter::Start(const v8::Arguments& args) {
|
||||
std::string product_name, company_name, submit_url;
|
||||
bool auto_submit, skip_system;
|
||||
std::map<std::string, std::string> dict;
|
||||
if (!FromV8Arguments(args, &product_name, &company_name, &submit_url,
|
||||
&auto_submit, &skip_system, &dict))
|
||||
return node::ThrowTypeError("Bad argument");
|
||||
|
||||
crash_reporter::CrashReporter::GetInstance()->Start(
|
||||
product_name, company_name, submit_url, auto_submit, skip_system, dict);
|
||||
|
||||
return v8::Undefined();
|
||||
}
|
||||
|
||||
// static
|
||||
void CrashReporter::Initialize(v8::Handle<v8::Object> target) {
|
||||
node::SetMethod(target, "start", Start);
|
||||
}
|
||||
|
||||
} // namespace api
|
||||
|
||||
} // namespace atom
|
||||
|
||||
NODE_MODULE(atom_common_crash_reporter, atom::api::CrashReporter::Initialize)
|
|
@ -2,8 +2,8 @@
|
|||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_API_ATOM_API_CRASH_REPORTER_H_
|
||||
#define ATOM_BROWSER_API_ATOM_API_CRASH_REPORTER_H_
|
||||
#ifndef ATOM_COMMON_API_ATOM_API_CRASH_REPORTER_H_
|
||||
#define ATOM_COMMON_API_ATOM_API_CRASH_REPORTER_H_
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "v8/include/v8.h"
|
||||
|
@ -17,9 +17,7 @@ class CrashReporter {
|
|||
static void Initialize(v8::Handle<v8::Object> target);
|
||||
|
||||
private:
|
||||
static v8::Handle<v8::Value> SetCompanyName(const v8::Arguments &args);
|
||||
static v8::Handle<v8::Value> SetSubmissionURL(const v8::Arguments &args);
|
||||
static v8::Handle<v8::Value> SetAutoSubmit(const v8::Arguments &args);
|
||||
static v8::Handle<v8::Value> Start(const v8::Arguments& args);
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(CrashReporter);
|
||||
};
|
||||
|
@ -28,4 +26,4 @@ class CrashReporter {
|
|||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_API_ATOM_API_CRASH_REPORTER_H_
|
||||
#endif // ATOM_COMMON_API_ATOM_API_CRASH_REPORTER_H_
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
#include "common/api/atom_bindings.h"
|
||||
|
||||
#include "base/debug/debugger.h"
|
||||
#include "base/logging.h"
|
||||
#include "common/atom_version.h"
|
||||
#include "common/v8_conversions.h"
|
||||
|
@ -18,6 +17,8 @@ static int kMaxCallStackSize = 200; // Same with WebKit.
|
|||
|
||||
static uv_async_t dummy_uv_handle;
|
||||
|
||||
struct DummyClass { bool crash; };
|
||||
|
||||
void UvNoOp(uv_async_t* handle, int status) {
|
||||
}
|
||||
|
||||
|
@ -110,7 +111,7 @@ v8::Handle<v8::Value> AtomBindings::Binding(const v8::Arguments& args) {
|
|||
|
||||
// static
|
||||
v8::Handle<v8::Value> AtomBindings::Crash(const v8::Arguments& args) {
|
||||
base::debug::BreakDebugger();
|
||||
static_cast<DummyClass*>(NULL)->crash = true;
|
||||
return v8::Undefined();
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@ NODE_EXT_LIST_START
|
|||
// Module names start with `atom_browser_` can only be used by browser process.
|
||||
NODE_EXT_LIST_ITEM(atom_browser_app)
|
||||
NODE_EXT_LIST_ITEM(atom_browser_auto_updater)
|
||||
NODE_EXT_LIST_ITEM(atom_browser_crash_reporter)
|
||||
NODE_EXT_LIST_ITEM(atom_browser_dialog)
|
||||
NODE_EXT_LIST_ITEM(atom_browser_ipc)
|
||||
NODE_EXT_LIST_ITEM(atom_browser_menu)
|
||||
|
@ -26,6 +25,7 @@ NODE_EXT_LIST_ITEM(atom_renderer_ipc)
|
|||
// Module names start with `atom_common_` can be used by both browser and
|
||||
// renderer processes.
|
||||
NODE_EXT_LIST_ITEM(atom_common_clipboard)
|
||||
NODE_EXT_LIST_ITEM(atom_common_crash_reporter)
|
||||
NODE_EXT_LIST_ITEM(atom_common_id_weak_map)
|
||||
NODE_EXT_LIST_ITEM(atom_common_shell)
|
||||
NODE_EXT_LIST_ITEM(atom_common_v8_util)
|
||||
|
|
38
common/api/lib/crash-reporter.coffee
Normal file
38
common/api/lib/crash-reporter.coffee
Normal file
|
@ -0,0 +1,38 @@
|
|||
{spawn} = require 'child_process'
|
||||
binding = process.atomBinding 'crash_reporter'
|
||||
|
||||
class CrashReporter
|
||||
start: (options={}) ->
|
||||
{productName, companyName, submitUrl, autoSubmit, ignoreSystemCrashHandler, extra} = options
|
||||
|
||||
productName ?= 'Atom-Shell'
|
||||
companyName ?= 'GitHub, Inc'
|
||||
submitUrl ?= 'http://54.249.141.25:1127/post'
|
||||
autoSubmit ?= true
|
||||
ignoreSystemCrashHandler ?= false
|
||||
extra ?= {}
|
||||
|
||||
extra._productName ?= productName
|
||||
extra._companyName ?= companyName
|
||||
extra._version ?=
|
||||
if process.__atom_type is 'browser'
|
||||
require('app').getVersion()
|
||||
else
|
||||
require('remote').require('app').getVersion()
|
||||
|
||||
start = -> binding.start productName, companyName, submitUrl, autoSubmit, ignoreSystemCrashHandler, extra
|
||||
|
||||
if process.platform is 'darwin'
|
||||
start()
|
||||
else
|
||||
args = [
|
||||
"--reporter-url=#{submitUrl}"
|
||||
"--application-name=#{productName}"
|
||||
"--v=1"
|
||||
]
|
||||
env = ATOM_SHELL_INTERNAL_CRASH_SERVICE: 1
|
||||
|
||||
spawn process.execPath, args, {env, detached: true}
|
||||
start()
|
||||
|
||||
module.exports = new CrashReporter
|
42
common/crash_reporter/crash_reporter.cc
Normal file
42
common/crash_reporter/crash_reporter.cc
Normal file
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "common/crash_reporter/crash_reporter.h"
|
||||
|
||||
#include "base/command_line.h"
|
||||
#include "browser/browser.h"
|
||||
#include "common/atom_version.h"
|
||||
#include "content/public/common/content_switches.h"
|
||||
|
||||
namespace crash_reporter {
|
||||
|
||||
CrashReporter::CrashReporter() {
|
||||
const CommandLine& command = *CommandLine::ForCurrentProcess();
|
||||
is_browser_ = command.GetSwitchValueASCII(switches::kProcessType).empty();
|
||||
}
|
||||
|
||||
CrashReporter::~CrashReporter() {
|
||||
}
|
||||
|
||||
void CrashReporter::Start(const std::string& product_name,
|
||||
const std::string& company_name,
|
||||
const std::string& submit_url,
|
||||
bool auto_submit,
|
||||
bool skip_system_crash_handler,
|
||||
const StringMap& extra_parameters) {
|
||||
SetUploadParameters(extra_parameters);
|
||||
|
||||
InitBreakpad(product_name, ATOM_VERSION_STRING, company_name, submit_url,
|
||||
auto_submit, skip_system_crash_handler);
|
||||
}
|
||||
|
||||
void CrashReporter::SetUploadParameters(const StringMap& parameters) {
|
||||
upload_parameters_ = parameters;
|
||||
upload_parameters_["process_type"] = is_browser_ ? "browser" : "renderer";
|
||||
|
||||
// Setting platform dependent parameters.
|
||||
SetUploadParameters();
|
||||
}
|
||||
|
||||
} // namespace crash_reporter
|
51
common/crash_reporter/crash_reporter.h
Normal file
51
common/crash_reporter/crash_reporter.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_H_
|
||||
#define ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "base/basictypes.h"
|
||||
|
||||
namespace crash_reporter {
|
||||
|
||||
class CrashReporter {
|
||||
public:
|
||||
typedef std::map<std::string, std::string> StringMap;
|
||||
|
||||
static CrashReporter* GetInstance();
|
||||
|
||||
void Start(const std::string& product_name,
|
||||
const std::string& company_name,
|
||||
const std::string& submit_url,
|
||||
bool auto_submit,
|
||||
bool skip_system_crash_handler,
|
||||
const StringMap& extra_parameters);
|
||||
|
||||
protected:
|
||||
CrashReporter();
|
||||
virtual ~CrashReporter();
|
||||
|
||||
virtual void InitBreakpad(const std::string& product_name,
|
||||
const std::string& version,
|
||||
const std::string& company_name,
|
||||
const std::string& submit_url,
|
||||
bool auto_submit,
|
||||
bool skip_system_crash_handler) = 0;
|
||||
virtual void SetUploadParameters() = 0;
|
||||
|
||||
StringMap upload_parameters_;
|
||||
bool is_browser_;
|
||||
|
||||
private:
|
||||
void SetUploadParameters(const StringMap& parameters);
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CrashReporter);
|
||||
};
|
||||
|
||||
} // namespace crash_reporter
|
||||
|
||||
#endif // ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_H_
|
41
common/crash_reporter/crash_reporter_mac.h
Normal file
41
common/crash_reporter/crash_reporter_mac.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_MAC_H_
|
||||
#define ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_MAC_H_
|
||||
|
||||
#include "base/compiler_specific.h"
|
||||
#include "common/crash_reporter/crash_reporter.h"
|
||||
#import "vendor/breakpad/src/client/mac/Framework/Breakpad.h"
|
||||
|
||||
template <typename T> struct DefaultSingletonTraits;
|
||||
|
||||
namespace crash_reporter {
|
||||
|
||||
class CrashReporterMac : public CrashReporter {
|
||||
public:
|
||||
static CrashReporterMac* GetInstance();
|
||||
|
||||
virtual void InitBreakpad(const std::string& product_name,
|
||||
const std::string& version,
|
||||
const std::string& company_name,
|
||||
const std::string& submit_url,
|
||||
bool auto_submit,
|
||||
bool skip_system_crash_handler) OVERRIDE;
|
||||
virtual void SetUploadParameters() OVERRIDE;
|
||||
|
||||
private:
|
||||
friend struct DefaultSingletonTraits<CrashReporterMac>;
|
||||
|
||||
CrashReporterMac();
|
||||
virtual ~CrashReporterMac();
|
||||
|
||||
BreakpadRef breakpad_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CrashReporterMac);
|
||||
};
|
||||
|
||||
} // namespace crash_reporter
|
||||
|
||||
#endif // ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_MAC_H_
|
81
common/crash_reporter/crash_reporter_mac.mm
Normal file
81
common/crash_reporter/crash_reporter_mac.mm
Normal file
|
@ -0,0 +1,81 @@
|
|||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "common/crash_reporter/crash_reporter_mac.h"
|
||||
|
||||
#include "base/memory/singleton.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#import "vendor/breakpad/src/client/apple/Framework/BreakpadDefines.h"
|
||||
|
||||
namespace crash_reporter {
|
||||
|
||||
CrashReporterMac::CrashReporterMac()
|
||||
: breakpad_(NULL) {
|
||||
}
|
||||
|
||||
CrashReporterMac::~CrashReporterMac() {
|
||||
if (breakpad_ != NULL)
|
||||
BreakpadRelease(breakpad_);
|
||||
}
|
||||
|
||||
void CrashReporterMac::InitBreakpad(const std::string& product_name,
|
||||
const std::string& version,
|
||||
const std::string& company_name,
|
||||
const std::string& submit_url,
|
||||
bool auto_submit,
|
||||
bool skip_system_crash_handler) {
|
||||
if (breakpad_ != NULL)
|
||||
BreakpadRelease(breakpad_);
|
||||
|
||||
NSMutableDictionary* parameters =
|
||||
[NSMutableDictionary dictionaryWithCapacity:4];
|
||||
|
||||
[parameters setValue:@"Atom-Shell"
|
||||
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];
|
||||
|
||||
breakpad_ = BreakpadCreate(parameters);
|
||||
|
||||
for (StringMap::const_iterator iter = upload_parameters_.begin();
|
||||
iter != upload_parameters_.end(); ++iter) {
|
||||
BreakpadAddUploadParameter(breakpad_,
|
||||
base::SysUTF8ToNSString(iter->first),
|
||||
base::SysUTF8ToNSString(iter->second));
|
||||
}
|
||||
}
|
||||
|
||||
void CrashReporterMac::SetUploadParameters() {
|
||||
upload_parameters_["platform"] = "darwin";
|
||||
}
|
||||
|
||||
// static
|
||||
CrashReporterMac* CrashReporterMac::GetInstance() {
|
||||
return Singleton<CrashReporterMac>::get();
|
||||
}
|
||||
|
||||
// static
|
||||
CrashReporter* CrashReporter::GetInstance() {
|
||||
return CrashReporterMac::GetInstance();
|
||||
}
|
||||
|
||||
} // namespace crash_reporter
|
129
common/crash_reporter/crash_reporter_win.cc
Normal file
129
common/crash_reporter/crash_reporter_win.cc
Normal file
|
@ -0,0 +1,129 @@
|
|||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "common/crash_reporter/crash_reporter_win.h"
|
||||
|
||||
#include "base/file_util.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/memory/singleton.h"
|
||||
#include "base/string_util.h"
|
||||
#include "base/utf_string_conversions.h"
|
||||
|
||||
namespace crash_reporter {
|
||||
|
||||
namespace {
|
||||
|
||||
// Minidump with stacks, PEB, TEB, and unloaded module list.
|
||||
const MINIDUMP_TYPE kSmallDumpType = static_cast<MINIDUMP_TYPE>(
|
||||
MiniDumpWithProcessThreadData | // Get PEB and TEB.
|
||||
MiniDumpWithUnloadedModules); // Get unloaded modules when available.
|
||||
|
||||
const wchar_t kPipeNameFormat[] = L"\\\\.\\pipe\\$1 Crash Service";
|
||||
|
||||
} // namespace
|
||||
|
||||
CrashReporterWin::CrashReporterWin() {
|
||||
}
|
||||
|
||||
CrashReporterWin::~CrashReporterWin() {
|
||||
}
|
||||
|
||||
void CrashReporterWin::InitBreakpad(const std::string& product_name,
|
||||
const std::string& version,
|
||||
const std::string& company_name,
|
||||
const std::string& submit_url,
|
||||
bool auto_submit,
|
||||
bool skip_system_crash_handler) {
|
||||
skip_system_crash_handler_ = skip_system_crash_handler;
|
||||
|
||||
base::FilePath temp_dir;
|
||||
if (!file_util::GetTempDir(&temp_dir)) {
|
||||
LOG(ERROR) << "Cannot get temp directory";
|
||||
return;
|
||||
}
|
||||
|
||||
string16 pipe_name = ReplaceStringPlaceholders(kPipeNameFormat,
|
||||
UTF8ToUTF16(product_name),
|
||||
NULL);
|
||||
|
||||
// Wait until the crash service is started.
|
||||
HANDLE waiting_event =
|
||||
::CreateEventW(NULL, TRUE, FALSE, L"g_atom_shell_crash_service");
|
||||
if (waiting_event != INVALID_HANDLE_VALUE)
|
||||
WaitForSingleObject(waiting_event, 1000);
|
||||
|
||||
breakpad_.reset(new google_breakpad::ExceptionHandler(
|
||||
temp_dir.value(),
|
||||
FilterCallback,
|
||||
MinidumpCallback,
|
||||
this,
|
||||
google_breakpad::ExceptionHandler::HANDLER_ALL,
|
||||
kSmallDumpType,
|
||||
pipe_name.c_str(),
|
||||
GetCustomInfo(product_name, version, company_name)));
|
||||
|
||||
if (!breakpad_->IsOutOfProcess())
|
||||
LOG(ERROR) << "Cannot initialize out-of-process crash handler";
|
||||
}
|
||||
|
||||
void CrashReporterWin::SetUploadParameters() {
|
||||
upload_parameters_["platform"] = "win32";
|
||||
}
|
||||
|
||||
// static
|
||||
bool CrashReporterWin::FilterCallback(void* context,
|
||||
EXCEPTION_POINTERS* exinfo,
|
||||
MDRawAssertionInfo* assertion) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool CrashReporterWin::MinidumpCallback(const wchar_t* dump_path,
|
||||
const wchar_t* minidump_id,
|
||||
void* context,
|
||||
EXCEPTION_POINTERS* exinfo,
|
||||
MDRawAssertionInfo* assertion,
|
||||
bool succeeded) {
|
||||
CrashReporterWin* self = static_cast<CrashReporterWin*>(context);
|
||||
if (succeeded && !self->skip_system_crash_handler_)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
google_breakpad::CustomClientInfo* CrashReporterWin::GetCustomInfo(
|
||||
const std::string& product_name,
|
||||
const std::string& version,
|
||||
const std::string& company_name) {
|
||||
custom_info_entries_.clear();
|
||||
custom_info_entries_.reserve(2 + upload_parameters_.size());
|
||||
|
||||
custom_info_entries_.push_back(google_breakpad::CustomInfoEntry(
|
||||
L"prod", L"Atom-Shell"));
|
||||
custom_info_entries_.push_back(google_breakpad::CustomInfoEntry(
|
||||
L"ver", UTF8ToWide(version).c_str()));
|
||||
|
||||
for (StringMap::const_iterator iter = upload_parameters_.begin();
|
||||
iter != upload_parameters_.end(); ++iter) {
|
||||
custom_info_entries_.push_back(google_breakpad::CustomInfoEntry(
|
||||
UTF8ToWide(iter->first).c_str(),
|
||||
UTF8ToWide(iter->second).c_str()));
|
||||
}
|
||||
|
||||
custom_info_.entries = &custom_info_entries_.front();
|
||||
custom_info_.count = custom_info_entries_.size();
|
||||
return &custom_info_;
|
||||
}
|
||||
|
||||
// static
|
||||
CrashReporterWin* CrashReporterWin::GetInstance() {
|
||||
return Singleton<CrashReporterWin>::get();
|
||||
}
|
||||
|
||||
// static
|
||||
CrashReporter* CrashReporter::GetInstance() {
|
||||
return CrashReporterWin::GetInstance();
|
||||
}
|
||||
|
||||
} // namespace crash_reporter
|
64
common/crash_reporter/crash_reporter_win.h
Normal file
64
common/crash_reporter/crash_reporter_win.h
Normal file
|
@ -0,0 +1,64 @@
|
|||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_WIN_H_
|
||||
#define ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_WIN_H_
|
||||
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "common/crash_reporter/crash_reporter.h"
|
||||
#include "vendor/breakpad/src/client/windows/handler/exception_handler.h"
|
||||
|
||||
template <typename T> struct DefaultSingletonTraits;
|
||||
|
||||
namespace crash_reporter {
|
||||
|
||||
class CrashReporterWin : public CrashReporter {
|
||||
public:
|
||||
static CrashReporterWin* GetInstance();
|
||||
|
||||
virtual void InitBreakpad(const std::string& product_name,
|
||||
const std::string& version,
|
||||
const std::string& company_name,
|
||||
const std::string& submit_url,
|
||||
bool auto_submit,
|
||||
bool skip_system_crash_handler) OVERRIDE;
|
||||
virtual void SetUploadParameters() OVERRIDE;
|
||||
|
||||
private:
|
||||
friend struct DefaultSingletonTraits<CrashReporterWin>;
|
||||
|
||||
CrashReporterWin();
|
||||
virtual ~CrashReporterWin();
|
||||
|
||||
static bool FilterCallback(void* context,
|
||||
EXCEPTION_POINTERS* exinfo,
|
||||
MDRawAssertionInfo* assertion);
|
||||
|
||||
static bool MinidumpCallback(const wchar_t* dump_path,
|
||||
const wchar_t* minidump_id,
|
||||
void* context,
|
||||
EXCEPTION_POINTERS* exinfo,
|
||||
MDRawAssertionInfo* assertion,
|
||||
bool succeeded);
|
||||
|
||||
// Returns the custom info structure based on parameters.
|
||||
google_breakpad::CustomClientInfo* GetCustomInfo(
|
||||
const std::string& product_name,
|
||||
const std::string& version,
|
||||
const std::string& company_name);
|
||||
|
||||
// Custom information to be passed to crash handler.
|
||||
std::vector<google_breakpad::CustomInfoEntry> custom_info_entries_;
|
||||
google_breakpad::CustomClientInfo custom_info_;
|
||||
|
||||
bool skip_system_crash_handler_;
|
||||
scoped_ptr<google_breakpad::ExceptionHandler> breakpad_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CrashReporterWin);
|
||||
};
|
||||
|
||||
} // namespace crash_reporter
|
||||
|
||||
#endif // ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_WIN_H_
|
490
common/crash_reporter/win/crash_service.cc
Normal file
490
common/crash_reporter/win/crash_service.cc
Normal file
|
@ -0,0 +1,490 @@
|
|||
// Copyright (c) 2012 The Chromium 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 "common/crash_reporter/win/crash_service.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <sddl.h>
|
||||
#include <fstream> // NOLINT
|
||||
#include <map>
|
||||
|
||||
#include "base/command_line.h"
|
||||
#include "base/file_util.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/win/windows_version.h"
|
||||
#include "vendor/breakpad/src/client/windows/crash_generation/client_info.h"
|
||||
#include "vendor/breakpad/src/client/windows/crash_generation/crash_generation_server.h"
|
||||
#include "vendor/breakpad/src/client/windows/sender/crash_report_sender.h"
|
||||
|
||||
namespace breakpad {
|
||||
|
||||
namespace {
|
||||
|
||||
const wchar_t kTestPipeName[] = L"\\\\.\\pipe\\ChromeCrashServices";
|
||||
|
||||
const wchar_t kGoogleReportURL[] = L"https://clients2.google.com/cr/report";
|
||||
const wchar_t kCheckPointFile[] = L"crash_checkpoint.txt";
|
||||
|
||||
typedef std::map<std::wstring, std::wstring> CrashMap;
|
||||
|
||||
bool CustomInfoToMap(const google_breakpad::ClientInfo* client_info,
|
||||
const std::wstring& reporter_tag, CrashMap* map) {
|
||||
google_breakpad::CustomClientInfo info = client_info->GetCustomInfo();
|
||||
|
||||
for (uintptr_t i = 0; i < info.count; ++i) {
|
||||
(*map)[info.entries[i].name] = info.entries[i].value;
|
||||
}
|
||||
|
||||
(*map)[L"rept"] = reporter_tag;
|
||||
|
||||
return !map->empty();
|
||||
}
|
||||
|
||||
bool WriteCustomInfoToFile(const std::wstring& dump_path, const CrashMap& map) {
|
||||
std::wstring file_path(dump_path);
|
||||
size_t last_dot = file_path.rfind(L'.');
|
||||
if (last_dot == std::wstring::npos)
|
||||
return false;
|
||||
file_path.resize(last_dot);
|
||||
file_path += L".txt";
|
||||
|
||||
std::wofstream file(file_path.c_str(),
|
||||
std::ios_base::out | std::ios_base::app | std::ios::binary);
|
||||
if (!file.is_open())
|
||||
return false;
|
||||
|
||||
CrashMap::const_iterator pos;
|
||||
for (pos = map.begin(); pos != map.end(); ++pos) {
|
||||
std::wstring line = pos->first;
|
||||
line += L':';
|
||||
line += pos->second;
|
||||
line += L'\n';
|
||||
file.write(line.c_str(), static_cast<std::streamsize>(line.length()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// The window procedure task is to handle when a) the user logs off.
|
||||
// b) the system shuts down or c) when the user closes the window.
|
||||
LRESULT __stdcall CrashSvcWndProc(HWND hwnd, UINT message,
|
||||
WPARAM wparam, LPARAM lparam) {
|
||||
switch (message) {
|
||||
case WM_CLOSE:
|
||||
case WM_ENDSESSION:
|
||||
case WM_DESTROY:
|
||||
PostQuitMessage(0);
|
||||
break;
|
||||
default:
|
||||
return DefWindowProc(hwnd, message, wparam, lparam);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// This is the main and only application window.
|
||||
HWND g_top_window = NULL;
|
||||
|
||||
bool CreateTopWindow(HINSTANCE instance, bool visible) {
|
||||
WNDCLASSEXW wcx = {0};
|
||||
wcx.cbSize = sizeof(wcx);
|
||||
wcx.style = CS_HREDRAW | CS_VREDRAW;
|
||||
wcx.lpfnWndProc = CrashSvcWndProc;
|
||||
wcx.hInstance = instance;
|
||||
wcx.lpszClassName = L"crash_svc_class";
|
||||
ATOM atom = ::RegisterClassExW(&wcx);
|
||||
DWORD style = visible ? WS_POPUPWINDOW | WS_VISIBLE : WS_OVERLAPPED;
|
||||
|
||||
// The window size is zero but being a popup window still shows in the
|
||||
// task bar and can be closed using the system menu or using task manager.
|
||||
HWND window = CreateWindowExW(0, wcx.lpszClassName, L"crash service", style,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT, 0, 0,
|
||||
NULL, NULL, instance, NULL);
|
||||
if (!window)
|
||||
return false;
|
||||
|
||||
::UpdateWindow(window);
|
||||
VLOG(1) << "window handle is " << window;
|
||||
g_top_window = window;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Simple helper class to keep the process alive until the current request
|
||||
// finishes.
|
||||
class ProcessingLock {
|
||||
public:
|
||||
ProcessingLock() {
|
||||
::InterlockedIncrement(&op_count_);
|
||||
}
|
||||
~ProcessingLock() {
|
||||
::InterlockedDecrement(&op_count_);
|
||||
}
|
||||
static bool IsWorking() {
|
||||
return (op_count_ != 0);
|
||||
}
|
||||
private:
|
||||
static volatile LONG op_count_;
|
||||
};
|
||||
|
||||
volatile LONG ProcessingLock::op_count_ = 0;
|
||||
|
||||
// This structure contains the information that the worker thread needs to
|
||||
// send a crash dump to the server.
|
||||
struct DumpJobInfo {
|
||||
DWORD pid;
|
||||
CrashService* self;
|
||||
CrashMap map;
|
||||
std::wstring dump_path;
|
||||
|
||||
DumpJobInfo(DWORD process_id, CrashService* service,
|
||||
const CrashMap& crash_map, const std::wstring& path)
|
||||
: pid(process_id), self(service), map(crash_map), dump_path(path) {
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// Command line switches:
|
||||
const char CrashService::kMaxReports[] = "max-reports";
|
||||
const char CrashService::kNoWindow[] = "no-window";
|
||||
const char CrashService::kReporterTag[] = "reporter";
|
||||
const char CrashService::kDumpsDir[] = "dumps-dir";
|
||||
const char CrashService::kPipeName[] = "pipe-name";
|
||||
const char CrashService::kReporterURL[] = "reporter-url";
|
||||
|
||||
CrashService::CrashService()
|
||||
: sender_(NULL),
|
||||
dumper_(NULL),
|
||||
requests_handled_(0),
|
||||
requests_sent_(0),
|
||||
clients_connected_(0),
|
||||
clients_terminated_(0) {
|
||||
}
|
||||
|
||||
CrashService::~CrashService() {
|
||||
base::AutoLock lock(sending_);
|
||||
delete dumper_;
|
||||
delete sender_;
|
||||
}
|
||||
|
||||
bool CrashService::Initialize(const base::FilePath& operating_dir,
|
||||
const base::FilePath& dumps_path) {
|
||||
using google_breakpad::CrashReportSender;
|
||||
using google_breakpad::CrashGenerationServer;
|
||||
|
||||
std::wstring pipe_name = kTestPipeName;
|
||||
int max_reports = -1;
|
||||
|
||||
// The checkpoint file allows CrashReportSender to enforce the the maximum
|
||||
// reports per day quota. Does not seem to serve any other purpose.
|
||||
base::FilePath checkpoint_path = operating_dir.Append(kCheckPointFile);
|
||||
|
||||
CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
|
||||
|
||||
base::FilePath dumps_path_to_use = dumps_path;
|
||||
|
||||
if (cmd_line.HasSwitch(kDumpsDir)) {
|
||||
dumps_path_to_use =
|
||||
base::FilePath(cmd_line.GetSwitchValueNative(kDumpsDir));
|
||||
}
|
||||
|
||||
// We can override the send reports quota with a command line switch.
|
||||
if (cmd_line.HasSwitch(kMaxReports))
|
||||
max_reports = _wtoi(cmd_line.GetSwitchValueNative(kMaxReports).c_str());
|
||||
|
||||
// Allow the global pipe name to be overridden for better testability.
|
||||
if (cmd_line.HasSwitch(kPipeName))
|
||||
pipe_name = cmd_line.GetSwitchValueNative(kPipeName);
|
||||
|
||||
if (max_reports > 0) {
|
||||
// Create the http sender object.
|
||||
sender_ = new CrashReportSender(checkpoint_path.value());
|
||||
sender_->set_max_reports_per_day(max_reports);
|
||||
}
|
||||
|
||||
SECURITY_ATTRIBUTES security_attributes = {0};
|
||||
SECURITY_ATTRIBUTES* security_attributes_actual = NULL;
|
||||
|
||||
if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
|
||||
SECURITY_DESCRIPTOR* security_descriptor =
|
||||
reinterpret_cast<SECURITY_DESCRIPTOR*>(
|
||||
GetSecurityDescriptorForLowIntegrity());
|
||||
DCHECK(security_descriptor != NULL);
|
||||
|
||||
security_attributes.nLength = sizeof(security_attributes);
|
||||
security_attributes.lpSecurityDescriptor = security_descriptor;
|
||||
security_attributes.bInheritHandle = FALSE;
|
||||
|
||||
security_attributes_actual = &security_attributes;
|
||||
}
|
||||
|
||||
// Create the OOP crash generator object.
|
||||
dumper_ = new CrashGenerationServer(pipe_name, security_attributes_actual,
|
||||
&CrashService::OnClientConnected, this,
|
||||
&CrashService::OnClientDumpRequest, this,
|
||||
&CrashService::OnClientExited, this,
|
||||
NULL, NULL,
|
||||
true, &dumps_path_to_use.value());
|
||||
|
||||
if (!dumper_) {
|
||||
LOG(ERROR) << "could not create dumper";
|
||||
if (security_attributes.lpSecurityDescriptor)
|
||||
LocalFree(security_attributes.lpSecurityDescriptor);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CreateTopWindow(::GetModuleHandleW(NULL),
|
||||
!cmd_line.HasSwitch(kNoWindow))) {
|
||||
LOG(ERROR) << "could not create window";
|
||||
if (security_attributes.lpSecurityDescriptor)
|
||||
LocalFree(security_attributes.lpSecurityDescriptor);
|
||||
return false;
|
||||
}
|
||||
|
||||
reporter_tag_ = L"crash svc";
|
||||
if (cmd_line.HasSwitch(kReporterTag))
|
||||
reporter_tag_ = cmd_line.GetSwitchValueNative(kReporterTag);
|
||||
|
||||
reporter_url_ = kGoogleReportURL;
|
||||
if (cmd_line.HasSwitch(kReporterURL))
|
||||
reporter_url_ = cmd_line.GetSwitchValueNative(kReporterURL);
|
||||
|
||||
// Log basic information.
|
||||
VLOG(1) << "pipe name is " << pipe_name
|
||||
<< "\ndumps at " << dumps_path_to_use.value();
|
||||
|
||||
if (sender_) {
|
||||
VLOG(1) << "checkpoint is " << checkpoint_path.value()
|
||||
<< "\nserver is " << reporter_url_
|
||||
<< "\nmaximum " << sender_->max_reports_per_day() << " reports/day"
|
||||
<< "\nreporter is " << reporter_tag_;
|
||||
}
|
||||
// Start servicing clients.
|
||||
if (!dumper_->Start()) {
|
||||
LOG(ERROR) << "could not start dumper";
|
||||
if (security_attributes.lpSecurityDescriptor)
|
||||
LocalFree(security_attributes.lpSecurityDescriptor);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (security_attributes.lpSecurityDescriptor)
|
||||
LocalFree(security_attributes.lpSecurityDescriptor);
|
||||
|
||||
// Create or open an event to signal the browser process that the crash
|
||||
// service is initialized.
|
||||
HANDLE running_event =
|
||||
::CreateEventW(NULL, TRUE, TRUE, L"g_atom_shell_crash_service");
|
||||
// If the browser already had the event open, the CreateEvent call did not
|
||||
// signal it. We need to do it manually.
|
||||
::SetEvent(running_event);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CrashService::OnClientConnected(void* context,
|
||||
const google_breakpad::ClientInfo* client_info) {
|
||||
ProcessingLock lock;
|
||||
VLOG(1) << "client start. pid = " << client_info->pid();
|
||||
CrashService* self = static_cast<CrashService*>(context);
|
||||
::InterlockedIncrement(&self->clients_connected_);
|
||||
}
|
||||
|
||||
void CrashService::OnClientExited(void* context,
|
||||
const google_breakpad::ClientInfo* client_info) {
|
||||
ProcessingLock lock;
|
||||
VLOG(1) << "client end. pid = " << client_info->pid();
|
||||
CrashService* self = static_cast<CrashService*>(context);
|
||||
::InterlockedIncrement(&self->clients_terminated_);
|
||||
|
||||
if (!self->sender_)
|
||||
return;
|
||||
|
||||
// When we are instructed to send reports we need to exit if there are
|
||||
// no more clients to service. The next client that runs will start us.
|
||||
// Only chrome.exe starts crash_service with a non-zero max_reports.
|
||||
if (self->clients_connected_ > self->clients_terminated_)
|
||||
return;
|
||||
if (self->sender_->max_reports_per_day() > 0) {
|
||||
// Wait for the other thread to send crashes, if applicable. The sender
|
||||
// thread takes the sending_ lock, so the sleep is just to give it a
|
||||
// chance to start.
|
||||
::Sleep(1000);
|
||||
base::AutoLock lock(self->sending_);
|
||||
// Some people can restart chrome very fast, check again if we have
|
||||
// a new client before exiting for real.
|
||||
if (self->clients_connected_ == self->clients_terminated_) {
|
||||
VLOG(1) << "zero clients. exiting";
|
||||
::PostMessage(g_top_window, WM_CLOSE, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CrashService::OnClientDumpRequest(void* context,
|
||||
const google_breakpad::ClientInfo* client_info,
|
||||
const std::wstring* file_path) {
|
||||
ProcessingLock lock;
|
||||
|
||||
if (!file_path) {
|
||||
LOG(ERROR) << "dump with no file path";
|
||||
return;
|
||||
}
|
||||
if (!client_info) {
|
||||
LOG(ERROR) << "dump with no client info";
|
||||
return;
|
||||
}
|
||||
|
||||
CrashService* self = static_cast<CrashService*>(context);
|
||||
if (!self) {
|
||||
LOG(ERROR) << "dump with no context";
|
||||
return;
|
||||
}
|
||||
|
||||
CrashMap map;
|
||||
CustomInfoToMap(client_info, self->reporter_tag_, &map);
|
||||
|
||||
// Move dump file to the directory under client breakpad dump location.
|
||||
base::FilePath dump_location = base::FilePath(*file_path);
|
||||
CrashMap::const_iterator it = map.find(L"breakpad-dump-location");
|
||||
if (it != map.end()) {
|
||||
base::FilePath alternate_dump_location = base::FilePath(it->second);
|
||||
file_util::CreateDirectoryW(alternate_dump_location);
|
||||
alternate_dump_location = alternate_dump_location.Append(
|
||||
dump_location.BaseName());
|
||||
file_util::Move(dump_location, alternate_dump_location);
|
||||
dump_location = alternate_dump_location;
|
||||
}
|
||||
|
||||
DWORD pid = client_info->pid();
|
||||
VLOG(1) << "dump for pid = " << pid << " is " << dump_location.value();
|
||||
|
||||
if (!WriteCustomInfoToFile(dump_location.value(), map)) {
|
||||
LOG(ERROR) << "could not write custom info file";
|
||||
}
|
||||
|
||||
if (!self->sender_)
|
||||
return;
|
||||
|
||||
// Send the crash dump using a worker thread. This operation has retry
|
||||
// logic in case there is no internet connection at the time.
|
||||
DumpJobInfo* dump_job = new DumpJobInfo(pid, self, map,
|
||||
dump_location.value());
|
||||
if (!::QueueUserWorkItem(&CrashService::AsyncSendDump,
|
||||
dump_job, WT_EXECUTELONGFUNCTION)) {
|
||||
LOG(ERROR) << "could not queue job";
|
||||
}
|
||||
}
|
||||
|
||||
// We are going to try sending the report several times. If we can't send,
|
||||
// we sleep from one minute to several hours depending on the retry round.
|
||||
DWORD CrashService::AsyncSendDump(void* context) {
|
||||
if (!context)
|
||||
return 0;
|
||||
|
||||
DumpJobInfo* info = static_cast<DumpJobInfo*>(context);
|
||||
|
||||
std::wstring report_id = L"<unsent>";
|
||||
|
||||
const DWORD kOneMinute = 60*1000;
|
||||
const DWORD kOneHour = 60*kOneMinute;
|
||||
|
||||
const DWORD kSleepSchedule[] = {
|
||||
24*kOneHour,
|
||||
8*kOneHour,
|
||||
4*kOneHour,
|
||||
kOneHour,
|
||||
15*kOneMinute,
|
||||
0};
|
||||
|
||||
int retry_round = arraysize(kSleepSchedule) - 1;
|
||||
|
||||
do {
|
||||
::Sleep(kSleepSchedule[retry_round]);
|
||||
{
|
||||
// Take the server lock while sending. This also prevent early
|
||||
// termination of the service object.
|
||||
base::AutoLock lock(info->self->sending_);
|
||||
VLOG(1) << "trying to send report for pid = " << info->pid;
|
||||
google_breakpad::ReportResult send_result
|
||||
= info->self->sender_->SendCrashReport(info->self->reporter_url_,
|
||||
info->map,
|
||||
info->dump_path,
|
||||
&report_id);
|
||||
switch (send_result) {
|
||||
case google_breakpad::RESULT_FAILED:
|
||||
report_id = L"<network issue>";
|
||||
break;
|
||||
case google_breakpad::RESULT_REJECTED:
|
||||
report_id = L"<rejected>";
|
||||
++info->self->requests_handled_;
|
||||
retry_round = 0;
|
||||
break;
|
||||
case google_breakpad::RESULT_SUCCEEDED:
|
||||
++info->self->requests_sent_;
|
||||
++info->self->requests_handled_;
|
||||
retry_round = 0;
|
||||
break;
|
||||
case google_breakpad::RESULT_THROTTLED:
|
||||
report_id = L"<throttled>";
|
||||
break;
|
||||
default:
|
||||
report_id = L"<unknown>";
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
VLOG(1) << "dump for pid =" << info->pid << " crash2 id =" << report_id;
|
||||
--retry_round;
|
||||
} while (retry_round >= 0);
|
||||
|
||||
if (!::DeleteFileW(info->dump_path.c_str()))
|
||||
LOG(WARNING) << "could not delete " << info->dump_path;
|
||||
|
||||
delete info;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CrashService::ProcessingLoop() {
|
||||
MSG msg;
|
||||
while (GetMessage(&msg, NULL, 0, 0)) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
|
||||
VLOG(1) << "session ending..";
|
||||
while (ProcessingLock::IsWorking()) {
|
||||
::Sleep(50);
|
||||
}
|
||||
|
||||
VLOG(1) << "clients connected :" << clients_connected_
|
||||
<< "\nclients terminated :" << clients_terminated_
|
||||
<< "\ndumps serviced :" << requests_handled_
|
||||
<< "\ndumps reported :" << requests_sent_;
|
||||
|
||||
return static_cast<int>(msg.wParam);
|
||||
}
|
||||
|
||||
PSECURITY_DESCRIPTOR CrashService::GetSecurityDescriptorForLowIntegrity() {
|
||||
// Build the SDDL string for the label.
|
||||
std::wstring sddl = L"S:(ML;;NW;;;S-1-16-4096)";
|
||||
|
||||
DWORD error = ERROR_SUCCESS;
|
||||
PSECURITY_DESCRIPTOR sec_desc = NULL;
|
||||
|
||||
PACL sacl = NULL;
|
||||
BOOL sacl_present = FALSE;
|
||||
BOOL sacl_defaulted = FALSE;
|
||||
|
||||
if (::ConvertStringSecurityDescriptorToSecurityDescriptorW(sddl.c_str(),
|
||||
SDDL_REVISION,
|
||||
&sec_desc, NULL)) {
|
||||
if (::GetSecurityDescriptorSacl(sec_desc, &sacl_present, &sacl,
|
||||
&sacl_defaulted)) {
|
||||
return sec_desc;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
} // namespace breakpad
|
||||
|
131
common/crash_reporter/win/crash_service.h
Normal file
131
common/crash_reporter/win/crash_service.h
Normal file
|
@ -0,0 +1,131 @@
|
|||
// Copyright (c) 2011 The Chromium 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 COMMON_CRASH_REPORTER_WIN_CRASH_SERVICE_H_
|
||||
#define COMMON_CRASH_REPORTER_WIN_CRASH_SERVICE_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/synchronization/lock.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class CrashReportSender;
|
||||
class CrashGenerationServer;
|
||||
class ClientInfo;
|
||||
|
||||
}
|
||||
|
||||
namespace breakpad {
|
||||
|
||||
// This class implements an out-of-process crash server. It uses breakpad's
|
||||
// CrashGenerationServer and CrashReportSender to generate and then send the
|
||||
// crash dumps. Internally, it uses OS specific pipe to allow applications to
|
||||
// register for crash dumps and later on when a registered application crashes
|
||||
// it will signal an event that causes this code to wake up and perform a
|
||||
// crash dump on the signaling process. The dump is then stored on disk and
|
||||
// possibly sent to the crash2 servers.
|
||||
class CrashService {
|
||||
public:
|
||||
CrashService();
|
||||
~CrashService();
|
||||
|
||||
// Starts servicing crash dumps. Returns false if it failed. Do not use
|
||||
// other members in that case. |operating_dir| is where the CrashService
|
||||
// should store breakpad's checkpoint file. |dumps_path| is the directory
|
||||
// where the crash dumps should be stored.
|
||||
bool Initialize(const base::FilePath& operating_dir,
|
||||
const base::FilePath& dumps_path);
|
||||
|
||||
// Command line switches:
|
||||
//
|
||||
// --max-reports=<number>
|
||||
// Allows to override the maximum number for reports per day. Normally
|
||||
// the crash dumps are never sent so if you want to send any you must
|
||||
// specify a positive number here.
|
||||
static const char kMaxReports[];
|
||||
// --no-window
|
||||
// Does not create a visible window on the desktop. The window does not have
|
||||
// any other functionality other than allowing the crash service to be
|
||||
// gracefully closed.
|
||||
static const char kNoWindow[];
|
||||
// --reporter=<string>
|
||||
// Allows to specify a custom string that appears on the detail crash report
|
||||
// page in the crash server. This should be a 25 chars or less string.
|
||||
// The default tag if not specified is 'crash svc'.
|
||||
static const char kReporterTag[];
|
||||
// --dumps-dir=<directory-path>
|
||||
// Override the directory to which crash dump files will be written.
|
||||
static const char kDumpsDir[];
|
||||
// --pipe-name=<string>
|
||||
// Override the name of the Windows named pipe on which we will
|
||||
// listen for crash dump request messages.
|
||||
static const char kPipeName[];
|
||||
// --reporter-url=<string>
|
||||
// Override the URL to which crash reports will be sent to.
|
||||
static const char kReporterURL[];
|
||||
|
||||
// Returns number of crash dumps handled.
|
||||
int requests_handled() const {
|
||||
return requests_handled_;
|
||||
}
|
||||
// Returns number of crash clients registered.
|
||||
int clients_connected() const {
|
||||
return clients_connected_;
|
||||
}
|
||||
// Returns number of crash clients terminated.
|
||||
int clients_terminated() const {
|
||||
return clients_terminated_;
|
||||
}
|
||||
|
||||
// Starts the processing loop. This function does not return unless the
|
||||
// user is logging off or the user closes the crash service window. The
|
||||
// return value is a good number to pass in ExitProcess().
|
||||
int ProcessingLoop();
|
||||
|
||||
private:
|
||||
static void OnClientConnected(void* context,
|
||||
const google_breakpad::ClientInfo* client_info);
|
||||
|
||||
static void OnClientDumpRequest(
|
||||
void* context,
|
||||
const google_breakpad::ClientInfo* client_info,
|
||||
const std::wstring* file_path);
|
||||
|
||||
static void OnClientExited(void* context,
|
||||
const google_breakpad::ClientInfo* client_info);
|
||||
|
||||
// This routine sends the crash dump to the server. It takes the sending_
|
||||
// lock when it is performing the send.
|
||||
static DWORD __stdcall AsyncSendDump(void* context);
|
||||
|
||||
// Returns the security descriptor which access to low integrity processes
|
||||
// The caller is supposed to free the security descriptor by calling
|
||||
// LocalFree.
|
||||
PSECURITY_DESCRIPTOR GetSecurityDescriptorForLowIntegrity();
|
||||
|
||||
google_breakpad::CrashGenerationServer* dumper_;
|
||||
google_breakpad::CrashReportSender* sender_;
|
||||
|
||||
// the extra tag sent to the server with each dump.
|
||||
std::wstring reporter_tag_;
|
||||
|
||||
// receiver URL of crash reports.
|
||||
std::wstring reporter_url_;
|
||||
|
||||
// clients serviced statistics:
|
||||
int requests_handled_;
|
||||
int requests_sent_;
|
||||
volatile LONG clients_connected_;
|
||||
volatile LONG clients_terminated_;
|
||||
base::Lock sending_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CrashService);
|
||||
};
|
||||
|
||||
} // namespace breakpad
|
||||
|
||||
#endif // COMMON_CRASH_REPORTER_WIN_CRASH_SERVICE_H_
|
92
common/crash_reporter/win/crash_service_main.cc
Normal file
92
common/crash_reporter/win/crash_service_main.cc
Normal file
|
@ -0,0 +1,92 @@
|
|||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "common/crash_reporter/win/crash_service_main.h"
|
||||
|
||||
#include "base/at_exit.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/file_util.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/string_util.h"
|
||||
#include "common/crash_reporter/win/crash_service.h"
|
||||
|
||||
namespace crash_service {
|
||||
|
||||
namespace {
|
||||
|
||||
const char kApplicationName[] = "application-name";
|
||||
|
||||
const wchar_t kPipeNameFormat[] = L"\\\\.\\pipe\\$1 Crash Service";
|
||||
const wchar_t kStandardLogFile[] = L"operation_log.txt";
|
||||
|
||||
bool GetCrashServiceDirectory(const std::wstring& application_name,
|
||||
base::FilePath* dir) {
|
||||
base::FilePath temp_dir;
|
||||
if (!file_util::GetTempDir(&temp_dir))
|
||||
return false;
|
||||
temp_dir = temp_dir.Append(application_name + L" Crashes");
|
||||
if (!file_util::PathExists(temp_dir)) {
|
||||
if (!file_util::CreateDirectory(temp_dir))
|
||||
return false;
|
||||
}
|
||||
*dir = temp_dir;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace.
|
||||
|
||||
int Main(const wchar_t* cmd) {
|
||||
// Initialize all Chromium things.
|
||||
base::AtExitManager exit_manager;
|
||||
CommandLine::Init(0, NULL);
|
||||
CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
|
||||
|
||||
// Use the application's name as pipe name and output directory.
|
||||
if (!cmd_line.HasSwitch(kApplicationName)) {
|
||||
LOG(ERROR) << "Application's name must be specified with --"
|
||||
<< kApplicationName;
|
||||
return 1;
|
||||
}
|
||||
std::wstring application_name = cmd_line.GetSwitchValueNative(
|
||||
kApplicationName);
|
||||
|
||||
// We use/create a directory under the user's temp folder, for logging.
|
||||
base::FilePath operating_dir;
|
||||
GetCrashServiceDirectory(application_name, &operating_dir);
|
||||
base::FilePath log_file = operating_dir.Append(kStandardLogFile);
|
||||
|
||||
// Logging out to a file.
|
||||
logging::InitLogging(
|
||||
log_file.value().c_str(),
|
||||
logging::LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG ,
|
||||
logging::LOCK_LOG_FILE,
|
||||
logging::DELETE_OLD_LOG_FILE,
|
||||
logging::DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS);
|
||||
logging::SetLogItems(true, false, true, false);
|
||||
|
||||
VLOG(1) << "Session start. cmdline is [" << cmd << "]";
|
||||
|
||||
// Setting the crash reporter.
|
||||
string16 pipe_name = ReplaceStringPlaceholders(kPipeNameFormat,
|
||||
application_name,
|
||||
NULL);
|
||||
cmd_line.AppendSwitch("no-window");
|
||||
cmd_line.AppendSwitchASCII("max-reports", "128");
|
||||
cmd_line.AppendSwitchASCII("reporter", "atom-shell-crash-service");
|
||||
cmd_line.AppendSwitchNative("pipe-name", pipe_name);
|
||||
|
||||
breakpad::CrashService crash_service;
|
||||
if (!crash_service.Initialize(operating_dir, operating_dir))
|
||||
return 2;
|
||||
|
||||
VLOG(1) << "Ready to process crash requests";
|
||||
|
||||
// Enter the message loop.
|
||||
int retv = crash_service.ProcessingLoop();
|
||||
// Time to exit.
|
||||
VLOG(1) << "Session end. return code is " << retv;
|
||||
return retv;
|
||||
}
|
||||
|
||||
} // namespace crash_service
|
15
common/crash_reporter/win/crash_service_main.h
Normal file
15
common/crash_reporter/win/crash_service_main.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef COMMON_CRASH_REPORTER_WIN_CRASH_SERVICE_MAIN_H_
|
||||
#define COMMON_CRASH_REPORTER_WIN_CRASH_SERVICE_MAIN_H_
|
||||
|
||||
namespace crash_service {
|
||||
|
||||
// Program entry, should be called by main();
|
||||
int Main(const wchar_t* cmd_line);
|
||||
|
||||
} // namespace crash_service
|
||||
|
||||
#endif // COMMON_CRASH_REPORTER_WIN_CRASH_SERVICE_MAIN_H_
|
|
@ -5,6 +5,7 @@
|
|||
#ifndef COMMON_V8_CONVERSIONS_H_
|
||||
#define COMMON_V8_CONVERSIONS_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
@ -62,6 +63,19 @@ struct FromV8Value {
|
|||
return array;
|
||||
}
|
||||
|
||||
operator std::map<std::string, std::string>() {
|
||||
std::map<std::string, std::string> dict;
|
||||
v8::Handle<v8::Object> v8_dict = value_->ToObject();
|
||||
v8::Handle<v8::Array> v8_keys = v8_dict->GetOwnPropertyNames();
|
||||
for (uint32_t i = 0; i < v8_keys->Length(); ++i) {
|
||||
v8::Handle<v8::Value> v8_key = v8_keys->Get(i);
|
||||
std::string key = FromV8Value(v8_key);
|
||||
dict[key] = std::string(FromV8Value(v8_dict->Get(v8_key)));
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
operator atom::NativeWindow*() {
|
||||
using atom::api::Window;
|
||||
if (value_->IsObject()) {
|
||||
|
@ -161,6 +175,12 @@ bool V8ValueCanBeConvertedTo<std::vector<std::string>>(
|
|||
return value->IsArray();
|
||||
}
|
||||
|
||||
template<> inline
|
||||
bool V8ValueCanBeConvertedTo<std::map<std::string, std::string>>(
|
||||
v8::Handle<v8::Value> value) {
|
||||
return value->IsObject();
|
||||
}
|
||||
|
||||
template<> inline
|
||||
bool V8ValueCanBeConvertedTo<atom::NativeWindow*>(v8::Handle<v8::Value> value) {
|
||||
using atom::api::Window;
|
||||
|
|
|
@ -25,7 +25,6 @@ Browser side modules:
|
|||
* [atom-delegate](api/browser/atom-delegate.md)
|
||||
* [auto-updater](api/browser/auto-updater.md)
|
||||
* [browser-window](api/browser/browser-window.md)
|
||||
* [crash-reporter](api/browser/crash-reporter.md)
|
||||
* [dialog](api/browser/dialog.md)
|
||||
* [ipc (browser)](api/browser/ipc-browser.md)
|
||||
* [menu](api/browser/menu.md)
|
||||
|
@ -36,4 +35,5 @@ Browser side modules:
|
|||
Common modules:
|
||||
|
||||
* [clipboard](api/common/clipboard.md)
|
||||
* [crash-reporter](api/common/crash-reporter.md)
|
||||
* [shell](api/common/shell.md)
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
# crash-reporter
|
||||
|
||||
An example of automatically submitting crash reporters to remote server:
|
||||
|
||||
```javascript
|
||||
crashReporter = require('crash-reporter');
|
||||
crashReporter.setCompanyName('YourCompany');
|
||||
crashReporter.setSubmissionUrl('https://your-domain.com/url-to-submit');
|
||||
crashReporter.setAutoSubmit(true);
|
||||
```
|
||||
|
||||
## crashReporter.setCompanyName(company)
|
||||
|
||||
* `company` String
|
||||
|
||||
## crashReporter.setSubmissionUrl(url)
|
||||
|
||||
* `url` String
|
||||
|
||||
## crashReporter.setAutoSubmit(is)
|
||||
|
||||
* `is` Boolean
|
22
docs/api/common/crash-reporter.md
Normal file
22
docs/api/common/crash-reporter.md
Normal file
|
@ -0,0 +1,22 @@
|
|||
# crash-reporter
|
||||
|
||||
An example of automatically submitting crash reporters to remote server:
|
||||
|
||||
```javascript
|
||||
crashReporter = require('crash-reporter');
|
||||
crashReporter.start({
|
||||
productName: 'YourName',
|
||||
companyName: 'YourCompany',
|
||||
submitUrl: 'https://your-domain.com/url-to-submit',
|
||||
autoSubmit: true
|
||||
});
|
||||
```
|
||||
|
||||
## crashReporter.start(options)
|
||||
|
||||
* `options` Object
|
||||
* `productName` String
|
||||
* `companyName` String
|
||||
* `submitUrl` String - URL that crash reports would be sent to
|
||||
* `autoSubmit` Boolean - Send the crash report without user interaction
|
||||
* `ignoreSystemCrashHandler` Boolean
|
|
@ -7,7 +7,9 @@
|
|||
"coffeelint": "~0.6.1",
|
||||
"mocha": "~1.13.0",
|
||||
"walkdir": "~0.0.7",
|
||||
"runas": "~0.2.0",
|
||||
|
||||
"formidable": "~1.0.14",
|
||||
"unzip": "~0.1.9",
|
||||
"d3": "~3.3.8",
|
||||
"int64-native": "~0.2.0"
|
||||
|
|
|
@ -58,7 +58,7 @@ def bootstrap_brightray(url):
|
|||
def update_apm():
|
||||
## NB: Without this, subprocess incorrectly searches for npm.exe
|
||||
npm_cmd = 'npm'
|
||||
if sys.platform == 'win32':
|
||||
if sys.platform in ['win32', 'cygwin']:
|
||||
npm_cmd += '.cmd'
|
||||
|
||||
with scoped_cwd(os.path.join('vendor', 'apm')):
|
||||
|
|
|
@ -4,11 +4,17 @@ import os
|
|||
import subprocess
|
||||
import sys
|
||||
|
||||
from lib.util import rm_rf
|
||||
|
||||
|
||||
SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
|
||||
|
||||
|
||||
def main():
|
||||
rm_rf(os.path.join(SOURCE_ROOT, 'out'))
|
||||
rm_rf(os.path.join(SOURCE_ROOT, 'vendor', 'brightray', 'vendor', 'download',
|
||||
'libchromiumcontent'))
|
||||
|
||||
run_script('bootstrap.py')
|
||||
run_script('cpplint.py')
|
||||
run_script('pylint.py')
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
|
@ -12,9 +13,11 @@ from lib.util import scoped_cwd, rm_rf, get_atom_shell_version, make_zip, \
|
|||
|
||||
ATOM_SHELL_VRESION = get_atom_shell_version()
|
||||
NODE_VERSION = 'v0.10.18'
|
||||
BASE_URL = 'https://gh-contractor-zcbenz.s3.amazonaws.com/libchromiumcontent'
|
||||
|
||||
SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
|
||||
DIST_DIR = os.path.join(SOURCE_ROOT, 'dist')
|
||||
OUT_DIR = os.path.join(SOURCE_ROOT, 'out', 'Release')
|
||||
NODE_DIR = os.path.join(SOURCE_ROOT, 'vendor', 'node')
|
||||
DIST_HEADERS_NAME = 'node-{0}'.format(NODE_VERSION)
|
||||
DIST_HEADERS_DIR = os.path.join(DIST_DIR, DIST_HEADERS_NAME)
|
||||
|
@ -68,28 +71,42 @@ def main():
|
|||
rm_rf(DIST_DIR)
|
||||
os.makedirs(DIST_DIR)
|
||||
|
||||
args = parse_args()
|
||||
|
||||
force_build()
|
||||
download_libchromiumcontent_symbols(args.url)
|
||||
create_symbols()
|
||||
copy_binaries()
|
||||
copy_headers()
|
||||
copy_license()
|
||||
create_version()
|
||||
create_zip()
|
||||
create_dist_zip()
|
||||
create_symbols_zip()
|
||||
create_header_tarball()
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(description='Create distributions')
|
||||
parser.add_argument('-u', '--url',
|
||||
help='The base URL from which to download '
|
||||
'libchromiumcontent (i.e., the URL you passed to '
|
||||
'libchromiumcontent\'s script/upload script',
|
||||
default=BASE_URL,
|
||||
required=False)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def force_build():
|
||||
build = os.path.join(SOURCE_ROOT, 'script', 'build.py')
|
||||
subprocess.check_call([sys.executable, build, '-c', 'Release'])
|
||||
|
||||
|
||||
def copy_binaries():
|
||||
out_dir = os.path.join(SOURCE_ROOT, 'out', 'Release')
|
||||
|
||||
for binary in TARGET_BINARIES[TARGET_PLATFORM]:
|
||||
shutil.copy2(os.path.join(out_dir, binary), DIST_DIR)
|
||||
shutil.copy2(os.path.join(OUT_DIR, binary), DIST_DIR)
|
||||
|
||||
for directory in TARGET_DIRECTORIES[TARGET_PLATFORM]:
|
||||
shutil.copytree(os.path.join(out_dir, directory),
|
||||
shutil.copytree(os.path.join(OUT_DIR, directory),
|
||||
os.path.join(DIST_DIR, directory),
|
||||
symlinks=True)
|
||||
|
||||
|
@ -131,7 +148,40 @@ def create_version():
|
|||
version_file.write(ATOM_SHELL_VRESION)
|
||||
|
||||
|
||||
def create_zip():
|
||||
def download_libchromiumcontent_symbols(url):
|
||||
if sys.platform == 'darwin':
|
||||
symbols_name = 'libchromiumcontent.dylib.dSYM'
|
||||
else:
|
||||
symbols_name = 'chromiumcontent.dll.pdb'
|
||||
|
||||
brightray_dir = os.path.join(SOURCE_ROOT, 'vendor', 'brightray', 'vendor')
|
||||
target_dir = os.path.join(brightray_dir, 'download', 'libchromiumcontent')
|
||||
symbols_path = os.path.join(target_dir, 'Release', symbols_name)
|
||||
if os.path.exists(symbols_path):
|
||||
return
|
||||
|
||||
download = os.path.join(brightray_dir, 'libchromiumcontent', 'script',
|
||||
'download')
|
||||
subprocess.check_call([sys.executable, download, '-f', '-s', url, target_dir])
|
||||
|
||||
if sys.platform == 'darwin':
|
||||
shutil.copytree(symbols_path,
|
||||
os.path.join(OUT_DIR, symbols_name),
|
||||
symlinks=True)
|
||||
|
||||
|
||||
def create_symbols():
|
||||
build = os.path.join(SOURCE_ROOT, 'script', 'build.py')
|
||||
subprocess.check_output([sys.executable, build, '-c', 'Release',
|
||||
'-t', 'atom_dump_symbols'])
|
||||
|
||||
directory = 'Atom-Shell.breakpad.syms'
|
||||
shutil.copytree(os.path.join(OUT_DIR, directory),
|
||||
os.path.join(DIST_DIR, directory),
|
||||
symlinks=True)
|
||||
|
||||
|
||||
def create_dist_zip():
|
||||
dist_name = 'atom-shell-{0}-{1}.zip'.format(ATOM_SHELL_VRESION,
|
||||
TARGET_PLATFORM)
|
||||
zip_file = os.path.join(SOURCE_ROOT, 'dist', dist_name)
|
||||
|
@ -142,6 +192,17 @@ def create_zip():
|
|||
make_zip(zip_file, files, dirs)
|
||||
|
||||
|
||||
def create_symbols_zip():
|
||||
dist_name = 'atom-shell-{0}-{1}-symbols.zip'.format(ATOM_SHELL_VRESION,
|
||||
TARGET_PLATFORM)
|
||||
zip_file = os.path.join(SOURCE_ROOT, 'dist', dist_name)
|
||||
|
||||
with scoped_cwd(DIST_DIR):
|
||||
files = ['LICENSE', 'version']
|
||||
dirs = ['Atom-Shell.breakpad.syms']
|
||||
make_zip(zip_file, files, dirs)
|
||||
|
||||
|
||||
def create_header_tarball():
|
||||
with scoped_cwd(DIST_DIR):
|
||||
tarball = tarfile.open(name=DIST_HEADERS_DIR + '.tar.gz', mode='w:gz')
|
||||
|
|
|
@ -59,7 +59,7 @@ def extract_tarball(tarball_path, member, destination):
|
|||
def extract_zip(zip_path, destination):
|
||||
if sys.platform == 'darwin':
|
||||
# Use unzip command on Mac to keep symbol links in zip file work.
|
||||
subprocess.check_call(['unzip', zip_path, '-d', destination])
|
||||
subprocess.check_output(['unzip', zip_path, '-d', destination])
|
||||
else:
|
||||
with zipfile.ZipFile(zip_path) as z:
|
||||
z.extractall(destination)
|
||||
|
@ -68,7 +68,7 @@ def make_zip(zip_file_path, files, dirs):
|
|||
safe_unlink(zip_file_path)
|
||||
if sys.platform == 'darwin':
|
||||
files += dirs
|
||||
subprocess.check_call(['zip', '-r', '-y', zip_file_path] + files)
|
||||
subprocess.check_output(['zip', '-r', '-y', zip_file_path] + files)
|
||||
else:
|
||||
zip_file = zipfile.ZipFile(zip_file_path, "w", zipfile.ZIP_DEFLATED)
|
||||
for filename in files:
|
||||
|
|
|
@ -13,7 +13,6 @@ FRAMEWORKS_URL = 'https://gh-contractor-zcbenz.s3.amazonaws.com/frameworks'
|
|||
def main():
|
||||
os.chdir(SOURCE_ROOT)
|
||||
safe_mkdir('frameworks')
|
||||
download_and_unzip('Quincy')
|
||||
download_and_unzip('Sparkle')
|
||||
|
||||
|
||||
|
|
|
@ -27,6 +27,8 @@ SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
|
|||
OUT_DIR = os.path.join(SOURCE_ROOT, 'out', 'Release')
|
||||
DIST_DIR = os.path.join(SOURCE_ROOT, 'dist')
|
||||
DIST_NAME = 'atom-shell-{0}-{1}.zip'.format(ATOM_SHELL_VRESION, TARGET_PLATFORM)
|
||||
SYMBOLS_NAME = 'atom-shell-{0}-{1}-symbols.zip'.format(ATOM_SHELL_VRESION,
|
||||
TARGET_PLATFORM)
|
||||
|
||||
|
||||
def main():
|
||||
|
@ -48,6 +50,7 @@ def main():
|
|||
github = GitHub(auth_token())
|
||||
release_id = create_or_get_release_draft(github, args.version)
|
||||
upload_atom_shell(github, release_id, os.path.join(DIST_DIR, DIST_NAME))
|
||||
upload_atom_shell(github, release_id, os.path.join(DIST_DIR, SYMBOLS_NAME))
|
||||
if not args.no_publish_release:
|
||||
publish_release(github, release_id)
|
||||
|
||||
|
|
32
spec/api/crash-reporter.coffee
Normal file
32
spec/api/crash-reporter.coffee
Normal file
|
@ -0,0 +1,32 @@
|
|||
assert = require 'assert'
|
||||
path = require 'path'
|
||||
http = require 'http'
|
||||
remote = require 'remote'
|
||||
formidable = require 'formidable'
|
||||
BrowserWindow = remote.require 'browser-window'
|
||||
|
||||
fixtures = path.resolve __dirname, '..', 'fixtures'
|
||||
|
||||
describe 'crash-reporter module', ->
|
||||
it 'should send minidump when renderer crashes', (done) ->
|
||||
w = new BrowserWindow(show: false)
|
||||
server = http.createServer (req, res) ->
|
||||
form = new formidable.IncomingForm()
|
||||
form.parse req, (error, fields, files) ->
|
||||
assert.equal fields['prod'], 'Atom-Shell'
|
||||
assert.equal fields['ver'], process.versions['atom-shell']
|
||||
assert.equal fields['process_type'], 'renderer'
|
||||
assert.equal fields['platform'], process.platform
|
||||
assert.equal fields['extra1'], 'extra1'
|
||||
assert.equal fields['extra2'], 'extra2'
|
||||
assert.equal fields['_productName'], 'Zombies'
|
||||
assert.equal fields['_companyName'], 'Umbrella Corporation'
|
||||
assert.equal fields['_version'], require('remote').require('app').getVersion()
|
||||
assert files['upload_file_minidump']['name']?
|
||||
|
||||
w.destroy()
|
||||
res.end()
|
||||
server.close()
|
||||
done()
|
||||
server.listen 1127, '127.0.0.1', ->
|
||||
w.loadUrl 'file://' + path.join(fixtures, 'api', 'crash.html')
|
20
spec/fixtures/api/crash.html
vendored
Normal file
20
spec/fixtures/api/crash.html
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
<html>
|
||||
<body>
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
var crashReporter = require('crash-reporter');
|
||||
crashReporter.start({
|
||||
productName: 'Zombies',
|
||||
companyName: 'Umbrella Corporation',
|
||||
submitUrl: 'http://127.0.0.1:1127',
|
||||
autoSubmit: true,
|
||||
ignoreSystemCrashHandler: true,
|
||||
extra: {
|
||||
'extra1': 'extra1',
|
||||
'extra2': 'extra2',
|
||||
}
|
||||
});
|
||||
|
||||
setImmediate(function() { process.crash(); });
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
261
tools/mac/generate_breakpad_symbols.py
Executable file
261
tools/mac/generate_breakpad_symbols.py
Executable file
|
@ -0,0 +1,261 @@
|
|||
#!/usr/bin/env python
|
||||
# Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
# Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
"""A tool to generate symbols for a binary suitable for breakpad.
|
||||
|
||||
Currently, the tool only supports Linux, Android, and Mac. Support for other
|
||||
platforms is planned.
|
||||
"""
|
||||
|
||||
import errno
|
||||
import optparse
|
||||
import os
|
||||
import Queue
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
|
||||
|
||||
CONCURRENT_TASKS=4
|
||||
|
||||
|
||||
def GetCommandOutput(command):
|
||||
"""Runs the command list, returning its output.
|
||||
|
||||
Prints the given command (which should be a list of one or more strings),
|
||||
then runs it and returns its output (stdout) as a string.
|
||||
|
||||
From chromium_utils.
|
||||
"""
|
||||
devnull = open(os.devnull, 'w')
|
||||
proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=devnull,
|
||||
bufsize=1)
|
||||
output = proc.communicate()[0]
|
||||
return output
|
||||
|
||||
|
||||
def GetDumpSymsBinary(build_dir=None):
|
||||
"""Returns the path to the dump_syms binary."""
|
||||
DUMP_SYMS = 'dump_syms'
|
||||
dump_syms_bin = os.path.join(os.path.expanduser(build_dir), DUMP_SYMS)
|
||||
if not os.access(dump_syms_bin, os.X_OK):
|
||||
print 'Cannot find %s.' % DUMP_SYMS
|
||||
sys.exit(1)
|
||||
|
||||
return dump_syms_bin
|
||||
|
||||
|
||||
def FindBundlePart(full_path):
|
||||
if full_path.endswith(('.dylib', '.framework', '.app')):
|
||||
return os.path.basename(full_path)
|
||||
elif full_path != '' and full_path != '/':
|
||||
return FindBundlePart(os.path.dirname(full_path))
|
||||
else:
|
||||
return ''
|
||||
|
||||
|
||||
def GetDSYMBundle(build_dir, binary_path):
|
||||
"""Finds the .dSYM bundle to the binary."""
|
||||
if binary_path[0] == '/' or binary_path == '':
|
||||
return binary_path
|
||||
|
||||
filename = FindBundlePart(binary_path)
|
||||
if filename.endswith(('.dylib', '.framework', '.app')):
|
||||
dsym_path = os.path.join(build_dir, filename) + '.dSYM'
|
||||
|
||||
if dsym_path != None and os.path.exists(dsym_path):
|
||||
return dsym_path
|
||||
else:
|
||||
return binary_path
|
||||
|
||||
|
||||
def Resolve(path, exe_path, loader_path, rpaths):
|
||||
"""Resolve a dyld path.
|
||||
|
||||
@executable_path is replaced with |exe_path|
|
||||
@loader_path is replaced with |loader_path|
|
||||
@rpath is replaced with the first path in |rpaths| where the referenced file
|
||||
is found
|
||||
"""
|
||||
path = path.replace('@loader_path', loader_path)
|
||||
path = path.replace('@executable_path', exe_path)
|
||||
if path.find('@rpath') != -1:
|
||||
for rpath in rpaths:
|
||||
new_path = Resolve(path.replace('@rpath', rpath), exe_path, loader_path,
|
||||
[])
|
||||
if os.access(new_path, os.F_OK):
|
||||
return new_path
|
||||
return ''
|
||||
return path
|
||||
|
||||
|
||||
def GetSharedLibraryDependenciesLinux(binary):
|
||||
"""Return absolute paths to all shared library dependecies of the binary.
|
||||
|
||||
This implementation assumes that we're running on a Linux system."""
|
||||
ldd = GetCommandOutput(['ldd', binary])
|
||||
lib_re = re.compile('\t.* => (.+) \(.*\)$')
|
||||
result = []
|
||||
for line in ldd.splitlines():
|
||||
m = lib_re.match(line)
|
||||
if m:
|
||||
result.append(m.group(1))
|
||||
return result
|
||||
|
||||
|
||||
def GetSharedLibraryDependenciesMac(binary, exe_path):
|
||||
"""Return absolute paths to all shared library dependecies of the binary.
|
||||
|
||||
This implementation assumes that we're running on a Mac system."""
|
||||
loader_path = os.path.dirname(binary)
|
||||
otool = GetCommandOutput(['otool', '-l', binary]).splitlines()
|
||||
rpaths = []
|
||||
for idx, line in enumerate(otool):
|
||||
if line.find('cmd LC_RPATH') != -1:
|
||||
m = re.match(' *path (.*) \(offset .*\)$', otool[idx+2])
|
||||
rpaths.append(m.group(1))
|
||||
|
||||
otool = GetCommandOutput(['otool', '-L', binary]).splitlines()
|
||||
lib_re = re.compile('\t(.*) \(compatibility .*\)$')
|
||||
deps = []
|
||||
for line in otool:
|
||||
m = lib_re.match(line)
|
||||
if m:
|
||||
dep = Resolve(m.group(1), exe_path, loader_path, rpaths)
|
||||
if dep:
|
||||
deps.append(os.path.normpath(dep))
|
||||
return deps
|
||||
|
||||
|
||||
def GetSharedLibraryDependencies(options, binary, exe_path):
|
||||
"""Return absolute paths to all shared library dependecies of the binary."""
|
||||
deps = []
|
||||
if sys.platform.startswith('linux'):
|
||||
deps = GetSharedLibraryDependenciesLinux(binary)
|
||||
elif sys.platform == 'darwin':
|
||||
deps = GetSharedLibraryDependenciesMac(binary, exe_path)
|
||||
else:
|
||||
print "Platform not supported."
|
||||
sys.exit(1)
|
||||
|
||||
result = []
|
||||
build_dir = os.path.abspath(options.build_dir)
|
||||
for dep in deps:
|
||||
if (os.access(dep, os.F_OK)):
|
||||
result.append(dep)
|
||||
return result
|
||||
|
||||
|
||||
def mkdir_p(path):
|
||||
"""Simulates mkdir -p."""
|
||||
try:
|
||||
os.makedirs(path)
|
||||
except OSError as e:
|
||||
if e.errno == errno.EEXIST and os.path.isdir(path):
|
||||
pass
|
||||
else: raise
|
||||
|
||||
|
||||
def GenerateSymbols(options, binaries):
|
||||
"""Dumps the symbols of binary and places them in the given directory."""
|
||||
|
||||
queue = Queue.Queue()
|
||||
print_lock = threading.Lock()
|
||||
|
||||
def _Worker():
|
||||
while True:
|
||||
binary = queue.get()
|
||||
|
||||
if options.verbose:
|
||||
with print_lock:
|
||||
print "Generating symbols for %s" % binary
|
||||
|
||||
if sys.platform == 'darwin':
|
||||
binary = GetDSYMBundle(options.build_dir, binary)
|
||||
|
||||
syms = GetCommandOutput([GetDumpSymsBinary(options.build_dir), '-r', '-c',
|
||||
binary])
|
||||
module_line = re.match("MODULE [^ ]+ [^ ]+ ([0-9A-F]+) (.*)\n", syms)
|
||||
output_path = os.path.join(options.symbols_dir, module_line.group(2),
|
||||
module_line.group(1))
|
||||
mkdir_p(output_path)
|
||||
symbol_file = "%s.sym" % module_line.group(2)
|
||||
f = open(os.path.join(output_path, symbol_file), 'w')
|
||||
f.write(syms)
|
||||
f.close()
|
||||
|
||||
queue.task_done()
|
||||
|
||||
for binary in binaries:
|
||||
queue.put(binary)
|
||||
|
||||
for _ in range(options.jobs):
|
||||
t = threading.Thread(target=_Worker)
|
||||
t.daemon = True
|
||||
t.start()
|
||||
|
||||
queue.join()
|
||||
|
||||
|
||||
def main():
|
||||
parser = optparse.OptionParser()
|
||||
parser.add_option('', '--build-dir', default='',
|
||||
help='The build output directory.')
|
||||
parser.add_option('', '--symbols-dir', default='',
|
||||
help='The directory where to write the symbols file.')
|
||||
parser.add_option('', '--binary', default='',
|
||||
help='The path of the binary to generate symbols for.')
|
||||
parser.add_option('', '--clear', default=False, action='store_true',
|
||||
help='Clear the symbols directory before writing new '
|
||||
'symbols.')
|
||||
parser.add_option('-j', '--jobs', default=CONCURRENT_TASKS, action='store',
|
||||
type='int', help='Number of parallel tasks to run.')
|
||||
parser.add_option('-v', '--verbose', action='store_true',
|
||||
help='Print verbose status output.')
|
||||
|
||||
(options, _) = parser.parse_args()
|
||||
|
||||
if not options.symbols_dir:
|
||||
print "Required option --symbols-dir missing."
|
||||
return 1
|
||||
|
||||
if not options.build_dir:
|
||||
print "Required option --build-dir missing."
|
||||
return 1
|
||||
|
||||
if not options.binary:
|
||||
print "Required option --binary missing."
|
||||
return 1
|
||||
|
||||
if not os.access(options.binary, os.X_OK):
|
||||
print "Cannot find %s." % options.binary
|
||||
return 1
|
||||
|
||||
if options.clear:
|
||||
try:
|
||||
shutil.rmtree(options.symbols_dir)
|
||||
except:
|
||||
pass
|
||||
|
||||
# Build the transitive closure of all dependencies.
|
||||
binaries = set([options.binary])
|
||||
queue = [options.binary]
|
||||
exe_path = os.path.dirname(options.binary)
|
||||
while queue:
|
||||
deps = GetSharedLibraryDependencies(options, queue.pop(0), exe_path)
|
||||
new_deps = set(deps) - binaries
|
||||
binaries |= new_deps
|
||||
queue.extend(list(new_deps))
|
||||
|
||||
GenerateSymbols(options, binaries)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if '__main__' == __name__:
|
||||
sys.exit(main())
|
135
tools/win/generate_breakpad_symbols.py
Normal file
135
tools/win/generate_breakpad_symbols.py
Normal file
|
@ -0,0 +1,135 @@
|
|||
#!/usr/bin/env python
|
||||
# Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
# Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
"""Convert pdb to sym for given directories"""
|
||||
|
||||
import errno
|
||||
import glob
|
||||
import optparse
|
||||
import os
|
||||
import Queue
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
|
||||
|
||||
CONCURRENT_TASKS=4
|
||||
SOURCE_ROOT=os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
|
||||
DUMP_SYMS=os.path.join(SOURCE_ROOT, 'vendor', 'breakpad', 'dump_syms.exe')
|
||||
|
||||
|
||||
def GetCommandOutput(command):
|
||||
"""Runs the command list, returning its output.
|
||||
|
||||
Prints the given command (which should be a list of one or more strings),
|
||||
then runs it and returns its output (stdout) as a string.
|
||||
|
||||
From chromium_utils.
|
||||
"""
|
||||
devnull = open(os.devnull, 'w')
|
||||
proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=devnull,
|
||||
bufsize=1)
|
||||
output = proc.communicate()[0]
|
||||
return output
|
||||
|
||||
|
||||
def mkdir_p(path):
|
||||
"""Simulates mkdir -p."""
|
||||
try:
|
||||
os.makedirs(path)
|
||||
except OSError as e:
|
||||
if e.errno == errno.EEXIST and os.path.isdir(path):
|
||||
pass
|
||||
else: raise
|
||||
|
||||
|
||||
def RegisterRequiredDll():
|
||||
register = os.path.join(os.path.dirname(__file__), 'register_msdia80_dll.js')
|
||||
node = os.path.join(SOURCE_ROOT, 'out', 'Release', 'atom.exe')
|
||||
os.environ['ATOM_SHELL_INTERNAL_RUN_AS_NODE'] = '1'
|
||||
subprocess.check_call([node, register]);
|
||||
|
||||
|
||||
def GenerateSymbols(options, binaries):
|
||||
"""Dumps the symbols of binary and places them in the given directory."""
|
||||
|
||||
queue = Queue.Queue()
|
||||
print_lock = threading.Lock()
|
||||
|
||||
def _Worker():
|
||||
while True:
|
||||
binary = queue.get()
|
||||
|
||||
if options.verbose:
|
||||
with print_lock:
|
||||
print "Generating symbols for %s" % binary
|
||||
|
||||
syms = GetCommandOutput([DUMP_SYMS, binary])
|
||||
module_line = re.match("MODULE [^ ]+ [^ ]+ ([0-9A-F]+) (.*)\r\n", syms)
|
||||
if module_line == None:
|
||||
with print_lock:
|
||||
print "Failed to get symbols for %s" % binary
|
||||
queue.task_done()
|
||||
continue
|
||||
|
||||
output_path = os.path.join(options.symbols_dir, module_line.group(2),
|
||||
module_line.group(1))
|
||||
mkdir_p(output_path)
|
||||
symbol_file = "%s.sym" % module_line.group(2)
|
||||
f = open(os.path.join(output_path, symbol_file), 'w')
|
||||
f.write(syms)
|
||||
f.close()
|
||||
|
||||
queue.task_done()
|
||||
|
||||
for binary in binaries:
|
||||
queue.put(binary)
|
||||
|
||||
for _ in range(options.jobs):
|
||||
t = threading.Thread(target=_Worker)
|
||||
t.daemon = True
|
||||
t.start()
|
||||
|
||||
queue.join()
|
||||
|
||||
|
||||
def main():
|
||||
parser = optparse.OptionParser()
|
||||
parser.add_option('', '--symbols-dir', default='',
|
||||
help='The directory where to write the symbols file.')
|
||||
parser.add_option('', '--clear', default=False, action='store_true',
|
||||
help='Clear the symbols directory before writing new '
|
||||
'symbols.')
|
||||
parser.add_option('-j', '--jobs', default=CONCURRENT_TASKS, action='store',
|
||||
type='int', help='Number of parallel tasks to run.')
|
||||
parser.add_option('-v', '--verbose', action='store_true',
|
||||
help='Print verbose status output.')
|
||||
|
||||
(options, directories) = parser.parse_args()
|
||||
|
||||
if not options.symbols_dir:
|
||||
print "Required option --symbols-dir missing."
|
||||
return 1
|
||||
|
||||
if options.clear:
|
||||
try:
|
||||
shutil.rmtree(options.symbols_dir)
|
||||
except:
|
||||
pass
|
||||
|
||||
pdbs = []
|
||||
for directory in directories:
|
||||
pdbs += glob.glob(os.path.join(directory, '*.pdb'))
|
||||
|
||||
RegisterRequiredDll();
|
||||
GenerateSymbols(options, pdbs)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if '__main__' == __name__:
|
||||
sys.exit(main())
|
12
tools/win/register_msdia80_dll.js
Normal file
12
tools/win/register_msdia80_dll.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var runas = require('runas');
|
||||
|
||||
var source = path.resolve(__dirname, '..', '..', 'vendor', 'breakpad', 'msdia80.dll');
|
||||
var target = 'C:\\Program Files\\Common Files\\Microsoft Shared\\VC\\msdia80.dll';
|
||||
if (fs.existsSync(target))
|
||||
return;
|
||||
|
||||
var copy = 'copy "' + source + '" "' + target + '"';
|
||||
var register = 'regsvr32 "' + target + '"';
|
||||
runas('cmd', ['/K', copy + ' & ' + register + ' & exit']);
|
1
vendor/breakpad
vendored
Submodule
1
vendor/breakpad
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 2483f32da1f729ac362fbbcaa9173843379697e9
|
2
vendor/depot_tools
vendored
2
vendor/depot_tools
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 7f834ad2f1fdde361379ccdc19dcc2d2b6426e3c
|
||||
Subproject commit a738935a12e7ef428b3a852f1f3eca6bbfa0182b
|
2
vendor/gyp
vendored
2
vendor/gyp
vendored
|
@ -1 +1 @@
|
|||
Subproject commit b8c2ab69b417009ab5d2a4228368563b33c19d37
|
||||
Subproject commit 88202fb4e5db8d3ba3051fc2ce36f35aae22f69d
|
Loading…
Reference in a new issue