fix: crashReporter incompatible with sandbox on Linux (#23265)

This commit is contained in:
Jeremy Apthorp 2020-05-07 13:31:26 -07:00 committed by GitHub
parent fc434f136b
commit 06bf0d08dc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
77 changed files with 2235 additions and 2404 deletions

View file

@ -325,6 +325,7 @@ source_set("electron_lib") {
"//chrome/app/resources:platform_locale_settings",
"//chrome/services/printing/public/mojom",
"//components/certificate_transparency",
"//components/crash/core/app",
"//components/language/core/browser",
"//components/net_log",
"//components/network_hints/browser",
@ -333,6 +334,7 @@ source_set("electron_lib") {
"//components/network_session_configurator/common",
"//components/pref_registry",
"//components/prefs",
"//components/upload_list",
"//components/user_prefs",
"//components/viz/host",
"//components/viz/service",
@ -446,10 +448,15 @@ source_set("electron_lib") {
]
}
if (is_linux) {
deps += [ "//components/crash/content/browser" ]
}
if (is_mac) {
deps += [
"//components/remote_cocoa/app_shim",
"//content/common:mac_helpers",
"//third_party/crashpad/crashpad/client",
"//ui/accelerated_widget_mac",
]
@ -471,11 +478,7 @@ source_set("electron_lib") {
]
if (is_mas_build) {
sources += [ "shell/browser/api/electron_api_app_mas.mm" ]
sources -= [
"shell/browser/auto_updater_mac.mm",
"shell/common/crash_reporter/crash_reporter_mac.h",
"shell/common/crash_reporter/crash_reporter_mac.mm",
]
sources -= [ "shell/browser/auto_updater_mac.mm" ]
defines += [ "MAS_BUILD" ]
} else {
libs += [
@ -498,7 +501,6 @@ source_set("electron_lib") {
"//build/config/linux/gtk",
"//dbus",
"//device/bluetooth",
"//third_party/breakpad:client",
"//ui/events/devices/x11",
"//ui/events/platform/x11",
"//ui/gtk",
@ -512,7 +514,6 @@ source_set("electron_lib") {
]
}
configs += [ ":gio_unix" ]
include_dirs += [ "//third_party/breakpad" ]
configs += [ "//build/config/linux:x11" ]
defines += [
# Disable warnings for g_settings_list_schemas.
@ -528,6 +529,7 @@ source_set("electron_lib") {
if (is_win) {
libs += [ "dwmapi.lib" ]
deps += [
"//components/crash/core/app:crash_export_thunks",
"//ui/native_theme:native_theme_browser",
"//ui/views/controls/webview",
"//ui/wm",
@ -539,14 +541,6 @@ source_set("electron_lib") {
]
}
if ((is_mac && !is_mas_build) || is_win) {
sources += [
"shell/common/crash_reporter/crash_reporter_crashpad.cc",
"shell/common/crash_reporter/crash_reporter_crashpad.h",
]
deps += [ "//third_party/crashpad/crashpad/client" ]
}
if (enable_plugins) {
deps += [ "chromium_src:plugins" ]
sources += [
@ -751,11 +745,11 @@ if (is_mac) {
}
bundle_data("electron_crashpad_helper") {
sources = [ "$root_out_dir/crashpad_handler" ]
sources = [ "$root_out_dir/chrome_crashpad_handler" ]
outputs = [ "{{bundle_resources_dir}}/{{source_file_part}}" ]
outputs = [ "{{bundle_contents_dir}}/Helpers/{{source_file_part}}" ]
public_deps = [ "//third_party/crashpad/crashpad/handler:crashpad_handler" ]
public_deps = [ "//components/crash/core/app:chrome_crashpad_handler" ]
if (is_asan) {
# crashpad_handler requires the ASan runtime at its @executable_path.
@ -770,6 +764,7 @@ if (is_mac) {
framework_contents = [
"Resources",
"Libraries",
"Helpers",
]
public_deps = [
":electron_framework_libraries",
@ -1037,6 +1032,7 @@ if (is_mac) {
":electron_app_manifest",
":electron_lib",
":packed_resources",
"//components/crash/core/app",
"//content:sandbox_helper_win",
"//electron/buildflags",
"//ui/strings",
@ -1066,6 +1062,11 @@ if (is_mac) {
"shell/browser/resources/win/resource.h",
]
deps += [
"//components/browser_watcher:browser_watcher_client",
"//components/crash/core/app:run_as_crashpad_handler",
]
libs = [
"comctl32.lib",
"uiautomationcore.lib",

View file

@ -14,6 +14,8 @@ static_library("chrome") {
sources = [
"//chrome/browser/browser_process.cc",
"//chrome/browser/browser_process.h",
"//chrome/browser/crash_upload_list/crash_upload_list_crashpad.cc",
"//chrome/browser/crash_upload_list/crash_upload_list_crashpad.h",
"//chrome/browser/devtools/devtools_contents_resizing_strategy.cc",
"//chrome/browser/devtools/devtools_contents_resizing_strategy.h",
"//chrome/browser/devtools/devtools_embedder_message_dispatcher.cc",
@ -55,6 +57,8 @@ static_library("chrome") {
"//chrome/browser/ui/views/autofill/autofill_popup_view_utils.h",
"//chrome/browser/win/chrome_process_finder.cc",
"//chrome/browser/win/chrome_process_finder.h",
"//chrome/child/v8_crashpad_support_win.cc",
"//chrome/child/v8_crashpad_support_win.h",
"//extensions/browser/app_window/size_constraints.cc",
"//extensions/browser/app_window/size_constraints.h",
]

View file

@ -610,6 +610,7 @@ Returns `String` - The current application directory.
* `videos` Directory for a user's videos.
* `logs` Directory for your app's log folder.
* `pepperFlashSystemPlugin` Full path to the system version of the Pepper Flash plugin.
* `crashDumps` Directory where crash dumps are stored.
Returns `String` - A path to a special directory or file associated with `name`. On
failure, an `Error` is thrown.

View file

@ -4,18 +4,13 @@
Process: [Main](../glossary.md#main-process), [Renderer](../glossary.md#renderer-process)
The following is an example of automatically submitting a crash report to a
remote server:
The following is an example of setting up Electron to automatically submit
crash reports to a remote server:
```javascript
const { crashReporter } = require('electron')
crashReporter.start({
productName: 'YourName',
companyName: 'YourCompany',
submitURL: 'https://your-domain.com/url-to-submit',
uploadToServer: true
})
crashReporter.start({ submitURL: 'https://your-domain.com/url-to-submit' })
```
For setting up a server to accept and process crash reports, you can use
@ -30,11 +25,19 @@ Or use a 3rd party hosted solution:
* [Sentry](https://docs.sentry.io/clients/electron)
* [BugSplat](https://www.bugsplat.com/docs/platforms/electron)
Crash reports are saved locally in an application-specific temp directory folder.
For a `productName` of `YourName`, crash reports will be stored in a folder
named `YourName Crashes` inside the temp directory. You can customize this temp
directory location for your app by calling the `app.setPath('temp', '/my/custom/temp')`
API before starting the crash reporter.
Crash reports are stored temporarily before being uploaded in a directory
underneath the app's user data directory (called 'Crashpad' on Windows and Mac,
or 'Crash Reports' on Linux). You can override this directory by calling
`app.setPath('crashDumps', '/path/to/crashes')` before starting the crash
reporter.
On Windows and macOS, Electron uses
[crashpad](https://chromium.googlesource.com/crashpad/crashpad/+/master/README.md)
to monitor and report crashes. On Linux, Electron uses
[breakpad](https://chromium.googlesource.com/breakpad/breakpad/+/master/). This
is an implementation detail driven by Chromium, and it may change in future. In
particular, crashpad is newer and will likely eventually replace breakpad on
all platforms.
## Methods
@ -43,45 +46,68 @@ The `crashReporter` module has the following methods:
### `crashReporter.start(options)`
* `options` Object
* `companyName` String
* `submitURL` String - URL that crash reports will be sent to as POST.
* `productName` String (optional) - Defaults to `app.name`.
* `uploadToServer` Boolean (optional) - Whether crash reports should be sent to the server. Default is `true`.
* `ignoreSystemCrashHandler` Boolean (optional) - Default is `false`.
* `companyName` String (optional) _Deprecated_ - Deprecated alias for
`{ globalExtra: { _companyName: ... } }`.
* `uploadToServer` Boolean (optional) - Whether crash reports should be sent
to the server. If false, crash reports will be collected and stored in the
crashes directory, but not uploaded. Default is `true`.
* `ignoreSystemCrashHandler` Boolean (optional) - If true, crashes generated
in the main process will not be forwarded to the system crash handler.
Default is `false`.
* `rateLimit` Boolean (optional) _macOS_ _Windows_ - If true, limit the
number of crashes uploaded to 1/hour. Default is `false`.
* `compress` Boolean (optional) _macOS_ _Windows_ - If true, crash reports
will be compressed and uploaded with `Content-Encoding: gzip`. Not all
collection servers support compressed payloads. Default is `false`.
* `extra` Record<String, String> (optional) - An object you can define that will be sent along with the
report. Only string properties are sent correctly. Nested objects are not
supported. When using Windows, the property names and values must be fewer than 64 characters.
* `crashesDirectory` String (optional) - Directory to store the crash reports temporarily (only used when the crash reporter is started via `process.crashReporter.start`).
* `extra` Record<String, String> (optional) - Extra string key/value
annotations that will be sent along with crash reports that are generated
in the main process. Only string values are supported. Crashes generated in
child processes will not contain these extra
parameters to crash reports generated from child processes, call
[`addExtraParameter`](#crashreporteraddextraparameterkey-value) from the
child process.
* `globalExtra` Record<String, String> (optional) - Extra string key/value
annotations that will be sent along with any crash reports generated in any
process. These annotations cannot be changed once the crash reporter has
been started. If a key is present in both the global extra parameters and
the process-specific extra parameters, then the global one will take
precedence. By default, `productName` and the app version are included, as
well as the Electron version.
You are required to call this method before using any other `crashReporter` APIs
and in each process (main/renderer) from which you want to collect crash reports.
You can pass different options to `crashReporter.start` when calling from different processes.
This method must be called before using any other `crashReporter` APIs. Once
initialized this way, the crashpad handler collects crashes from all
subsequently created processes. The crash reporter cannot be disabled once
started.
**Note** Child processes created via the `child_process` module will not have access to the Electron modules.
Therefore, to collect crash reports from them, use `process.crashReporter.start` instead. Pass the same options as above
along with an additional one called `crashesDirectory` that should point to a directory to store the crash
reports temporarily. You can test this out by calling `process.crash()` to crash the child process.
This method should be called as early as possible in app startup, preferably
before `app.on('ready')`. If the crash reporter is not initialized at the time
a renderer process is created, then that renderer process will not be monitored
by the crash reporter.
**Note:** If you need send additional/updated `extra` parameters after your
first call `start` you can call `addExtraParameter` on macOS or call `start`
again with the new/updated `extra` parameters on Linux and Windows.
**Note:** You can test out the crash reporter by generating a crash using
`process.crash()`.
**Note:** On macOS and windows, Electron uses a new `crashpad` client for crash collection and reporting.
If you want to enable crash reporting, initializing `crashpad` from the main process using `crashReporter.start` is required
regardless of which process you want to collect crashes from. Once initialized this way, the crashpad handler collects
crashes from all processes. You still have to call `crashReporter.start` from the renderer or child process, otherwise crashes from
them will get reported without `companyName`, `productName` or any of the `extra` information.
**Note:** If you need to send additional/updated `extra` parameters after your
first call `start` you can call `addExtraParameter`.
**Note:** Parameters passed in `extra`, `globalExtra` or set with
`addExtraParameter` have limits on the length of the keys and values. Key names
must be at most 39 bytes long, and values must be no longer than 127 bytes.
Keys with names longer than the maximum will be silently ignored. Key values
longer than the maximum length will be truncated.
**Note:** Calling this method from the renderer process is deprecated.
### `crashReporter.getLastCrashReport()`
Returns [`CrashReport`](structures/crash-report.md):
Returns [`CrashReport`](structures/crash-report.md) - The date and ID of the
last crash report. Only crash reports that have been uploaded will be returned;
even if a crash report is present on disk it will not be returned until it is
uploaded. In the case that there are no uploaded reports, `null` is returned.
Returns the date and ID of the last crash report. Only crash reports that have been uploaded will be returned; even if a crash report is present on disk it will not be returned until it is uploaded. In the case that there are no uploaded reports, `null` is returned.
**Note:** Calling this method from the renderer process is deprecated.
### `crashReporter.getUploadedReports()`
@ -90,43 +116,61 @@ Returns [`CrashReport[]`](structures/crash-report.md):
Returns all uploaded crash reports. Each report contains the date and uploaded
ID.
**Note:** Calling this method from the renderer process is deprecated.
### `crashReporter.getUploadToServer()`
Returns `Boolean` - Whether reports should be submitted to the server. Set through
the `start` method or `setUploadToServer`.
**Note:** This API can only be called from the main process.
**Note:** Calling this method from the renderer process is deprecated.
### `crashReporter.setUploadToServer(uploadToServer)`
* `uploadToServer` Boolean _macOS_ - Whether reports should be submitted to the server.
* `uploadToServer` Boolean - Whether reports should be submitted to the server.
This would normally be controlled by user preferences. This has no effect if
called before `start` is called.
**Note:** This API can only be called from the main process.
**Note:** Calling this method from the renderer process is deprecated.
### `crashReporter.addExtraParameter(key, value)` _macOS_ _Windows_
### `crashReporter.getCrashesDirectory()` _Deprecated_
* `key` String - Parameter key, must be less than 64 characters long.
* `value` String - Parameter value, must be less than 64 characters long.
Returns `String` - The directory where crashes are temporarily stored before being uploaded.
Set an extra parameter to be sent with the crash report. The values
specified here will be sent in addition to any values set via the `extra` option when `start` was called. This API is only available on macOS and windows, if you need to add/update extra parameters on Linux after your first call to `start` you can call `start` again with the updated `extra` options.
**Note:** This method is deprecated, use `app.getPath('crashDumps')` instead.
### `crashReporter.removeExtraParameter(key)` _macOS_ _Windows_
### `crashReporter.addExtraParameter(key, value)`
* `key` String - Parameter key, must be less than 64 characters long.
* `key` String - Parameter key, must be no longer than 39 bytes.
* `value` String - Parameter value, must be no longer than 127 bytes.
Remove a extra parameter from the current set of parameters so that it will not be sent with the crash report.
Set an extra parameter to be sent with the crash report. The values specified
here will be sent in addition to any values set via the `extra` option when
`start` was called.
Parameters added in this fashion (or via the `extra` parameter to
`crashReporter.start`) are specific to the calling process. Adding extra
parameters in the main process will not cause those parameters to be sent along
with crashes from renderer or other child processes. Similarly, adding extra
parameters in a renderer process will not result in those parameters being sent
with crashes that occur in other renderer processes or in the main process.
**Note:** Parameters have limits on the length of the keys and values. Key
names must be no longer than 39 bytes, and values must be no longer than 127
bytes. Keys with names longer than the maximum will be silently ignored. Key
values longer than the maximum length will be truncated.
### `crashReporter.removeExtraParameter(key)`
* `key` String - Parameter key, must be no longer than 39 bytes.
Remove a extra parameter from the current set of parameters. Future crashes
will not include this parameter.
### `crashReporter.getParameters()`
See all of the current parameters being passed to the crash reporter.
### `crashReporter.getCrashesDirectory()`
Returns `String` - The directory where crashes are temporarily stored before being uploaded.
Returns `Record<String, String>` - The current 'extra' parameters of the crash reporter.
## Crash Report Payload

View file

@ -12,8 +12,73 @@ This document uses the following convention to categorize breaking changes:
- **Deprecated:** An API was marked as deprecated. The API will continue to function, but will emit a deprecation warning, and will be removed in a future release.
- **Removed:** An API or feature was removed, and is no longer supported by Electron.
## Planned Breaking API Changes (12.0)
### Removed: `crashReporter` methods in the renderer process
The following `crashReporter` methods are no longer available in the renderer
process:
- `crashReporter.start`
- `crashReporter.getLastCrashReport`
- `crashReporter.getUploadedReports`
- `crashReporter.getUploadToServer`
- `crashReporter.setUploadToServer`
- `crashReporter.getCrashesDirectory`
They should be called only from the main process.
See [#23265](https://github.com/electron/electron/pull/23265) for more details.
## Planned Breaking API Changes (11.0)
## Planned Breaking API Changes (10.0)
### Deprecated: `companyName` argument to `crashReporter.start()`
The `companyName` argument to `crashReporter.start()`, which was previously
required, is now optional, and further, is deprecated. To get the same
behavior in a non-deprecated way, you can pass a `companyName` value in
`globalExtra`.
```js
// Deprecated in Electron 10
crashReporter.start({ companyName: 'Umbrella Corporation' })
// Replace with
crashReporter.start({ globalExtra: { _companyName: 'Umbrella Corporation' } })
```
### Deprecated: `crashReporter.getCrashesDirectory()`
The `crashReporter.getCrashesDirectory` method has been deprecated. Usage
should be replaced by `app.getPath('crashDumps')`.
```js
// Deprecated in Electron 10
crashReporter.getCrashesDirectory()
// Replace with
app.getPath('crashDumps')
```
### Deprecated: `crashReporter` methods in the renderer process
Calling the following `crashReporter` methods from the renderer process is
deprecated:
- `crashReporter.start`
- `crashReporter.getLastCrashReport`
- `crashReporter.getUploadedReports`
- `crashReporter.getUploadToServer`
- `crashReporter.setUploadToServer`
- `crashReporter.getCrashesDirectory`
The only non-deprecated methods remaining in the `crashReporter` module in the
renderer are `addExtraParameter`, `removeExtraParameter` and `getParameters`.
All above methods remain non-deprecated when called from the main process.
See [#23265](https://github.com/electron/electron/pull/23265) for more details.
### Removed: Browser Window Affinity
The `affinity` option when constructing a new `BrowserWindow` will be removed

View file

@ -141,14 +141,13 @@ auto_filenames = {
"lib/common/api/module-list.ts",
"lib/common/api/native-image.js",
"lib/common/api/shell.js",
"lib/common/crash-reporter.js",
"lib/common/define-properties.ts",
"lib/common/electron-binding-setup.ts",
"lib/common/type-utils.ts",
"lib/common/web-view-methods.ts",
"lib/common/webpack-globals-provider.ts",
"lib/renderer/api/context-bridge.ts",
"lib/renderer/api/crash-reporter.js",
"lib/renderer/api/crash-reporter.ts",
"lib/renderer/api/desktop-capturer.ts",
"lib/renderer/api/ipc-renderer.ts",
"lib/renderer/api/remote.js",
@ -193,7 +192,7 @@ auto_filenames = {
"lib/browser/api/browser-view.js",
"lib/browser/api/browser-window.js",
"lib/browser/api/content-tracing.js",
"lib/browser/api/crash-reporter.js",
"lib/browser/api/crash-reporter.ts",
"lib/browser/api/dialog.js",
"lib/browser/api/exports/electron.ts",
"lib/browser/api/global-shortcut.js",
@ -223,7 +222,6 @@ auto_filenames = {
"lib/browser/api/web-contents-view.js",
"lib/browser/api/web-contents.js",
"lib/browser/chrome-extension-shim.js",
"lib/browser/crash-reporter-init.js",
"lib/browser/default-menu.ts",
"lib/browser/desktop-capturer.ts",
"lib/browser/devtools.ts",
@ -244,7 +242,6 @@ auto_filenames = {
"lib/common/api/module-list.ts",
"lib/common/api/native-image.js",
"lib/common/api/shell.js",
"lib/common/crash-reporter.js",
"lib/common/define-properties.ts",
"lib/common/electron-binding-setup.ts",
"lib/common/init.ts",
@ -267,7 +264,6 @@ auto_filenames = {
"lib/common/api/module-list.ts",
"lib/common/api/native-image.js",
"lib/common/api/shell.js",
"lib/common/crash-reporter.js",
"lib/common/define-properties.ts",
"lib/common/electron-binding-setup.ts",
"lib/common/init.ts",
@ -276,7 +272,7 @@ auto_filenames = {
"lib/common/web-view-methods.ts",
"lib/common/webpack-globals-provider.ts",
"lib/renderer/api/context-bridge.ts",
"lib/renderer/api/crash-reporter.js",
"lib/renderer/api/crash-reporter.ts",
"lib/renderer/api/desktop-capturer.ts",
"lib/renderer/api/exports/electron.ts",
"lib/renderer/api/ipc-renderer.ts",
@ -310,7 +306,6 @@ auto_filenames = {
"lib/common/api/module-list.ts",
"lib/common/api/native-image.js",
"lib/common/api/shell.js",
"lib/common/crash-reporter.js",
"lib/common/define-properties.ts",
"lib/common/electron-binding-setup.ts",
"lib/common/init.ts",
@ -318,7 +313,7 @@ auto_filenames = {
"lib/common/type-utils.ts",
"lib/common/webpack-globals-provider.ts",
"lib/renderer/api/context-bridge.ts",
"lib/renderer/api/crash-reporter.js",
"lib/renderer/api/crash-reporter.ts",
"lib/renderer/api/desktop-capturer.ts",
"lib/renderer/api/exports/electron.ts",
"lib/renderer/api/ipc-renderer.ts",

View file

@ -31,6 +31,8 @@ filenames = {
"shell/app/command_line_args.h",
"shell/app/electron_content_client.cc",
"shell/app/electron_content_client.h",
"shell/app/electron_crash_reporter_client.cc",
"shell/app/electron_crash_reporter_client.h",
"shell/app/electron_main_delegate.cc",
"shell/app/electron_main_delegate.h",
"shell/app/electron_main_delegate_mac.h",
@ -51,6 +53,8 @@ filenames = {
"shell/browser/api/electron_api_content_tracing.cc",
"shell/browser/api/electron_api_cookies.cc",
"shell/browser/api/electron_api_cookies.h",
"shell/browser/api/electron_api_crash_reporter.cc",
"shell/browser/api/electron_api_crash_reporter.h",
"shell/browser/api/electron_api_data_pipe_holder.cc",
"shell/browser/api/electron_api_data_pipe_holder.h",
"shell/browser/api/electron_api_debugger.cc",
@ -170,7 +174,6 @@ filenames = {
"shell/browser/electron_javascript_dialog_manager.h",
"shell/browser/electron_navigation_throttle.cc",
"shell/browser/electron_navigation_throttle.h",
"shell/browser/electron_paths.h",
"shell/browser/electron_permission_manager.cc",
"shell/browser/electron_permission_manager.h",
"shell/browser/electron_quota_permission_context.cc",
@ -442,7 +445,6 @@ filenames = {
"shell/common/api/electron_api_clipboard.h",
"shell/common/api/electron_api_clipboard_mac.mm",
"shell/common/api/electron_api_command_line.cc",
"shell/common/api/electron_api_crash_reporter.cc",
"shell/common/api/electron_api_key_weak_map.h",
"shell/common/api/electron_api_native_image.cc",
"shell/common/api/electron_api_native_image.h",
@ -465,22 +467,13 @@ filenames = {
"shell/common/asar/scoped_temporary_file.h",
"shell/common/color_util.cc",
"shell/common/color_util.h",
"shell/common/crash_reporter/crash_reporter.cc",
"shell/common/crash_reporter/crash_reporter.h",
"shell/common/crash_reporter/crash_reporter_linux.cc",
"shell/common/crash_reporter/crash_reporter_linux.h",
"shell/common/crash_reporter/crash_reporter_mac.h",
"shell/common/crash_reporter/crash_reporter_mac.mm",
"shell/common/crash_reporter/crash_reporter_win.cc",
"shell/common/crash_reporter/crash_reporter_win.h",
"shell/common/crash_reporter/linux/crash_dump_handler.cc",
"shell/common/crash_reporter/linux/crash_dump_handler.h",
"shell/common/crash_reporter/win/crash_service_main.cc",
"shell/common/crash_reporter/win/crash_service_main.h",
"shell/common/crash_keys.cc",
"shell/common/crash_keys.h",
"shell/common/electron_command_line.cc",
"shell/common/electron_command_line.h",
"shell/common/electron_constants.cc",
"shell/common/electron_constants.h",
"shell/common/electron_paths.h",
"shell/common/gin_converters/accelerator_converter.cc",
"shell/common/gin_converters/accelerator_converter.h",
"shell/common/gin_converters/blink_converter.cc",
@ -502,6 +495,8 @@ filenames = {
"shell/common/gin_converters/net_converter.cc",
"shell/common/gin_converters/net_converter.h",
"shell/common/gin_converters/std_converter.h",
"shell/common/gin_converters/time_converter.cc",
"shell/common/gin_converters/time_converter.h",
"shell/common/gin_converters/value_converter.cc",
"shell/common/gin_converters/value_converter.h",
"shell/common/gin_helper/arguments.cc",
@ -583,6 +578,7 @@ filenames = {
"shell/renderer/api/context_bridge/render_frame_function_store.h",
"shell/renderer/api/electron_api_context_bridge.cc",
"shell/renderer/api/electron_api_context_bridge.h",
"shell/renderer/api/electron_api_crash_reporter_renderer.cc",
"shell/renderer/api/electron_api_ipc_renderer.cc",
"shell/renderer/api/electron_api_spell_check_client.cc",
"shell/renderer/api/electron_api_spell_check_client.h",

View file

@ -1,12 +0,0 @@
'use strict';
const CrashReporter = require('@electron/internal/common/crash-reporter');
const { crashReporterInit } = require('@electron/internal/browser/crash-reporter-init');
class CrashReporterMain extends CrashReporter {
init (options) {
return crashReporterInit(options);
}
}
module.exports = new CrashReporterMain();

View file

@ -1,47 +1,35 @@
'use strict';
import { app } from 'electron';
const binding = process.electronBinding('crash_reporter');
class CrashReporter {
constructor () {
this.productName = null;
this.crashesDirectory = null;
}
init (options) {
throw new Error('Not implemented');
}
start (options) {
if (options == null) options = {};
start (options: Electron.CrashReporterStartOptions) {
const {
productName,
productName = app.name,
companyName,
extra = {},
globalExtra = {},
ignoreSystemCrashHandler = false,
submitURL,
uploadToServer = true,
rateLimit = false,
compress = false
} = options;
} = options || {};
if (companyName == null) throw new Error('companyName is a required option to crashReporter.start');
if (submitURL == null) throw new Error('submitURL is a required option to crashReporter.start');
const ret = this.init({
submitURL,
productName
});
const appVersion = app.getVersion();
this.productName = ret.productName;
this.crashesDirectory = ret.crashesDirectory;
if (companyName && globalExtra._companyName == null) globalExtra._companyName = companyName;
if (extra._productName == null) extra._productName = ret.productName;
if (extra._companyName == null) extra._companyName = companyName;
if (extra._version == null) extra._version = ret.appVersion;
const globalExtraAmended = {
_productName: productName,
_version: appVersion,
...globalExtra
};
binding.start(submitURL, ret.crashesDirectory, uploadToServer, ignoreSystemCrashHandler, rateLimit, compress, extra);
binding.start(submitURL, uploadToServer,
ignoreSystemCrashHandler, rateLimit, compress, globalExtraAmended, extra, false);
}
getLastCrashReport () {
@ -55,17 +43,12 @@ class CrashReporter {
return (reports.length > 0) ? reports[0] : null;
}
getUploadedReports () {
const crashDir = this.getCrashesDirectory();
if (!crashDir) {
throw new Error('crashReporter has not been started');
}
return binding.getUploadedReports(crashDir);
getUploadedReports (): Electron.CrashReport[] {
return binding.getUploadedReports();
}
getCrashesDirectory () {
return this.crashesDirectory;
return app.getPath('crashDumps');
}
getUploadToServer () {
@ -76,7 +59,7 @@ class CrashReporter {
}
}
setUploadToServer (uploadToServer) {
setUploadToServer (uploadToServer: boolean) {
if (process.type === 'browser') {
return binding.setUploadToServer(uploadToServer);
} else {
@ -84,11 +67,11 @@ class CrashReporter {
}
}
addExtraParameter (key, value) {
addExtraParameter (key: string, value: string) {
binding.addExtraParameter(key, value);
}
removeExtraParameter (key) {
removeExtraParameter (key: string) {
binding.removeExtraParameter(key);
}
@ -97,4 +80,4 @@ class CrashReporter {
}
}
module.exports = CrashReporter;
export default new CrashReporter();

View file

@ -1,25 +0,0 @@
'use strict';
const { app } = require('electron');
const path = require('path');
const getTempDirectory = function () {
try {
return app.getPath('temp');
} catch {
// Delibrately laze-load the os module, this file is on the hot
// path when booting Electron and os takes between 5 - 8ms to load and we do not need it yet
return require('os').tmpdir();
}
};
exports.crashReporterInit = function (options) {
const productName = options.productName || app.name;
const crashesDirectory = path.join(getTempDirectory(), `${productName} Crashes`);
return {
productName,
crashesDirectory,
appVersion: app.getVersion()
};
};

View file

@ -7,7 +7,6 @@ const eventBinding = process.electronBinding('event');
const clipboard = process.electronBinding('clipboard');
const features = process.electronBinding('features');
const { crashReporterInit } = require('@electron/internal/browser/crash-reporter-init');
const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal');
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils');
const guestViewManager = require('@electron/internal/browser/guest-view-manager');
@ -37,10 +36,6 @@ ipcMainInternal.on('ELECTRON_BROWSER_WINDOW_CLOSE', function (event) {
event.returnValue = null;
});
ipcMainUtils.handleSync('ELECTRON_CRASH_REPORTER_INIT', function (event, options) {
return crashReporterInit(options);
});
ipcMainInternal.handle('ELECTRON_BROWSER_GET_LAST_WEB_PREFERENCES', function (event) {
return event.sender.getLastWebPreferences();
});
@ -120,3 +115,23 @@ ipcMainUtils.handleSync('ELECTRON_BROWSER_SANDBOX_LOAD', async function (event)
ipcMainInternal.on('ELECTRON_BROWSER_PRELOAD_ERROR', function (event, preloadPath, error) {
event.sender.emit('preload-error', event, preloadPath, error);
});
ipcMainUtils.handleSync('ELECTRON_CRASH_REPORTER_GET_LAST_CRASH_REPORT', () => {
return electron.crashReporter.getLastCrashReport();
});
ipcMainUtils.handleSync('ELECTRON_CRASH_REPORTER_GET_UPLOADED_REPORTS', () => {
return electron.crashReporter.getUploadedReports();
});
ipcMainUtils.handleSync('ELECTRON_CRASH_REPORTER_GET_UPLOAD_TO_SERVER', () => {
return electron.crashReporter.getUploadToServer();
});
ipcMainUtils.handleSync('ELECTRON_CRASH_REPORTER_SET_UPLOAD_TO_SERVER', (event, uploadToServer) => {
return electron.crashReporter.setUploadToServer(uploadToServer);
});
ipcMainUtils.handleSync('ELECTRON_CRASH_REPORTER_GET_CRASHES_DIRECTORY', () => {
return electron.crashReporter.getCrashesDirectory();
});

View file

@ -1,12 +0,0 @@
'use strict';
const CrashReporter = require('@electron/internal/common/crash-reporter');
const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils');
class CrashReporterRenderer extends CrashReporter {
init (options) {
return ipcRendererUtils.invokeSync('ELECTRON_CRASH_REPORTER_INIT', options);
}
}
module.exports = new CrashReporterRenderer();

View file

@ -0,0 +1,50 @@
import { invokeSync } from '../ipc-renderer-internal-utils';
import { deprecate } from 'electron';
const binding = process.electronBinding('crash_reporter');
export default {
start (options: Electron.CrashReporterStartOptions) {
deprecate.log('crashReporter.start is deprecated in the renderer process. Call it from the main process instead.');
for (const [k, v] of Object.entries(options.extra || {})) {
binding.addExtraParameter(k, String(v));
}
},
getLastCrashReport (): Electron.CrashReport | null {
deprecate.log('crashReporter.getLastCrashReport is deprecated in the renderer process. Call it from the main process instead.');
return invokeSync('ELECTRON_CRASH_REPORTER_GET_LAST_CRASH_REPORT');
},
getUploadedReports () {
deprecate.log('crashReporter.getUploadedReports is deprecated in the renderer process. Call it from the main process instead.');
return invokeSync('ELECTRON_CRASH_REPORTER_GET_UPLOADED_REPORTS');
},
getUploadToServer () {
deprecate.log('crashReporter.getUploadToServer is deprecated in the renderer process. Call it from the main process instead.');
return invokeSync('ELECTRON_CRASH_REPORTER_GET_UPLOAD_TO_SERVER');
},
setUploadToServer (uploadToServer: boolean) {
deprecate.log('crashReporter.setUploadToServer is deprecated in the renderer process. Call it from the main process instead.');
return invokeSync('ELECTRON_CRASH_REPORTER_SET_UPLOAD_TO_SERVER', uploadToServer);
},
getCrashesDirectory () {
deprecate.log('crashReporter.getCrashesDirectory is deprecated in the renderer process. Call it from the main process instead.');
return invokeSync('ELECTRON_CRASH_REPORTER_GET_CRASHES_DIRECTORY');
},
addExtraParameter (key: string, value: string) {
binding.addExtraParameter(key, value);
},
removeExtraParameter (key: string) {
binding.removeExtraParameter(key);
},
getParameters () {
return binding.getParameters();
}
};

View file

@ -89,3 +89,8 @@ feat_add_onclose_to_messageport.patch
fix_account_for_print_preview_disabled_when_printing_to_pdf.patch
web_contents.patch
ui_gtk_public_header.patch
crash_allow_setting_more_options.patch
breakpad_disable_upload_compression.patch
breakpad_treat_node_processes_as_browser_processes.patch
upload_list_add_loadsync_method.patch
breakpad_allow_getting_string_values_for_crash_keys.patch

View file

@ -0,0 +1,29 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jeremy Apthorp <nornagon@nornagon.net>
Date: Tue, 5 May 2020 12:36:39 -0700
Subject: breakpad: allow getting string values for crash keys
Linux is currently recording both crashpad and breakpad keys on linux
(because upstream is experimenting with crashpad-on-linux). We can fetch
the string values for crashpad keys on win/mac, and they're easily
available on linux too, this just exposes them.
Should be upstreamed, or failing that, deleted once crashpad is enabled
on linux. If removing this patch doesn't cause a compile failure, it's
fine to delete!
diff --git a/components/crash/core/common/crash_key.h b/components/crash/core/common/crash_key.h
index 9d193ea962e919d4509eef296c900e47059761f4..225b8ce822a42d31d59bc89aab473710384c3010 100644
--- a/components/crash/core/common/crash_key.h
+++ b/components/crash/core/common/crash_key.h
@@ -212,6 +212,10 @@ class CrashKeyStringCombined : public internal::CrashKeyStringCombinedImpl {
crashpad_key_.Set(value);
}
+ const base::StringPiece value() const {
+ return crashpad_key_.value();
+ }
+
private:
CrashKeyStringBreakpad<MaxLength> breakpad_key_;
crashpad::StringAnnotation<MaxLength> crashpad_key_;

View file

@ -0,0 +1,49 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jeremy Apthorp <nornagon@nornagon.net>
Date: Wed, 29 Apr 2020 16:28:35 -0700
Subject: breakpad: disable upload compression
Our prior implementation of breakpad uploading did not compress files on
upload. In order to maintain that behavior, this disables compression in
//components/crash.
Ideally, this would be made configurable.
diff --git a/components/crash/core/app/breakpad_linux.cc b/components/crash/core/app/breakpad_linux.cc
index 364af690879d79d25d118d5b2fdbaabe21a1c52d..390cca9edc26d3153c8dbc86024cb50097d7ac78 100644
--- a/components/crash/core/app/breakpad_linux.cc
+++ b/components/crash/core/app/breakpad_linux.cc
@@ -1334,6 +1334,7 @@ void ExecUploadProcessOrTerminate(const BreakpadInfo& info,
#else // defined(OS_CHROMEOS)
+ /*
// Compress |dumpfile| with gzip.
const pid_t gzip_child = sys_fork();
if (gzip_child < 0) {
@@ -1377,13 +1378,16 @@ void ExecUploadProcessOrTerminate(const BreakpadInfo& info,
WriteLog(msg, sizeof(msg) - 1);
sys__exit(1);
}
+ */
// The --header argument to wget looks like:
// --header=Content-Encoding: gzip
// --header=Content-Type: multipart/form-data; boundary=XYZ
// where the boundary has two fewer leading '-' chars
+ /*
static const char header_content_encoding[] =
"--header=Content-Encoding: gzip";
+ */
static const char header_msg[] =
"--header=Content-Type: multipart/form-data; boundary=";
const size_t header_content_type_size =
@@ -1412,7 +1416,7 @@ void ExecUploadProcessOrTerminate(const BreakpadInfo& info,
static const char kWgetBinary[] = "/usr/bin/wget";
const char* args[] = {
kWgetBinary,
- header_content_encoding,
+ //header_content_encoding,
header_content_type,
post_file,
upload_url,

View file

@ -0,0 +1,45 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jeremy Apthorp <nornagon@nornagon.net>
Date: Thu, 30 Apr 2020 17:04:13 -0700
Subject: breakpad: treat node processes as browser processes
On Linux, to avoid the need to pass breakpad FDs to child node processes
spawned by child_process.fork(), each child process must re-initialize
breakpad independently, as a "browser" process. This patches
//components/crash so that it will correctly report 'ptype=node' as a
crash annotation.
diff --git a/components/crash/core/app/breakpad_linux.cc b/components/crash/core/app/breakpad_linux.cc
index 390cca9edc26d3153c8dbc86024cb50097d7ac78..2ae2429e6054f2d54e785c49a49fc243469bb72b 100644
--- a/components/crash/core/app/breakpad_linux.cc
+++ b/components/crash/core/app/breakpad_linux.cc
@@ -722,8 +722,13 @@ bool CrashDone(const MinidumpDescriptor& minidump,
log_path[log_path_len] = '\0';
info.log_filename = log_path;
#endif
- info.process_type = "browser";
- info.process_type_length = 7;
+ if (g_is_node) {
+ info.process_type = "node";
+ info.process_type_length = 4;
+ } else {
+ info.process_type = "browser";
+ info.process_type_length = 7;
+ }
info.distro = base::g_linux_distro;
info.distro_length = my_strlen(base::g_linux_distro);
info.upload = upload;
@@ -2050,8 +2055,13 @@ void InitCrashReporter(const std::string& process_type) {
process_type == kWebViewSingleProcessType ||
process_type == kBrowserProcessType ||
#endif
+ process_type == "node" ||
process_type.empty();
+ if (process_type == "node") {
+ g_is_node = true;
+ }
+
std::string upload_url;
if (GetCrashReporterClient()->GetUploadUrl(&upload_url))
SetUploadURL(upload_url);

View file

@ -0,0 +1,201 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jeremy Apthorp <jeremya@chromium.org>
Date: Thu, 30 Apr 2020 10:08:06 -0700
Subject: crash: allow setting more options
This allows the client of //components/crash to set upload url,
rate-limiting, compression and global annotations.
This should be upstreamed.
diff --git a/components/crash/core/app/breakpad_linux.cc b/components/crash/core/app/breakpad_linux.cc
index 192b0a7f137f311abb6a991f997e11f5eba52256..364af690879d79d25d118d5b2fdbaabe21a1c52d 100644
--- a/components/crash/core/app/breakpad_linux.cc
+++ b/components/crash/core/app/breakpad_linux.cc
@@ -103,9 +103,18 @@ namespace {
// while we do have functions to deal with uint64_t's.
uint64_t g_crash_loop_before_time = 0;
#else
-const char kUploadURL[] = "https://clients2.google.com/cr/report";
+const char kDefaultUploadURL[] = "https://clients2.google.com/cr/report";
+char* g_upload_url = nullptr;
#endif
+void SetUploadURL(const std::string& url) {
+ const size_t url_len = url.size() + 1;
+ DCHECK(!g_upload_url);
+ g_upload_url = new char[url_len];
+ strncpy(g_upload_url, url.c_str(), url_len);
+}
+
+bool g_is_node = false;
bool g_is_crash_reporter_enabled = false;
uint64_t g_process_start_time = 0;
pid_t g_pid = 0;
@@ -1398,13 +1407,15 @@ void ExecUploadProcessOrTerminate(const BreakpadInfo& info,
char* status_fd_path =
StringFromPrefixAndUint("/dev/fd/", upload_status_fd, allocator);
+ const char* upload_url = g_upload_url ? g_upload_url : kDefaultUploadURL;
+
static const char kWgetBinary[] = "/usr/bin/wget";
const char* args[] = {
kWgetBinary,
header_content_encoding,
header_content_type,
post_file,
- kUploadURL,
+ upload_url,
"--timeout=10", // Set a timeout so we don't hang forever.
"--tries=1", // Don't retry if the upload fails.
"-O", // Output reply to the file descriptor path.
@@ -2037,6 +2048,10 @@ void InitCrashReporter(const std::string& process_type) {
#endif
process_type.empty();
+ std::string upload_url;
+ if (GetCrashReporterClient()->GetUploadUrl(&upload_url))
+ SetUploadURL(upload_url);
+
if (is_browser_process) {
bool enable_breakpad = GetCrashReporterClient()->GetCollectStatsConsent() ||
GetCrashReporterClient()->IsRunningUnattended();
diff --git a/components/crash/core/app/crash_reporter_client.cc b/components/crash/core/app/crash_reporter_client.cc
index e778f68af30f8c071d1360d06b553cc957160c5b..b7f6ca60ef9c2367989d39e1268bf9f9a517951c 100644
--- a/components/crash/core/app/crash_reporter_client.cc
+++ b/components/crash/core/app/crash_reporter_client.cc
@@ -148,6 +148,17 @@ bool CrashReporterClient::ReportingIsEnforcedByPolicy(bool* breakpad_enabled) {
return false;
}
+bool CrashReporterClient::GetShouldRateLimit() {
+ return true;
+}
+
+bool CrashReporterClient::GetShouldCompressUploads() {
+ return true;
+}
+
+void CrashReporterClient::GetProcessSimpleAnnotations(std::map<std::string, std::string>* annotations) {
+}
+
#if defined(OS_ANDROID)
unsigned int CrashReporterClient::GetCrashDumpPercentage() {
return 100;
@@ -196,6 +207,10 @@ void CrashReporterClient::GetSanitizationInformation(
}
#endif
+bool CrashReporterClient::GetUploadUrl(std::string* url) {
+ return false;
+}
+
bool CrashReporterClient::ShouldMonitorCrashHandlerExpensively() {
return false;
}
diff --git a/components/crash/core/app/crash_reporter_client.h b/components/crash/core/app/crash_reporter_client.h
index 9cc78fc2584061d26fd1e83b3ebf5ada0a12c138..aa63d7b95c37e4a143283450798b8bd500dc17a1 100644
--- a/components/crash/core/app/crash_reporter_client.h
+++ b/components/crash/core/app/crash_reporter_client.h
@@ -5,6 +5,7 @@
#ifndef COMPONENTS_CRASH_CORE_APP_CRASH_REPORTER_CLIENT_H_
#define COMPONENTS_CRASH_CORE_APP_CRASH_REPORTER_CLIENT_H_
+#include <map>
#include <string>
#include "base/strings/string16.h"
@@ -153,6 +154,19 @@ class CrashReporterClient {
// that case, |breakpad_enabled| is set to the value enforced by policies.
virtual bool ReportingIsEnforcedByPolicy(bool* breakpad_enabled);
+ // Returns true if crash uploads should be rate limited. If false, no
+ // throttling will be applied for uploads.
+ virtual bool GetShouldRateLimit();
+
+ // Returns true if crash uploads should be compressed with gzip. If false,
+ // reports will be uploaded uncompressed.
+ virtual bool GetShouldCompressUploads();
+
+ // Allows the client to add or edit global annotations passed to the crashpad
+ // handler.
+ virtual void GetProcessSimpleAnnotations(
+ std::map<std::string, std::string>* annotations);
+
#if defined(OS_ANDROID)
// Used by WebView to sample crashes without generating the unwanted dumps. If
// the returned value is less than 100, crash dumping will be sampled to that
@@ -194,6 +208,9 @@ class CrashReporterClient {
bool* sanitize_stacks);
#endif
+ // Override the default upload url. Returns true if overridden.
+ virtual bool GetUploadUrl(std::string* url);
+
// This method should return true to configure a crash reporter capable of
// monitoring itself for its own crashes to do so, even if self-monitoring
// would be expensive. "Expensive" self-monitoring dedicates an additional
diff --git a/components/crash/core/app/crashpad_mac.mm b/components/crash/core/app/crashpad_mac.mm
index b579521d55860823722df2ee849f6b1628b3c950..f4f71e5174cf8fb706a2f8385252ba877d1a03a7 100644
--- a/components/crash/core/app/crashpad_mac.mm
+++ b/components/crash/core/app/crashpad_mac.mm
@@ -67,6 +67,8 @@ std::map<std::string, std::string> GetProcessSimpleAnnotations() {
} // @autoreleasepool
return process_annotations;
}();
+ CrashReporterClient* crash_reporter_client = GetCrashReporterClient();
+ crash_reporter_client->GetProcessSimpleAnnotations(&annotations);
return annotations;
}
@@ -140,9 +142,17 @@ base::FilePath PlatformCrashpadInitialization(
#else
std::string url;
#endif
+ crash_reporter_client->GetUploadUrl(&url);
std::vector<std::string> arguments;
+ if (!crash_reporter_client->GetShouldRateLimit()) {
+ arguments.push_back("--no-rate-limit");
+ }
+ if (!crash_reporter_client->GetShouldCompressUploads()) {
+ arguments.push_back("--no-upload-gzip");
+ }
+
if (crash_reporter_client->ShouldMonitorCrashHandlerExpensively()) {
arguments.push_back("--monitor-self");
}
diff --git a/components/crash/core/app/crashpad_win.cc b/components/crash/core/app/crashpad_win.cc
index 669f5bea844d75f0e5c34b58994f4cfb8e856af0..8c1fb8fb8f2e02466b51ef08de25b056f8b2052d 100644
--- a/components/crash/core/app/crashpad_win.cc
+++ b/components/crash/core/app/crashpad_win.cc
@@ -84,12 +84,14 @@ base::FilePath PlatformCrashpadInitialization(
std::map<std::string, std::string> process_annotations;
GetPlatformCrashpadAnnotations(&process_annotations);
+ crash_reporter_client->GetProcessSimpleAnnotations(&process_annotations);
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
std::string url = "https://clients2.google.com/cr/report";
#else
std::string url;
#endif
+ crash_reporter_client->GetUploadUrl(&url);
// Allow the crash server to be overridden for testing. If the variable
// isn't present in the environment then the default URL will remain.
@@ -126,6 +128,13 @@ base::FilePath PlatformCrashpadInitialization(
std::vector<std::string> arguments(start_arguments);
+ if (!crash_reporter_client->GetShouldRateLimit()) {
+ arguments.push_back("--no-rate-limit");
+ }
+ if (!crash_reporter_client->GetShouldCompressUploads()) {
+ arguments.push_back("--no-upload-gzip");
+ }
+
if (crash_reporter_client->ShouldMonitorCrashHandlerExpensively()) {
arguments.push_back("--monitor-self");
for (const std::string& start_argument : start_arguments) {

View file

@ -0,0 +1,38 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jeremy Apthorp <jeremya@chromium.org>
Date: Fri, 1 May 2020 18:25:59 -0700
Subject: upload_list: add LoadSync method
This allows synchronous loading of the upload list, which is required by
the crashReporter.getUploadedReports() API. The synchronous version is
deprecated, and this API should be removed once the deprecated behavior
is no longer supported.
diff --git a/components/upload_list/upload_list.cc b/components/upload_list/upload_list.cc
index e39549c9bb552dc19cad3685629406fdbe44c2c7..428b0f486767b630fc42e0e19c8d63bdb2d04cbc 100644
--- a/components/upload_list/upload_list.cc
+++ b/components/upload_list/upload_list.cc
@@ -71,6 +71,10 @@ void UploadList::Load(base::OnceClosure callback) {
base::BindOnce(&UploadList::OnLoadComplete, this));
}
+void UploadList::LoadSync() {
+ uploads_ = LoadUploadList();
+}
+
void UploadList::Clear(const base::Time& begin,
const base::Time& end,
base::OnceClosure callback) {
diff --git a/components/upload_list/upload_list.h b/components/upload_list/upload_list.h
index 20358339df63ae2bb8b955870c5daf51b65f19f7..7cf89626bea8ee9436f15366446f053a479ac438 100644
--- a/components/upload_list/upload_list.h
+++ b/components/upload_list/upload_list.h
@@ -73,6 +73,8 @@ class UploadList : public base::RefCountedThreadSafe<UploadList> {
// overwrite the previously supplied one, and the first will not be called.
void Load(base::OnceClosure callback);
+ void LoadSync();
+
// Clears any data associated with the upload list, where the upload time or
// capture time falls within the given range.
void Clear(const base::Time& begin,

View file

@ -20,7 +20,6 @@ fix_key_gen_apis_are_not_available_in_boringssl.patch
build_modify_js2c_py_to_allow_injection_of_original-fs_and_custom_embedder_js.patch
refactor_allow_embedder_overriding_of_internal_fs_calls.patch
chore_prevent_warn_non_context-aware_native_modules_being_loaded.patch
inherit_electron_crashpad_pipe_name_in_child_process.patch
chore_read_nobrowserglobals_from_global_not_process.patch
build_bring_back_node_with_ltcg_configuration.patch
revert_crypto_add_oaeplabel_option.patch

View file

@ -1,22 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Cheng Zhao <zcbenz@gmail.com>
Date: Tue, 4 Jun 2019 17:42:11 +0900
Subject: Inherit ELECTRON_CRASHPAD_PIPE_NAME in child process
This is required for crashReporter to work correctly in node process.
diff --git a/lib/child_process.js b/lib/child_process.js
index 5ed166e1ed76b830c2d97f8170a4a72841201537..c919527a7c06f87fc5220cb7234368c4686563a3 100644
--- a/lib/child_process.js
+++ b/lib/child_process.js
@@ -115,6 +115,10 @@ function fork(modulePath /* , args, options */) {
options.env = Object.create(options.env || process.env)
options.env.ELECTRON_RUN_AS_NODE = 1;
+ if (process.platform === 'win32') {
+ options.env.ELECTRON_CRASHPAD_PIPE_NAME = process.env.ELECTRON_CRASHPAD_PIPE_NAME;
+ }
+
if (!options.execPath && process.type && process.platform == 'darwin') {
options.execPath = process.helperExecPath;
}

View file

@ -3,11 +3,14 @@ Electron.app/Contents/
Electron.app/Contents/Frameworks/
Electron.app/Contents/Frameworks/Electron Framework.framework/
Electron.app/Contents/Frameworks/Electron Framework.framework/Electron Framework
Electron.app/Contents/Frameworks/Electron Framework.framework/Helpers
Electron.app/Contents/Frameworks/Electron Framework.framework/Libraries
Electron.app/Contents/Frameworks/Electron Framework.framework/Resources
Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/
Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/
Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Electron Framework
Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Helpers/
Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Helpers/chrome_crashpad_handler
Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Libraries/
Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Libraries/libEGL.dylib
Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Libraries/libGLESv2.dylib
@ -31,7 +34,6 @@ Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resourc
Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/ca.lproj/locale.pak
Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/chrome_100_percent.pak
Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/chrome_200_percent.pak
Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/crashpad_handler
Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/cs.lproj/
Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/cs.lproj/locale.pak
Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/da.lproj/

View file

@ -3,6 +3,7 @@ Electron.app/Contents/
Electron.app/Contents/Frameworks/
Electron.app/Contents/Frameworks/Electron Framework.framework/
Electron.app/Contents/Frameworks/Electron Framework.framework/Electron Framework
Electron.app/Contents/Frameworks/Electron Framework.framework/Helpers
Electron.app/Contents/Frameworks/Electron Framework.framework/Libraries
Electron.app/Contents/Frameworks/Electron Framework.framework/Resources
Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/

View file

@ -19,7 +19,7 @@
#include "electron/buildflags/buildflags.h"
#include "extensions/common/constants.h"
#include "ppapi/buildflags/buildflags.h"
#include "shell/browser/electron_paths.h"
#include "shell/common/electron_paths.h"
#include "shell/common/options_switches.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"

View file

@ -0,0 +1,209 @@
// Copyright 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.
#include "shell/app/electron_crash_reporter_client.h"
#include <map>
#include <memory>
#include <string>
#include "base/command_line.h"
#include "base/environment.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/strings/string_split.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "chrome/common/chrome_paths.h"
#include "components/crash/core/common/crash_keys.h"
#include "components/upload_list/crash_upload_list.h"
#include "content/public/common/content_switches.h"
#include "electron/electron_version.h"
#include "services/service_manager/embedder/switches.h"
#include "shell/common/electron_paths.h"
#if defined(OS_POSIX) && !defined(OS_MACOSX)
#include "components/version_info/version_info_values.h"
#endif
#if defined(OS_POSIX)
#include "base/debug/dump_without_crashing.h"
#endif
namespace {
ElectronCrashReporterClient* Instance() {
static base::NoDestructor<ElectronCrashReporterClient> crash_client;
return crash_client.get();
}
} // namespace
// static
void ElectronCrashReporterClient::Create() {
crash_reporter::SetCrashReporterClient(Instance());
// By setting the BREAKPAD_DUMP_LOCATION environment variable, an alternate
// location to write crash dumps can be set.
std::unique_ptr<base::Environment> env(base::Environment::Create());
std::string alternate_crash_dump_location;
base::FilePath crash_dumps_dir_path;
if (env->GetVar("BREAKPAD_DUMP_LOCATION", &alternate_crash_dump_location)) {
crash_dumps_dir_path =
base::FilePath::FromUTF8Unsafe(alternate_crash_dump_location);
}
if (!crash_dumps_dir_path.empty()) {
base::ThreadRestrictions::ScopedAllowIO allow_io;
base::PathService::Override(electron::DIR_CRASH_DUMPS,
crash_dumps_dir_path);
}
}
// static
ElectronCrashReporterClient* ElectronCrashReporterClient::Get() {
return Instance();
}
void ElectronCrashReporterClient::SetCollectStatsConsent(bool upload_allowed) {
collect_stats_consent_ = upload_allowed;
}
void ElectronCrashReporterClient::SetUploadUrl(const std::string& url) {
upload_url_ = url;
}
void ElectronCrashReporterClient::SetShouldRateLimit(bool rate_limit) {
rate_limit_ = rate_limit;
}
void ElectronCrashReporterClient::SetShouldCompressUploads(bool compress) {
compress_uploads_ = compress;
}
void ElectronCrashReporterClient::SetGlobalAnnotations(
const std::map<std::string, std::string>& annotations) {
global_annotations_ = annotations;
}
ElectronCrashReporterClient::ElectronCrashReporterClient() {}
ElectronCrashReporterClient::~ElectronCrashReporterClient() {}
#if defined(OS_LINUX)
void ElectronCrashReporterClient::SetCrashReporterClientIdFromGUID(
const std::string& client_guid) {
crash_keys::SetMetricsClientIdFromGUID(client_guid);
}
void ElectronCrashReporterClient::GetProductNameAndVersion(
const char** product_name,
const char** version) {
DCHECK(product_name);
DCHECK(version);
*product_name = ELECTRON_PRODUCT_NAME;
*version = ELECTRON_VERSION_STRING;
}
void ElectronCrashReporterClient::GetProductNameAndVersion(
std::string* product_name,
std::string* version,
std::string* channel) {
const char* c_product_name;
const char* c_version;
GetProductNameAndVersion(&c_product_name, &c_version);
*product_name = c_product_name;
*version = c_version;
*channel = "";
}
base::FilePath ElectronCrashReporterClient::GetReporterLogFilename() {
return base::FilePath(CrashUploadList::kReporterLogFilename);
}
#endif
#if defined(OS_WIN)
void ElectronCrashReporterClient::GetProductNameAndVersion(
const base::string16& exe_path,
base::string16* product_name,
base::string16* version,
base::string16* special_build,
base::string16* channel_name) {
*product_name = base::UTF8ToUTF16(ELECTRON_PRODUCT_NAME);
*version = base::UTF8ToUTF16(ELECTRON_VERSION_STRING);
}
#endif
#if defined(OS_WIN)
bool ElectronCrashReporterClient::GetCrashDumpLocation(
base::string16* crash_dir_str) {
base::FilePath crash_dir;
if (!base::PathService::Get(electron::DIR_CRASH_DUMPS, &crash_dir))
return false;
*crash_dir_str = crash_dir.value();
return true;
}
#else
bool ElectronCrashReporterClient::GetCrashDumpLocation(
base::FilePath* crash_dir) {
return base::PathService::Get(electron::DIR_CRASH_DUMPS, crash_dir);
}
#endif
#if defined(OS_MACOSX) || defined(OS_LINUX)
bool ElectronCrashReporterClient::GetCrashMetricsLocation(
base::FilePath* metrics_dir) {
return base::PathService::Get(electron::DIR_USER_DATA, metrics_dir);
}
#endif // OS_MACOSX || OS_LINUX
bool ElectronCrashReporterClient::IsRunningUnattended() {
return false;
}
bool ElectronCrashReporterClient::GetCollectStatsConsent() {
return collect_stats_consent_;
}
#if defined(OS_MACOSX)
bool ElectronCrashReporterClient::ReportingIsEnforcedByPolicy(
bool* breakpad_enabled) {
return false;
}
#endif
bool ElectronCrashReporterClient::GetShouldRateLimit() {
return rate_limit_;
}
bool ElectronCrashReporterClient::GetShouldCompressUploads() {
return compress_uploads_;
}
void ElectronCrashReporterClient::GetProcessSimpleAnnotations(
std::map<std::string, std::string>* annotations) {
*annotations = global_annotations_;
(*annotations)["prod"] = ELECTRON_PRODUCT_NAME;
(*annotations)["ver"] = ELECTRON_VERSION_STRING;
}
#if defined(OS_LINUX) || defined(OS_MACOSX)
bool ElectronCrashReporterClient::ShouldMonitorCrashHandlerExpensively() {
return false;
}
#endif // OS_LINUX
bool ElectronCrashReporterClient::GetUploadUrl(std::string* url) {
*url = upload_url_;
return true;
}
bool ElectronCrashReporterClient::EnableBreakpadForProcess(
const std::string& process_type) {
return process_type == switches::kRendererProcess ||
process_type == switches::kPpapiPluginProcess ||
process_type == service_manager::switches::kZygoteProcess ||
process_type == switches::kGpuProcess ||
process_type == switches::kUtilityProcess || process_type == "node";
}

View file

@ -0,0 +1,96 @@
// Copyright 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.
#ifndef SHELL_APP_ELECTRON_CRASH_REPORTER_CLIENT_H_
#define SHELL_APP_ELECTRON_CRASH_REPORTER_CLIENT_H_
#include <map>
#include <string>
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "base/no_destructor.h"
#include "build/build_config.h"
#include "components/crash/core/app/crash_reporter_client.h"
class ElectronCrashReporterClient : public crash_reporter::CrashReporterClient {
public:
static void Create();
static ElectronCrashReporterClient* Get();
void SetCollectStatsConsent(bool upload_allowed);
void SetUploadUrl(const std::string& url);
void SetShouldRateLimit(bool rate_limit);
void SetShouldCompressUploads(bool compress_uploads);
void SetGlobalAnnotations(
const std::map<std::string, std::string>& annotations);
// crash_reporter::CrashReporterClient implementation.
#if defined(OS_LINUX)
void SetCrashReporterClientIdFromGUID(
const std::string& client_guid) override;
void GetProductNameAndVersion(const char** product_name,
const char** version) override;
void GetProductNameAndVersion(std::string* product_name,
std::string* version,
std::string* channel) override;
base::FilePath GetReporterLogFilename() override;
#endif
#if defined(OS_WIN)
void GetProductNameAndVersion(const base::string16& exe_path,
base::string16* product_name,
base::string16* version,
base::string16* special_build,
base::string16* channel_name) override;
#endif
#if defined(OS_WIN)
bool GetCrashDumpLocation(base::string16* crash_dir) override;
#else
bool GetCrashDumpLocation(base::FilePath* crash_dir) override;
#endif
#if defined(OS_MACOSX) || defined(OS_LINUX)
bool GetCrashMetricsLocation(base::FilePath* metrics_dir) override;
#endif
bool IsRunningUnattended() override;
bool GetCollectStatsConsent() override;
bool GetShouldRateLimit() override;
bool GetShouldCompressUploads() override;
void GetProcessSimpleAnnotations(
std::map<std::string, std::string>* annotations) override;
#if defined(OS_MACOSX)
bool ReportingIsEnforcedByPolicy(bool* breakpad_enabled) override;
#endif
#if defined(OS_MACOSX) || defined(OS_LINUX)
bool ShouldMonitorCrashHandlerExpensively() override;
#endif
bool EnableBreakpadForProcess(const std::string& process_type) override;
bool GetUploadUrl(std::string* url) override;
private:
friend class base::NoDestructor<ElectronCrashReporterClient>;
std::string upload_url_;
bool collect_stats_consent_;
bool rate_limit_ = false;
bool compress_uploads_ = false;
std::map<std::string, std::string> global_annotations_;
ElectronCrashReporterClient();
~ElectronCrashReporterClient() override;
DISALLOW_COPY_AND_ASSIGN(ElectronCrashReporterClient);
};
#endif // SHELL_APP_ELECTRON_CRASH_REPORTER_CLIENT_H_

View file

@ -7,6 +7,8 @@
#include <algorithm>
#include <cstdlib>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#if defined(OS_WIN)
@ -21,11 +23,15 @@
#include "base/process/launch.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/windows_version.h"
#include "components/browser_watcher/exit_code_watcher_win.h"
#include "components/crash/core/app/crash_switches.h"
#include "components/crash/core/app/run_as_crashpad_handler_win.h"
#include "content/public/app/sandbox_helper_win.h"
#include "sandbox/win/src/sandbox_types.h"
#include "shell/app/command_line_args.h"
#include "shell/app/electron_main_delegate.h"
#include "shell/common/crash_reporter/win/crash_service_main.h"
#include "third_party/crashpad/crashpad/util/win/initial_client_data.h"
#elif defined(OS_LINUX) // defined(OS_WIN)
#include <unistd.h>
#include <cstdio>
@ -51,6 +57,13 @@
namespace {
#if defined(OS_WIN)
// Redefined here so we don't have to introduce a dependency on //content
// from //electron:electron_app
const char kUserDataDir[] = "user-data-dir";
const char kProcessType[] = "type";
#endif
ALLOW_UNUSED_TYPE bool IsEnvSet(const char* name) {
#if defined(OS_WIN)
size_t required_size;
@ -138,10 +151,49 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t* cmd, int) {
#endif
base::CommandLine::Init(argv.size(), argv.data());
const base::CommandLine& cmd_line = *base::CommandLine::ForCurrentProcess();
if (cmd_line.GetSwitchValueASCII("type") ==
crash_reporter::kCrashpadProcess) {
return crash_service::Main(&argv);
const base::CommandLine* command_line =
base::CommandLine::ForCurrentProcess();
const std::string process_type =
command_line->GetSwitchValueASCII(kProcessType);
if (process_type == crash_reporter::switches::kCrashpadHandler) {
// Check if we should monitor the exit code of this process
std::unique_ptr<browser_watcher::ExitCodeWatcher> exit_code_watcher;
// Retrieve the client process from the command line
crashpad::InitialClientData initial_client_data;
if (initial_client_data.InitializeFromString(
command_line->GetSwitchValueASCII("initial-client-data"))) {
// Setup exit code watcher to monitor the parent process
HANDLE duplicate_handle = INVALID_HANDLE_VALUE;
if (DuplicateHandle(
::GetCurrentProcess(), initial_client_data.client_process(),
::GetCurrentProcess(), &duplicate_handle,
PROCESS_QUERY_INFORMATION, FALSE, DUPLICATE_SAME_ACCESS)) {
base::Process parent_process(duplicate_handle);
exit_code_watcher =
std::make_unique<browser_watcher::ExitCodeWatcher>();
if (exit_code_watcher->Initialize(std::move(parent_process))) {
exit_code_watcher->StartWatching();
}
}
}
// The handler process must always be passed the user data dir on the
// command line.
DCHECK(command_line->HasSwitch(kUserDataDir));
base::FilePath user_data_dir =
command_line->GetSwitchValuePath(kUserDataDir);
int crashpad_status = crash_reporter::RunAsCrashpadHandler(
*command_line, user_data_dir, kProcessType, kUserDataDir);
if (crashpad_status != 0 && exit_code_watcher) {
// Crashpad failed to initialize, explicitly stop the exit code watcher
// so the crashpad-handler process can exit with an error
exit_code_watcher->StopWatching();
}
return crashpad_status;
}
if (!electron::CheckCommandLineArguments(arguments.argc, arguments.argv))

View file

@ -15,11 +15,16 @@
#include "base/command_line.h"
#include "base/debug/stack_trace.h"
#include "base/environment.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/mac/bundle_locations.h"
#include "base/path_service.h"
#include "base/strings/string_split.h"
#include "chrome/common/chrome_paths.h"
#include "components/content_settings/core/common/content_settings_pattern.h"
#include "components/crash/core/app/crashpad.h"
#include "components/crash/core/common/crash_key.h"
#include "components/crash/core/common/crash_keys.h"
#include "content/public/common/content_switches.h"
#include "electron/buildflags/buildflags.h"
#include "extensions/common/constants.h"
@ -28,10 +33,14 @@
#include "services/service_manager/sandbox/switches.h"
#include "services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h"
#include "shell/app/electron_content_client.h"
#include "shell/app/electron_crash_reporter_client.h"
#include "shell/browser/api/electron_api_crash_reporter.h"
#include "shell/browser/electron_browser_client.h"
#include "shell/browser/electron_gpu_client.h"
#include "shell/browser/feature_list.h"
#include "shell/browser/relauncher.h"
#include "shell/common/crash_keys.h"
#include "shell/common/electron_paths.h"
#include "shell/common/options_switches.h"
#include "shell/renderer/electron_renderer_client.h"
#include "shell/renderer/electron_sandboxed_renderer_client.h"
@ -46,9 +55,13 @@
#if defined(OS_WIN)
#include "base/win/win_util.h"
#if defined(_WIN64)
#include "shell/common/crash_reporter/crash_reporter_win.h"
#include "chrome/child/v8_crashpad_support_win.h"
#endif
#if defined(OS_LINUX)
#include "components/crash/core/app/breakpad_linux.h"
#include "v8/include/v8-wasm-trap-handler-posix.h"
#include "v8/include/v8.h"
#endif
namespace electron {
@ -71,7 +84,7 @@ bool IsSandboxEnabled(base::CommandLine* command_line) {
// and resources loaded.
bool SubprocessNeedsResourceBundle(const std::string& process_type) {
return
#if defined(OS_POSIX) && !defined(OS_MACOSX)
#if defined(OS_LINUX)
// The zygote process opens the resources for the renderers.
process_type == service_manager::switches::kZygoteProcess ||
#endif
@ -98,6 +111,41 @@ void InvalidParameterHandler(const wchar_t*,
} // namespace
// TODO(nornagon): move path provider overriding to its own file in
// shell/common
namespace electron {
bool GetDefaultCrashDumpsPath(base::FilePath* path) {
base::FilePath cur;
if (!base::PathService::Get(DIR_USER_DATA, &cur))
return false;
#if defined(OS_MACOSX) || defined(OS_WIN)
cur = cur.Append(FILE_PATH_LITERAL("Crashpad"));
#else
cur = cur.Append(FILE_PATH_LITERAL("Crash Reports"));
#endif
// TODO(bauerb): http://crbug.com/259796
base::ThreadRestrictions::ScopedAllowIO allow_io;
if (!base::PathExists(cur) && !base::CreateDirectory(cur))
return false;
*path = cur;
return true;
}
bool ElectronPathProvider(int key, base::FilePath* path) {
if (key == DIR_CRASH_DUMPS) {
return GetDefaultCrashDumpsPath(path);
}
return false;
}
void RegisterPathProvider() {
base::PathService::RegisterProvider(ElectronPathProvider, PATH_START,
PATH_END);
}
} // namespace electron
void LoadResourceBundle(const std::string& locale) {
const bool initialized = ui::ResourceBundle::HasSharedInstance();
if (initialized)
@ -134,9 +182,7 @@ bool ElectronMainDelegate::BasicStartupComplete(int* exit_code) {
logging::LoggingSettings settings;
#if defined(OS_WIN)
#if defined(_WIN64)
crash_reporter::CrashReporterWin::SetUnhandledExceptionFilter();
#endif
v8_crashpad_support::SetUp();
// On Windows the terminal returns immediately, so we add a new line to
// prevent output in the same line as the prompt.
@ -185,6 +231,8 @@ bool ElectronMainDelegate::BasicStartupComplete(int* exit_code) {
tracing::TracingSamplerProfiler::CreateOnMainThread();
chrome::RegisterPathProvider();
electron::RegisterPathProvider();
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
ContentSettingsPattern::SetNonWildcardDomainNonPortSchemes(
kNonWildcardDomainNonPortSchemes, kNonWildcardDomainNonPortSchemesSize);
@ -272,6 +320,10 @@ void ElectronMainDelegate::PreSandboxStartup() {
std::string process_type =
command_line->GetSwitchValueASCII(::switches::kProcessType);
#if !defined(MAS_BUILD)
crash_reporter::InitializeCrashKeys();
#endif
// Initialize ResourceBundle which handles files loaded from external
// sources. The language should have been passed in to us from the
// browser process as a command line flag.
@ -280,17 +332,40 @@ void ElectronMainDelegate::PreSandboxStartup() {
LoadResourceBundle(locale);
}
// Only append arguments for browser process.
if (!IsBrowserProcess(command_line))
return;
#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(MAS_BUILD))
// In the main process, we wait for JS to call crashReporter.start() before
// initializing crashpad. If we're in the renderer, we want to initialize it
// immediately at boot.
if (!process_type.empty()) {
ElectronCrashReporterClient::Create();
crash_reporter::InitializeCrashpad(false, process_type);
}
#endif
// Allow file:// URIs to read other file:// URIs by default.
command_line->AppendSwitch(::switches::kAllowFileAccessFromFiles);
#if defined(OS_LINUX)
if (process_type != service_manager::switches::kZygoteProcess &&
!process_type.empty()) {
ElectronCrashReporterClient::Create();
breakpad::InitCrashReporter(process_type);
}
#endif
#if !defined(MAS_BUILD)
crash_keys::SetCrashKeysFromCommandLine(*command_line);
crash_keys::SetPlatformCrashKey();
#endif
if (IsBrowserProcess(command_line)) {
// Only append arguments for browser process.
// Allow file:// URIs to read other file:// URIs by default.
command_line->AppendSwitch(::switches::kAllowFileAccessFromFiles);
#if defined(OS_MACOSX)
// Enable AVFoundation.
command_line->AppendSwitch("enable-avfoundation");
// Enable AVFoundation.
command_line->AppendSwitch("enable-avfoundation");
#endif
}
}
void ElectronMainDelegate::PreCreateMainMessageLoop() {
@ -350,4 +425,20 @@ bool ElectronMainDelegate::ShouldLockSchemeRegistry() {
return false;
}
#if defined(OS_LINUX)
void ElectronMainDelegate::ZygoteForked() {
// Needs to be called after we have DIR_USER_DATA. BrowserMain sets
// this up for the browser process in a different manner.
ElectronCrashReporterClient::Create();
const base::CommandLine* command_line =
base::CommandLine::ForCurrentProcess();
std::string process_type =
command_line->GetSwitchValueASCII(::switches::kProcessType);
breakpad::InitCrashReporter(process_type);
// Reset the command line for the newly spawned process.
crash_keys::SetCrashKeysFromCommandLine(*command_line);
}
#endif // defined(OS_LINUX)
} // namespace electron

View file

@ -41,6 +41,9 @@ class ElectronMainDelegate : public content::ContentMainDelegate {
const content::MainFunctionParams& main_function_params) override;
bool ShouldCreateFeatureList() override;
bool ShouldLockSchemeRegistry() override;
#if defined(OS_LINUX)
void ZygoteForked() override;
#endif
private:
#if defined(OS_MACOSX)

View file

@ -4,33 +4,43 @@
#include "shell/app/node_main.h"
#include <map>
#include <memory>
#include <string>
#include <unordered_set>
#include <utility>
#include <vector>
#include "base/base_switches.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/crash/core/app/crashpad.h"
#include "content/public/common/content_switches.h"
#include "electron/electron_version.h"
#include "gin/array_buffer.h"
#include "gin/public/isolate_holder.h"
#include "gin/v8_initializer.h"
#include "shell/app/electron_crash_reporter_client.h"
#include "shell/app/uv_task_runner.h"
#include "shell/browser/api/electron_api_crash_reporter.h"
#include "shell/browser/javascript_environment.h"
#include "shell/browser/node_debugger.h"
#include "shell/common/api/electron_bindings.h"
#include "shell/common/crash_reporter/crash_reporter.h"
#include "shell/common/crash_keys.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/node_bindings.h"
#include "shell/common/node_includes.h"
#if defined(_WIN64)
#include "shell/common/crash_reporter/crash_reporter_win.h"
#if defined(OS_LINUX)
#include "components/crash/core/app/breakpad_linux.h"
#endif
#if defined(OS_WIN)
#include "chrome/child/v8_crashpad_support_win.h"
#endif
namespace {
@ -78,19 +88,64 @@ void SetNodeCliFlags() {
namespace electron {
#if !defined(OS_LINUX)
void AddExtraParameter(const std::string& key, const std::string& value) {
crash_reporter::CrashReporter::GetInstance()->AddExtraParameter(key, value);
}
#if defined(OS_LINUX)
void CrashReporterStart(gin_helper::Dictionary options) {
std::string submit_url;
bool upload_to_server = true;
bool ignore_system_crash_handler = false;
bool rate_limit = false;
bool compress = false;
std::map<std::string, std::string> global_extra;
std::map<std::string, std::string> extra;
options.Get("submitURL", &submit_url);
options.Get("uploadToServer", &upload_to_server);
options.Get("ignoreSystemCrashHandler", &ignore_system_crash_handler);
options.Get("rateLimit", &rate_limit);
options.Get("compress", &compress);
options.Get("extra", &extra);
options.Get("globalExtra", &global_extra);
void RemoveExtraParameter(const std::string& key) {
crash_reporter::CrashReporter::GetInstance()->RemoveExtraParameter(key);
std::string product_name;
if (options.Get("productName", &product_name))
global_extra["_productName"] = product_name;
std::string company_name;
if (options.Get("companyName", &company_name))
global_extra["_companyName"] = company_name;
api::crash_reporter::Start(submit_url, upload_to_server,
ignore_system_crash_handler, rate_limit, compress,
global_extra, extra, true);
}
#endif
v8::Local<v8::Value> GetParameters(v8::Isolate* isolate) {
std::map<std::string, std::string> keys;
#if !defined(MAS_BUILD)
electron::crash_keys::GetCrashKeys(&keys);
#endif
return gin::ConvertToV8(isolate, keys);
}
int NodeMain(int argc, char* argv[]) {
base::CommandLine::Init(argc, argv);
#if defined(OS_WIN)
v8_crashpad_support::SetUp();
#endif
#if !defined(MAS_BUILD)
ElectronCrashReporterClient::Create();
#endif
#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(MAS_BUILD))
crash_reporter::InitializeCrashpad(false, "node");
#endif
#if !defined(MAS_BUILD)
crash_keys::SetCrashKeysFromCommandLine(
*base::CommandLine::ForCurrentProcess());
crash_keys::SetPlatformCrashKey();
#endif
int exit_code = 1;
{
// Feed gin::PerIsolateData with a task runner.
@ -104,10 +159,6 @@ int NodeMain(int argc, char* argv[]) {
feature_list->InitializeFromCommandLine("", "");
base::FeatureList::SetInstance(std::move(feature_list));
#if defined(_WIN64)
crash_reporter::CrashReporterWin::SetUnhandledExceptionFilter();
#endif
// We do not want to double-set the error level and promise rejection
// callback.
node::g_standalone_mode = false;
@ -159,16 +210,18 @@ int NodeMain(int argc, char* argv[]) {
#endif
process.SetMethod("crash", &ElectronBindings::Crash);
// Setup process.crashReporter.start in child node processes
// Setup process.crashReporter in child node processes
gin_helper::Dictionary reporter = gin::Dictionary::CreateEmpty(isolate);
reporter.SetMethod("start",
&crash_reporter::CrashReporter::StartInstance);
#if !defined(OS_LINUX)
reporter.SetMethod("addExtraParameter", &AddExtraParameter);
reporter.SetMethod("removeExtraParameter", &RemoveExtraParameter);
#if defined(OS_LINUX)
reporter.SetMethod("start", &CrashReporterStart);
#endif
reporter.SetMethod("getParameters", &GetParameters);
reporter.SetMethod("addExtraParameter",
&electron::crash_keys::SetCrashKey);
reporter.SetMethod("removeExtraParameter",
&electron::crash_keys::ClearCrashKey);
process.Set("crashReporter", reporter);
gin_helper::Dictionary versions;

View file

@ -39,11 +39,11 @@
#include "shell/browser/api/gpuinfo_manager.h"
#include "shell/browser/electron_browser_context.h"
#include "shell/browser/electron_browser_main_parts.h"
#include "shell/browser/electron_paths.h"
#include "shell/browser/login_handler.h"
#include "shell/browser/relauncher.h"
#include "shell/common/application_info.h"
#include "shell/common/electron_command_line.h"
#include "shell/common/electron_paths.h"
#include "shell/common/gin_converters/callback_converter.h"
#include "shell/common/gin_converters/file_path_converter.h"
#include "shell/common/gin_converters/gurl_converter.h"
@ -403,6 +403,8 @@ int GetPathConstant(const std::string& name) {
return DIR_USER_CACHE;
else if (name == "logs")
return DIR_APP_LOGS;
else if (name == "crashDumps")
return DIR_CRASH_DUMPS;
else if (name == "home")
return base::DIR_HOME;
else if (name == "temp")

View file

@ -6,7 +6,7 @@
#include "base/path_service.h"
#include "shell/browser/api/electron_api_app.h"
#include "shell/browser/electron_paths.h"
#include "shell/common/electron_paths.h"
#import <Cocoa/Cocoa.h>

View file

@ -9,28 +9,12 @@
#include "shell/browser/native_window.h"
#include "shell/browser/window_list.h"
#include "shell/common/gin_converters/callback_converter.h"
#include "shell/common/gin_converters/time_converter.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/gin_helper/event_emitter_caller.h"
#include "shell/common/gin_helper/object_template_builder.h"
#include "shell/common/node_includes.h"
namespace gin {
template <>
struct Converter<base::Time> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const base::Time& val) {
v8::MaybeLocal<v8::Value> date =
v8::Date::New(isolate->GetCurrentContext(), val.ToJsTime());
if (date.IsEmpty())
return v8::Null(isolate);
else
return date.ToLocalChecked();
}
};
} // namespace gin
namespace electron {
namespace api {

View file

@ -0,0 +1,216 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "shell/browser/api/electron_api_crash_reporter.h"
#include <limits>
#include <map>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/no_destructor.h"
#include "base/path_service.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "chrome/browser/crash_upload_list/crash_upload_list_crashpad.h"
#include "chrome/common/chrome_paths.h"
#include "components/crash/core/app/crashpad.h"
#include "components/crash/core/common/crash_key.h"
#include "components/upload_list/crash_upload_list.h"
#include "components/upload_list/text_log_upload_list.h"
#include "content/public/common/content_switches.h"
#include "gin/arguments.h"
#include "gin/data_object_builder.h"
#include "services/service_manager/embedder/switches.h"
#include "shell/app/electron_crash_reporter_client.h"
#include "shell/common/crash_keys.h"
#include "shell/common/electron_paths.h"
#include "shell/common/gin_converters/callback_converter.h"
#include "shell/common/gin_converters/file_path_converter.h"
#include "shell/common/gin_converters/time_converter.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/node_includes.h"
#include "third_party/crashpad/crashpad/client/crashpad_info.h"
#if defined(OS_LINUX)
#include "components/crash/core/app/breakpad_linux.h"
#include "v8/include/v8-wasm-trap-handler-posix.h"
#include "v8/include/v8.h"
#endif
namespace {
#if defined(OS_LINUX)
std::map<std::string, std::string>& GetGlobalCrashKeysMutable() {
static base::NoDestructor<std::map<std::string, std::string>>
global_crash_keys;
return *global_crash_keys;
}
#endif // defined(OS_LINUX)
bool g_crash_reporter_initialized = false;
} // namespace
namespace electron {
namespace api {
namespace crash_reporter {
bool IsCrashReporterEnabled() {
return g_crash_reporter_initialized;
}
#if defined(OS_LINUX)
const std::map<std::string, std::string>& GetGlobalCrashKeys() {
return GetGlobalCrashKeysMutable();
}
#endif
void Start(const std::string& submit_url,
bool upload_to_server,
bool ignore_system_crash_handler,
bool rate_limit,
bool compress,
const std::map<std::string, std::string>& global_extra,
const std::map<std::string, std::string>& extra,
bool is_node_process) {
#if !defined(MAS_BUILD)
if (g_crash_reporter_initialized)
return;
g_crash_reporter_initialized = true;
ElectronCrashReporterClient::Create();
ElectronCrashReporterClient::Get()->SetUploadUrl(submit_url);
ElectronCrashReporterClient::Get()->SetCollectStatsConsent(upload_to_server);
ElectronCrashReporterClient::Get()->SetShouldRateLimit(rate_limit);
ElectronCrashReporterClient::Get()->SetShouldCompressUploads(compress);
ElectronCrashReporterClient::Get()->SetGlobalAnnotations(global_extra);
auto* command_line = base::CommandLine::ForCurrentProcess();
std::string process_type =
is_node_process
? "node"
: command_line->GetSwitchValueASCII(::switches::kProcessType);
#if defined(OS_LINUX)
auto& global_crash_keys = GetGlobalCrashKeysMutable();
for (const auto& pair : global_extra) {
global_crash_keys[pair.first] = pair.second;
}
for (const auto& pair : extra)
electron::crash_keys::SetCrashKey(pair.first, pair.second);
for (const auto& pair : global_extra)
electron::crash_keys::SetCrashKey(pair.first, pair.second);
breakpad::InitCrashReporter(process_type);
#elif defined(OS_MACOSX)
for (const auto& pair : extra)
electron::crash_keys::SetCrashKey(pair.first, pair.second);
::crash_reporter::InitializeCrashpad(process_type.empty(), process_type);
if (ignore_system_crash_handler) {
crashpad::CrashpadInfo::GetCrashpadInfo()
->set_system_crash_reporter_forwarding(crashpad::TriState::kDisabled);
}
#elif defined(OS_WIN)
for (const auto& pair : extra)
electron::crash_keys::SetCrashKey(pair.first, pair.second);
base::FilePath user_data_dir;
base::PathService::Get(DIR_USER_DATA, &user_data_dir);
::crash_reporter::InitializeCrashpadWithEmbeddedHandler(
process_type.empty(), process_type,
base::UTF16ToUTF8(user_data_dir.value()), base::FilePath());
#endif
#endif
}
} // namespace crash_reporter
} // namespace api
} // namespace electron
namespace {
#if defined(MAS_BUILD)
void GetUploadedReports(
base::OnceCallback<void(v8::Local<v8::Value>)> callback) {
std::move(callback).Run(v8::Array::New(v8::Isolate::GetCurrent()));
}
#else
scoped_refptr<UploadList> CreateCrashUploadList() {
#if defined(OS_MACOSX) || defined(OS_WIN)
return new CrashUploadListCrashpad();
#else
base::FilePath crash_dir_path;
base::PathService::Get(electron::DIR_CRASH_DUMPS, &crash_dir_path);
base::FilePath upload_log_path =
crash_dir_path.AppendASCII(CrashUploadList::kReporterLogFilename);
return new TextLogUploadList(upload_log_path);
#endif // defined(OS_MACOSX) || defined(OS_WIN)
}
v8::Local<v8::Value> GetUploadedReports(v8::Isolate* isolate) {
auto list = CreateCrashUploadList();
// TODO(nornagon): switch to using Load() instead of LoadSync() once the
// synchronous version of getUploadedReports is deprecated so we can remove
// our patch.
{
base::ThreadRestrictions::ScopedAllowIO allow_io;
list->LoadSync();
}
std::vector<UploadList::UploadInfo> uploads;
constexpr size_t kMaxUploadReportsToList = std::numeric_limits<size_t>::max();
list->GetUploads(kMaxUploadReportsToList, &uploads);
std::vector<v8::Local<v8::Object>> result;
for (const auto& upload : uploads) {
result.push_back(gin::DataObjectBuilder(isolate)
.Set("date", upload.upload_time)
.Set("id", upload.upload_id)
.Build());
}
v8::Local<v8::Value> v8_result = gin::ConvertToV8(isolate, result);
return v8_result;
}
#endif
void SetUploadToServer(bool upload) {
#if !defined(MAS_BUILD)
ElectronCrashReporterClient::Get()->SetCollectStatsConsent(upload);
#endif
}
bool GetUploadToServer() {
#if defined(MAS_BUILD)
return false;
#else
return ElectronCrashReporterClient::Get()->GetCollectStatsConsent();
#endif
}
v8::Local<v8::Value> GetParameters(v8::Isolate* isolate) {
std::map<std::string, std::string> keys;
#if !defined(MAS_BUILD)
electron::crash_keys::GetCrashKeys(&keys);
#endif
return gin::ConvertToV8(isolate, keys);
}
void Initialize(v8::Local<v8::Object> exports,
v8::Local<v8::Value> unused,
v8::Local<v8::Context> context,
void* priv) {
gin_helper::Dictionary dict(context->GetIsolate(), exports);
dict.SetMethod("start", &electron::api::crash_reporter::Start);
dict.SetMethod("addExtraParameter", &electron::crash_keys::SetCrashKey);
dict.SetMethod("removeExtraParameter", &electron::crash_keys::ClearCrashKey);
dict.SetMethod("getParameters", &GetParameters);
dict.SetMethod("getUploadedReports", &GetUploadedReports);
dict.SetMethod("setUploadToServer", &SetUploadToServer);
dict.SetMethod("getUploadToServer", &GetUploadToServer);
}
} // namespace
NODE_LINKED_MODULE_CONTEXT_AWARE(electron_browser_crash_reporter, Initialize)

View file

@ -0,0 +1,40 @@
// Copyright (c) 2020 Slack Technologies, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef SHELL_BROWSER_API_ELECTRON_API_CRASH_REPORTER_H_
#define SHELL_BROWSER_API_ELECTRON_API_CRASH_REPORTER_H_
#include <map>
#include <string>
#include "base/files/file_path.h"
namespace electron {
namespace api {
namespace crash_reporter {
bool IsCrashReporterEnabled();
#if defined(OS_LINUX)
const std::map<std::string, std::string>& GetGlobalCrashKeys();
#endif
// JS bindings API; exposed publicly because it's also called from node_main.cc
void Start(const std::string& submit_url,
bool upload_to_server,
bool ignore_system_crash_handler,
bool rate_limit,
bool compress,
const std::map<std::string, std::string>& global_extra,
const std::map<std::string, std::string>& extra,
bool is_node_process);
} // namespace crash_reporter
} // namespace api
} // namespace electron
#endif // SHELL_BROWSER_API_ELECTRON_API_CRASH_REPORTER_H_

View file

@ -17,11 +17,11 @@
#include "base/threading/thread_task_runner_handle.h"
#include "shell/browser/browser_observer.h"
#include "shell/browser/electron_browser_main_parts.h"
#include "shell/browser/electron_paths.h"
#include "shell/browser/login_handler.h"
#include "shell/browser/native_window.h"
#include "shell/browser/window_list.h"
#include "shell/common/application_info.h"
#include "shell/common/electron_paths.h"
#include "shell/common/gin_helper/arguments.h"
namespace electron {

View file

@ -11,6 +11,7 @@
#include <memory>
#include <utility>
#include "base/base_switches.h"
#include "base/command_line.h"
#include "base/environment.h"
#include "base/files/file_util.h"
@ -24,6 +25,7 @@
#include "base/strings/utf_string_conversions.h"
#include "base/task/post_task.h"
#include "chrome/browser/browser_process.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_version.h"
#include "components/net_log/chrome_net_log.h"
#include "components/network_hints/common/network_hints.mojom.h"
@ -37,6 +39,7 @@
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/site_instance.h"
#include "content/public/common/content_descriptors.h"
#include "content/public/common/content_paths.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/service_names.mojom.h"
@ -47,6 +50,7 @@
#include "extensions/browser/extension_navigation_ui_data.h"
#include "extensions/browser/extension_protocols.h"
#include "extensions/common/constants.h"
#include "extensions/common/switches.h"
#include "net/base/escape.h"
#include "net/ssl/ssl_cert_request_info.h"
#include "ppapi/buildflags/buildflags.h"
@ -58,6 +62,7 @@
#include "services/service_manager/public/cpp/binder_map.h"
#include "shell/app/manifests.h"
#include "shell/browser/api/electron_api_app.h"
#include "shell/browser/api/electron_api_crash_reporter.h"
#include "shell/browser/api/electron_api_protocol.h"
#include "shell/browser/api/electron_api_session.h"
#include "shell/browser/api/electron_api_web_contents.h"
@ -67,7 +72,6 @@
#include "shell/browser/electron_browser_context.h"
#include "shell/browser/electron_browser_main_parts.h"
#include "shell/browser/electron_navigation_throttle.h"
#include "shell/browser/electron_paths.h"
#include "shell/browser/electron_quota_permission_context.h"
#include "shell/browser/electron_speech_recognition_manager_delegate.h"
#include "shell/browser/font_defaults.h"
@ -89,6 +93,7 @@
#include "shell/browser/window_list.h"
#include "shell/common/api/api.mojom.h"
#include "shell/common/application_info.h"
#include "shell/common/electron_paths.h"
#include "shell/common/options_switches.h"
#include "shell/common/platform_util.h"
#include "third_party/blink/public/common/loader/url_loader_throttle.h"
@ -164,6 +169,14 @@
#include "content/public/common/child_process_host.h"
#endif
#if defined(OS_LINUX)
#include "base/debug/leak_annotations.h"
#include "components/crash/content/browser/crash_handler_host_linux.h"
#include "components/crash/core/app/breakpad_linux.h"
#include "components/crash/core/app/crash_switches.h"
#include "components/crash/core/app/crashpad.h"
#endif
using content::BrowserThread;
namespace electron {
@ -265,6 +278,64 @@ const extensions::Extension* GetEnabledExtensionFromEffectiveURL(
}
#endif // BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
#if defined(OS_LINUX)
breakpad::CrashHandlerHostLinux* CreateCrashHandlerHost(
const std::string& process_type) {
base::FilePath dumps_path;
base::PathService::Get(electron::DIR_CRASH_DUMPS, &dumps_path);
{
ANNOTATE_SCOPED_MEMORY_LEAK;
breakpad::CrashHandlerHostLinux* crash_handler =
new breakpad::CrashHandlerHostLinux(process_type, dumps_path, true);
crash_handler->StartUploaderThread();
return crash_handler;
}
}
int GetCrashSignalFD(const base::CommandLine& command_line) {
// Extensions have the same process type as renderers.
if (command_line.HasSwitch(extensions::switches::kExtensionProcess)) {
static breakpad::CrashHandlerHostLinux* crash_handler = nullptr;
if (!crash_handler)
crash_handler = CreateCrashHandlerHost("extension");
return crash_handler->GetDeathSignalSocket();
}
std::string process_type =
command_line.GetSwitchValueASCII(::switches::kProcessType);
if (process_type == ::switches::kRendererProcess) {
static breakpad::CrashHandlerHostLinux* crash_handler = nullptr;
if (!crash_handler)
crash_handler = CreateCrashHandlerHost(process_type);
return crash_handler->GetDeathSignalSocket();
}
if (process_type == ::switches::kPpapiPluginProcess) {
static breakpad::CrashHandlerHostLinux* crash_handler = nullptr;
if (!crash_handler)
crash_handler = CreateCrashHandlerHost(process_type);
return crash_handler->GetDeathSignalSocket();
}
if (process_type == ::switches::kGpuProcess) {
static breakpad::CrashHandlerHostLinux* crash_handler = nullptr;
if (!crash_handler)
crash_handler = CreateCrashHandlerHost(process_type);
return crash_handler->GetDeathSignalSocket();
}
if (process_type == ::switches::kUtilityProcess) {
static breakpad::CrashHandlerHostLinux* crash_handler = nullptr;
if (!crash_handler)
crash_handler = CreateCrashHandlerHost(process_type);
return crash_handler->GetDeathSignalSocket();
}
return -1;
}
#endif // defined(OS_LINUX)
} // namespace
// static
@ -649,6 +720,23 @@ void ElectronBrowserClient::AppendExtraCommandLineSwitches(
std::string process_type =
command_line->GetSwitchValueASCII(::switches::kProcessType);
#if defined(OS_LINUX)
bool enable_crash_reporter = false;
enable_crash_reporter = breakpad::IsCrashReporterEnabled();
if (enable_crash_reporter) {
command_line->AppendSwitch(::switches::kEnableCrashReporter);
std::string switch_value;
for (const auto& pair : api::crash_reporter::GetGlobalCrashKeys()) {
if (!switch_value.empty())
switch_value += ",";
switch_value += pair.first;
switch_value += "=";
switch_value += pair.second;
}
command_line->AppendSwitchASCII(switches::kGlobalCrashKeys, switch_value);
}
#endif
if (process_type == ::switches::kUtilityProcess ||
process_type == ::switches::kRendererProcess) {
// Copy following switches to child process.
@ -1530,6 +1618,18 @@ void ElectronBrowserClient::RegisterBrowserInterfaceBindersForFrame(
#endif
}
#if defined(OS_LINUX)
void ElectronBrowserClient::GetAdditionalMappedFilesForChildProcess(
const base::CommandLine& command_line,
int child_process_id,
content::PosixFileDescriptorInfo* mappings) {
int crash_signal_fd = GetCrashSignalFD(command_line);
if (crash_signal_fd >= 0) {
mappings->Share(service_manager::kCrashDumpSignal, crash_signal_fd);
}
}
#endif
std::unique_ptr<content::LoginDelegate>
ElectronBrowserClient::CreateLoginDelegate(
const net::AuthChallengeInfo& auth_info,

View file

@ -71,6 +71,12 @@ class ElectronBrowserClient : public content::ContentBrowserClient,
content::RenderFrameHost* render_frame_host,
service_manager::BinderMapWithContext<content::RenderFrameHost*>* map)
override;
#if defined(OS_LINUX)
void GetAdditionalMappedFilesForChildProcess(
const base::CommandLine& command_line,
int child_process_id,
content::PosixFileDescriptorInfo* mappings) override;
#endif
std::string GetUserAgent() override;
void SetUserAgent(const std::string& user_agent);

View file

@ -38,7 +38,6 @@
#include "shell/browser/electron_browser_client.h"
#include "shell/browser/electron_browser_main_parts.h"
#include "shell/browser/electron_download_manager_delegate.h"
#include "shell/browser/electron_paths.h"
#include "shell/browser/electron_permission_manager.h"
#include "shell/browser/net/resolve_proxy_helper.h"
#include "shell/browser/pref_store_delegate.h"
@ -48,6 +47,7 @@
#include "shell/browser/web_view_manager.h"
#include "shell/browser/zoom_level_delegate.h"
#include "shell/common/application_info.h"
#include "shell/common/electron_paths.h"
#include "shell/common/options_switches.h"
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)

View file

@ -37,7 +37,6 @@
#include "shell/browser/browser_process_impl.h"
#include "shell/browser/electron_browser_client.h"
#include "shell/browser/electron_browser_context.h"
#include "shell/browser/electron_paths.h"
#include "shell/browser/electron_web_ui_controller_factory.h"
#include "shell/browser/feature_list.h"
#include "shell/browser/javascript_environment.h"
@ -47,6 +46,7 @@
#include "shell/common/api/electron_bindings.h"
#include "shell/common/application_info.h"
#include "shell/common/asar/asar_util.h"
#include "shell/common/electron_paths.h"
#include "shell/common/gin_helper/trackable_object.h"
#include "shell/common/node_bindings.h"
#include "shell/common/node_includes.h"

View file

@ -7,9 +7,9 @@
#include "base/mac/bundle_locations.h"
#include "base/mac/foundation_util.h"
#include "base/path_service.h"
#include "shell/browser/electron_paths.h"
#import "shell/browser/mac/electron_application.h"
#include "shell/browser/mac/electron_application_delegate.h"
#include "shell/common/electron_paths.h"
#include "ui/base/l10n/l10n_util_mac.h"
namespace electron {

View file

@ -27,7 +27,7 @@
#include "net/base/net_errors.h"
#include "net/socket/stream_socket.h"
#include "net/socket/tcp_server_socket.h"
#include "shell/browser/electron_paths.h"
#include "shell/common/electron_paths.h"
#include "ui/base/resource/resource_bundle.h"
namespace electron {

View file

@ -15,8 +15,6 @@ interface ElectronRenderer {
ReceivePostMessage(string channel, blink.mojom.TransferableMessage message);
UpdateCrashpadPipeName(string pipe_name);
// This is an API specific to the "remote" module, and will ultimately be
// replaced by generic IPC once WeakRef is generally available.
[EnableIf=enable_remote_module]

View file

@ -1,67 +0,0 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include <map>
#include <string>
#include "base/bind.h"
#include "gin/data_object_builder.h"
#include "shell/common/crash_reporter/crash_reporter.h"
#include "shell/common/gin_converters/callback_converter.h"
#include "shell/common/gin_converters/file_path_converter.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/node_includes.h"
using crash_reporter::CrashReporter;
namespace gin {
template <>
struct Converter<CrashReporter::UploadReportResult> {
static v8::Local<v8::Value> ToV8(
v8::Isolate* isolate,
const CrashReporter::UploadReportResult& reports) {
return gin::DataObjectBuilder(isolate)
.Set("date",
v8::Date::New(isolate->GetCurrentContext(), reports.first * 1000.0)
.ToLocalChecked())
.Set("id", reports.second)
.Build();
}
};
} // namespace gin
namespace {
void Initialize(v8::Local<v8::Object> exports,
v8::Local<v8::Value> unused,
v8::Local<v8::Context> context,
void* priv) {
auto reporter = base::Unretained(CrashReporter::GetInstance());
gin_helper::Dictionary dict(context->GetIsolate(), exports);
dict.SetMethod("start", base::BindRepeating(&CrashReporter::Start, reporter));
dict.SetMethod(
"addExtraParameter",
base::BindRepeating(&CrashReporter::AddExtraParameter, reporter));
dict.SetMethod(
"removeExtraParameter",
base::BindRepeating(&CrashReporter::RemoveExtraParameter, reporter));
dict.SetMethod("getParameters",
base::BindRepeating(&CrashReporter::GetParameters, reporter));
dict.SetMethod(
"getUploadedReports",
base::BindRepeating(&CrashReporter::GetUploadedReports, reporter));
dict.SetMethod(
"setUploadToServer",
base::BindRepeating(&CrashReporter::SetUploadToServer, reporter));
dict.SetMethod(
"getUploadToServer",
base::BindRepeating(&CrashReporter::GetUploadToServer, reporter));
}
} // namespace
NODE_LINKED_MODULE_CONTEXT_AWARE(electron_common_crash_reporter, Initialize)

144
shell/common/crash_keys.cc Normal file
View file

@ -0,0 +1,144 @@
// Copyright (c) 2020 Slack Technologies, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "shell/common/crash_keys.h"
#include <deque>
#include <utility>
#include <vector>
#include "base/command_line.h"
#include "base/environment.h"
#include "base/no_destructor.h"
#include "base/strings/string_split.h"
#include "components/crash/core/common/crash_key.h"
#include "content/public/common/content_switches.h"
#include "shell/common/electron_constants.h"
#include "shell/common/options_switches.h"
#include "third_party/crashpad/crashpad/client/annotation.h"
namespace electron {
namespace crash_keys {
namespace {
using ExtraCrashKeys = std::deque<crash_reporter::CrashKeyString<127>>;
ExtraCrashKeys& GetExtraCrashKeys() {
static base::NoDestructor<ExtraCrashKeys> extra_keys;
return *extra_keys;
}
std::deque<std::string>& GetExtraCrashKeyNames() {
static base::NoDestructor<std::deque<std::string>> crash_key_names;
return *crash_key_names;
}
} // namespace
constexpr uint32_t kMaxCrashKeyNameLength = 40;
#if defined(OS_LINUX)
static_assert(kMaxCrashKeyNameLength <=
crash_reporter::internal::kCrashKeyStorageKeySize,
"max crash key name length above what breakpad supports");
#else
static_assert(kMaxCrashKeyNameLength <= crashpad::Annotation::kNameMaxLength,
"max crash key name length above what crashpad supports");
#endif
void SetCrashKey(const std::string& key, const std::string& value) {
// Chrome DCHECK()s if we try to set an annotation with a name longer than
// the max.
// TODO(nornagon): warn the developer (via console.warn) when this happens.
if (key.size() >= kMaxCrashKeyNameLength)
return;
auto& crash_key_names = GetExtraCrashKeyNames();
auto iter = std::find(crash_key_names.begin(), crash_key_names.end(), key);
if (iter == crash_key_names.end()) {
crash_key_names.emplace_back(key);
GetExtraCrashKeys().emplace_back(crash_key_names.back().c_str());
iter = crash_key_names.end() - 1;
}
GetExtraCrashKeys()[iter - crash_key_names.begin()].Set(value);
}
void ClearCrashKey(const std::string& key) {
const auto& crash_key_names = GetExtraCrashKeyNames();
auto iter = std::find(crash_key_names.begin(), crash_key_names.end(), key);
if (iter != crash_key_names.end()) {
GetExtraCrashKeys()[iter - crash_key_names.begin()].Clear();
}
}
void GetCrashKeys(std::map<std::string, std::string>* keys) {
const auto& crash_key_names = GetExtraCrashKeyNames();
const auto& crash_keys = GetExtraCrashKeys();
int i = 0;
for (const auto& key : crash_key_names) {
const auto& value = crash_keys[i++];
if (value.is_set()) {
keys->emplace(key, value.value());
}
}
}
namespace {
bool IsRunningAsNode() {
#if BUILDFLAG(ENABLE_RUN_AS_NODE)
return base::Environment::Create()->HasVar(electron::kRunAsNode);
#else
return false;
#endif
}
} // namespace
void SetCrashKeysFromCommandLine(const base::CommandLine& command_line) {
#if defined(OS_LINUX)
if (command_line.HasSwitch(switches::kGlobalCrashKeys)) {
std::vector<std::pair<std::string, std::string>> global_crash_keys;
base::SplitStringIntoKeyValuePairs(
command_line.GetSwitchValueASCII(switches::kGlobalCrashKeys), '=', ',',
&global_crash_keys);
for (const auto& pair : global_crash_keys) {
SetCrashKey(pair.first, pair.second);
}
}
#endif
// NB. this is redundant with the 'ptype' key that //components/crash
// reports; it's present for backwards compatibility.
static crash_reporter::CrashKeyString<16> process_type_key("process_type");
if (IsRunningAsNode()) {
process_type_key.Set("node");
} else {
std::string process_type =
command_line.GetSwitchValueASCII(::switches::kProcessType);
if (process_type.empty()) {
process_type_key.Set("browser");
} else {
process_type_key.Set(process_type);
}
}
}
void SetPlatformCrashKey() {
// TODO(nornagon): this is redundant with the 'plat' key that
// //components/crash already includes. Remove it.
static crash_reporter::CrashKeyString<8> platform_key("platform");
#if defined(OS_WIN)
platform_key.Set("win32");
#elif defined(OS_MACOSX)
platform_key.Set("darwin");
#elif defined(OS_LINUX)
platform_key.Set("linux");
#else
platform_key.Set("unknown");
#endif
}
} // namespace crash_keys
} // namespace electron

30
shell/common/crash_keys.h Normal file
View file

@ -0,0 +1,30 @@
// Copyright (c) 2020 Slack Technologies, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef SHELL_COMMON_CRASH_KEYS_H_
#define SHELL_COMMON_CRASH_KEYS_H_
#include <map>
#include <string>
namespace base {
class CommandLine;
}
namespace electron {
namespace crash_keys {
void SetCrashKey(const std::string& key, const std::string& value);
void ClearCrashKey(const std::string& key);
void GetCrashKeys(std::map<std::string, std::string>* keys);
void SetCrashKeysFromCommandLine(const base::CommandLine& command_line);
void SetPlatformCrashKey();
} // namespace crash_keys
} // namespace electron
#endif // SHELL_COMMON_CRASH_KEYS_H_

View file

@ -1,159 +0,0 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "shell/common/crash_reporter/crash_reporter.h"
#include <memory>
#include "base/command_line.h"
#include "base/environment.h"
#include "base/files/file_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/threading/thread_restrictions.h"
#include "content/public/common/content_switches.h"
#include "electron/electron_version.h"
#include "shell/browser/browser.h"
#include "shell/common/electron_constants.h"
#include "shell/common/gin_converters/file_path_converter.h"
#include "shell/common/gin_helper/dictionary.h"
namespace crash_reporter {
const char kCrashpadProcess[] = "crash-handler";
const char kCrashesDirectoryKey[] = "crashes-directory";
CrashReporter::CrashReporter() {
#if BUILDFLAG(ENABLE_RUN_AS_NODE)
bool run_as_node = base::Environment::Create()->HasVar(electron::kRunAsNode);
#else
bool run_as_node = false;
#endif
if (run_as_node) {
process_type_ = "node";
} else {
auto* cmd = base::CommandLine::ForCurrentProcess();
process_type_ = cmd->GetSwitchValueASCII(switches::kProcessType);
}
// process_type_ will be empty for browser process
}
CrashReporter::~CrashReporter() = default;
bool CrashReporter::IsInitialized() {
return is_initialized_;
}
void CrashReporter::Start(const std::string& submit_url,
const base::FilePath& crashes_dir,
bool upload_to_server,
bool skip_system_crash_handler,
bool rate_limit,
bool compress,
const StringMap& extra_parameters) {
is_initialized_ = true;
SetUploadParameters(extra_parameters);
Init(submit_url, crashes_dir, upload_to_server, skip_system_crash_handler,
rate_limit, compress);
}
void CrashReporter::SetUploadParameters(const StringMap& parameters) {
upload_parameters_ = parameters;
upload_parameters_["process_type"] =
process_type_.empty() ? "browser" : process_type_;
upload_parameters_["prod"] = ELECTRON_PRODUCT_NAME;
upload_parameters_["ver"] = ELECTRON_VERSION_STRING;
// Setting platform dependent parameters.
SetUploadParameters();
}
std::vector<CrashReporter::UploadReportResult>
CrashReporter::GetUploadedReports(const base::FilePath& crashes_dir) {
base::ThreadRestrictions::ScopedAllowIO allow_io;
std::string file_content;
std::vector<CrashReporter::UploadReportResult> result;
base::FilePath uploads_path =
crashes_dir.Append(FILE_PATH_LITERAL("uploads.log"));
if (base::ReadFileToString(uploads_path, &file_content)) {
std::vector<std::string> reports = base::SplitString(
file_content, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
for (const std::string& report : reports) {
std::vector<std::string> report_item = base::SplitString(
report, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
int report_time = 0;
if (report_item.size() >= 2 &&
base::StringToInt(report_item[0], &report_time)) {
result.emplace_back(report_time, report_item[1]);
}
}
}
return result;
}
std::map<std::string, std::string> CrashReporter::GetParameters() const {
return upload_parameters_;
}
#if defined(OS_MACOSX) && defined(MAS_BUILD)
class DummyCrashReporter : public CrashReporter {
public:
~DummyCrashReporter() override {}
void SetUploadToServer(bool upload_to_server) override {}
bool GetUploadToServer() override { return false; }
void AddExtraParameter(const std::string& key,
const std::string& value) override {}
void RemoveExtraParameter(const std::string& key) override {}
void Init(const std::string& submit_url,
const base::FilePath& crashes_dir,
bool upload_to_server,
bool skip_system_crash_handler,
bool rate_limit,
bool compress) override {}
void SetUploadParameters() override {}
};
// static
CrashReporter* CrashReporter::GetInstance() {
static DummyCrashReporter crash_reporter;
return &crash_reporter;
}
#endif
void CrashReporter::StartInstance(const gin_helper::Dictionary& options) {
auto* reporter = GetInstance();
if (!reporter)
return;
std::string product_name;
options.Get("productName", &product_name);
std::string company_name;
options.Get("companyName", &company_name);
std::string submit_url;
options.Get("submitURL", &submit_url);
base::FilePath crashes_dir;
options.Get("crashesDirectory", &crashes_dir);
StringMap extra_parameters;
options.Get("extra", &extra_parameters);
bool rate_limit = false;
options.Get("rateLimit", &rate_limit);
bool compress = false;
options.Get("compress", &compress);
extra_parameters["_productName"] = product_name;
extra_parameters["_companyName"] = company_name;
bool upload_to_server = true;
bool skip_system_crash_handler = false;
reporter->Start(submit_url, crashes_dir, upload_to_server,
skip_system_crash_handler, rate_limit, compress,
extra_parameters);
}
} // namespace crash_reporter

View file

@ -1,79 +0,0 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef SHELL_COMMON_CRASH_REPORTER_CRASH_REPORTER_H_
#define SHELL_COMMON_CRASH_REPORTER_CRASH_REPORTER_H_
#include <map>
#include <string>
#include <utility>
#include <vector>
#include "base/files/file_path.h"
#include "base/macros.h"
namespace gin_helper {
class Dictionary;
}
namespace crash_reporter {
extern const char kCrashpadProcess[];
extern const char kCrashesDirectoryKey[];
class CrashReporter {
public:
typedef std::map<std::string, std::string> StringMap;
typedef std::pair<int, std::string> UploadReportResult; // upload-date, id
static CrashReporter* GetInstance();
// FIXME(zcbenz): We should not do V8 in this file, this method should only
// accept C++ struct as parameter, and atom_api_crash_reporter.cc is
// responsible for parsing the parameter from JavaScript.
static void StartInstance(const gin_helper::Dictionary& options);
bool IsInitialized();
void Start(const std::string& submit_url,
const base::FilePath& crashes_dir,
bool upload_to_server,
bool skip_system_crash_handler,
bool rate_limit,
bool compress,
const StringMap& extra_parameters);
virtual std::vector<CrashReporter::UploadReportResult> GetUploadedReports(
const base::FilePath& crashes_dir);
virtual void SetUploadToServer(bool upload_to_server) = 0;
virtual bool GetUploadToServer() = 0;
virtual void AddExtraParameter(const std::string& key,
const std::string& value) = 0;
virtual void RemoveExtraParameter(const std::string& key) = 0;
virtual std::map<std::string, std::string> GetParameters() const;
protected:
CrashReporter();
virtual ~CrashReporter();
virtual void Init(const std::string& submit_url,
const base::FilePath& crashes_dir,
bool upload_to_server,
bool skip_system_crash_handler,
bool rate_limit,
bool compress) = 0;
virtual void SetUploadParameters() = 0;
StringMap upload_parameters_;
std::string process_type_;
private:
bool is_initialized_ = false;
void SetUploadParameters(const StringMap& parameters);
DISALLOW_COPY_AND_ASSIGN(CrashReporter);
};
} // namespace crash_reporter
#endif // SHELL_COMMON_CRASH_REPORTER_CRASH_REPORTER_H_

View file

@ -1,118 +0,0 @@
// Copyright (c) 2019 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "shell/common/crash_reporter/crash_reporter_crashpad.h"
#include <algorithm>
#include <memory>
#include "base/files/file_util.h"
#include "base/strings/string_piece.h"
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "third_party/crashpad/crashpad/client/settings.h"
namespace crash_reporter {
CrashReporterCrashpad::CrashReporterCrashpad() {}
CrashReporterCrashpad::~CrashReporterCrashpad() {}
bool CrashReporterCrashpad::GetUploadToServer() {
bool enabled = true;
if (database_) {
database_->GetSettings()->GetUploadsEnabled(&enabled);
}
return enabled;
}
void CrashReporterCrashpad::SetUploadToServer(const bool upload_to_server) {
if (database_) {
database_->GetSettings()->SetUploadsEnabled(upload_to_server);
}
}
void CrashReporterCrashpad::SetCrashKeyValue(base::StringPiece key,
base::StringPiece value) {
simple_string_dictionary_->SetKeyValue(key.data(), value.data());
}
void CrashReporterCrashpad::SetInitialCrashKeyValues() {
for (const auto& upload_parameter : upload_parameters_)
SetCrashKeyValue(upload_parameter.first, upload_parameter.second);
}
void CrashReporterCrashpad::AddExtraParameter(const std::string& key,
const std::string& value) {
if (simple_string_dictionary_) {
SetCrashKeyValue(key, value);
} else {
upload_parameters_[key] = value;
}
}
void CrashReporterCrashpad::RemoveExtraParameter(const std::string& key) {
if (simple_string_dictionary_)
simple_string_dictionary_->RemoveKey(key.data());
else
upload_parameters_.erase(key);
}
std::map<std::string, std::string> CrashReporterCrashpad::GetParameters()
const {
if (simple_string_dictionary_) {
std::map<std::string, std::string> ret;
crashpad::SimpleStringDictionary::Iterator iter(*simple_string_dictionary_);
for (;;) {
auto* const entry = iter.Next();
if (!entry)
break;
ret[entry->key] = entry->value;
}
return ret;
}
return upload_parameters_;
}
std::vector<CrashReporter::UploadReportResult>
CrashReporterCrashpad::GetUploadedReports(const base::FilePath& crashes_dir) {
std::vector<CrashReporter::UploadReportResult> uploaded_reports;
{
base::ThreadRestrictions::ScopedAllowIO allow_io;
if (!base::PathExists(crashes_dir)) {
return uploaded_reports;
}
}
// Load crashpad database.
std::unique_ptr<crashpad::CrashReportDatabase> database =
crashpad::CrashReportDatabase::Initialize(crashes_dir);
DCHECK(database);
std::vector<crashpad::CrashReportDatabase::Report> completed_reports;
crashpad::CrashReportDatabase::OperationStatus status =
database->GetCompletedReports(&completed_reports);
if (status != crashpad::CrashReportDatabase::kNoError) {
return uploaded_reports;
}
for (const crashpad::CrashReportDatabase::Report& completed_report :
completed_reports) {
if (completed_report.uploaded) {
uploaded_reports.push_back(
UploadReportResult(static_cast<int>(completed_report.creation_time),
completed_report.id));
}
}
auto sort_by_time = [](const UploadReportResult& a,
const UploadReportResult& b) {
return a.first > b.first;
};
std::sort(uploaded_reports.begin(), uploaded_reports.end(), sort_by_time);
return uploaded_reports;
}
} // namespace crash_reporter

View file

@ -1,49 +0,0 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef SHELL_COMMON_CRASH_REPORTER_CRASH_REPORTER_CRASHPAD_H_
#define SHELL_COMMON_CRASH_REPORTER_CRASH_REPORTER_CRASHPAD_H_
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "base/compiler_specific.h"
#include "base/strings/string_piece.h"
#include "shell/common/crash_reporter/crash_reporter.h"
#include "third_party/crashpad/crashpad/client/crash_report_database.h"
#include "third_party/crashpad/crashpad/client/simple_string_dictionary.h"
namespace crash_reporter {
class CrashReporterCrashpad : public CrashReporter {
public:
void SetUploadToServer(bool upload_to_server) override;
bool GetUploadToServer() override;
void AddExtraParameter(const std::string& key,
const std::string& value) override;
void RemoveExtraParameter(const std::string& key) override;
std::map<std::string, std::string> GetParameters() const override;
protected:
CrashReporterCrashpad();
~CrashReporterCrashpad() override;
void SetUploadsEnabled(bool enable_uploads);
void SetCrashKeyValue(base::StringPiece key, base::StringPiece value);
void SetInitialCrashKeyValues();
std::vector<UploadReportResult> GetUploadedReports(
const base::FilePath& crashes_dir) override;
std::unique_ptr<crashpad::SimpleStringDictionary> simple_string_dictionary_;
std::unique_ptr<crashpad::CrashReportDatabase> database_;
DISALLOW_COPY_AND_ASSIGN(CrashReporterCrashpad);
};
} // namespace crash_reporter
#endif // SHELL_COMMON_CRASH_REPORTER_CRASH_REPORTER_CRASHPAD_H_

View file

@ -1,150 +0,0 @@
// Copyright (c) 2014 GitHub, Inc.
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "shell/common/crash_reporter/crash_reporter_linux.h"
#include <sys/time.h>
#include <unistd.h>
#include <memory>
#include <string>
#include "base/debug/crash_logging.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/linux_util.h"
#include "base/logging.h"
#include "base/memory/singleton.h"
#include "base/process/memory.h"
#include "base/threading/thread_restrictions.h"
#include "breakpad/src/client/linux/handler/exception_handler.h"
#include "breakpad/src/common/linux/linux_libc_support.h"
using google_breakpad::ExceptionHandler;
using google_breakpad::MinidumpDescriptor;
namespace crash_reporter {
namespace {
// Define a preferred limit on minidump sizes, because Crash Server currently
// throws away any larger than 1.2MB (1.2 * 1024 * 1024). A value of -1 means
// no limit.
static const off_t kMaxMinidumpFileSize = 1258291;
} // namespace
CrashReporterLinux::CrashReporterLinux() : pid_(getpid()) {
// Set the base process start time value.
struct timeval tv;
if (!gettimeofday(&tv, nullptr)) {
uint64_t ret = tv.tv_sec;
ret *= 1000;
ret += tv.tv_usec / 1000;
process_start_time_ = ret;
}
{
base::ThreadRestrictions::ScopedAllowIO allow_io;
// Make base::g_linux_distro work.
base::SetLinuxDistro(base::GetLinuxDistro());
}
}
CrashReporterLinux::~CrashReporterLinux() = default;
void CrashReporterLinux::Init(const std::string& submit_url,
const base::FilePath& crashes_dir,
bool upload_to_server,
bool skip_system_crash_handler,
bool rate_limit,
bool compress) {
EnableCrashDumping(crashes_dir);
upload_url_ = submit_url;
upload_to_server_ = upload_to_server;
crash_keys_ = std::make_unique<CrashKeyStorage>();
for (StringMap::const_iterator iter = upload_parameters_.begin();
iter != upload_parameters_.end(); ++iter)
crash_keys_->SetKeyValue(iter->first.c_str(), iter->second.c_str());
}
void CrashReporterLinux::SetUploadParameters() {
upload_parameters_["platform"] = "linux";
}
void CrashReporterLinux::SetUploadToServer(const bool upload_to_server) {
upload_to_server_ = upload_to_server;
}
bool CrashReporterLinux::GetUploadToServer() {
return upload_to_server_;
}
void CrashReporterLinux::AddExtraParameter(const std::string& key,
const std::string& value) {}
void CrashReporterLinux::RemoveExtraParameter(const std::string& key) {}
void CrashReporterLinux::EnableCrashDumping(const base::FilePath& crashes_dir) {
{
base::ThreadRestrictions::ScopedAllowIO allow_io;
base::CreateDirectory(crashes_dir);
}
std::string log_file = crashes_dir.Append("uploads.log").value();
strncpy(g_crash_log_path, log_file.c_str(), sizeof(g_crash_log_path));
MinidumpDescriptor minidump_descriptor(crashes_dir.value());
minidump_descriptor.set_size_limit(kMaxMinidumpFileSize);
breakpad_ = std::make_unique<ExceptionHandler>(minidump_descriptor, nullptr,
CrashDone, this,
true, // Install handlers.
-1);
}
bool CrashReporterLinux::CrashDone(const MinidumpDescriptor& minidump,
void* context,
const bool succeeded) {
CrashReporterLinux* self = static_cast<CrashReporterLinux*>(context);
// WARNING: this code runs in a compromised context. It may not call into
// libc nor allocate memory normally.
if (!succeeded) {
const char msg[] = "Failed to generate minidump.";
WriteLog(msg, sizeof(msg) - 1);
return false;
}
DCHECK(!minidump.IsFD());
BreakpadInfo info = {0};
info.filename = minidump.path();
info.fd = minidump.fd();
info.distro = base::g_linux_distro;
info.distro_length = my_strlen(base::g_linux_distro);
info.upload = self->upload_to_server_;
info.process_start_time = self->process_start_time_;
info.oom_size = base::g_oom_size;
info.pid = self->pid_;
info.upload_url = self->upload_url_.c_str();
info.crash_keys = self->crash_keys_.get();
HandleCrashDump(info);
return true;
}
// static
CrashReporterLinux* CrashReporterLinux::GetInstance() {
return base::Singleton<CrashReporterLinux>::get();
}
// static
CrashReporter* CrashReporter::GetInstance() {
return CrashReporterLinux::GetInstance();
}
} // namespace crash_reporter

View file

@ -1,68 +0,0 @@
// Copyright (c) 2014 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef SHELL_COMMON_CRASH_REPORTER_CRASH_REPORTER_LINUX_H_
#define SHELL_COMMON_CRASH_REPORTER_CRASH_REPORTER_LINUX_H_
#include <memory>
#include <string>
#include "base/compiler_specific.h"
#include "shell/common/crash_reporter/crash_reporter.h"
#include "shell/common/crash_reporter/linux/crash_dump_handler.h"
namespace base {
template <typename T>
struct DefaultSingletonTraits;
}
namespace google_breakpad {
class ExceptionHandler;
class MinidumpDescriptor;
} // namespace google_breakpad
namespace crash_reporter {
class CrashReporterLinux : public CrashReporter {
public:
static CrashReporterLinux* GetInstance();
void Init(const std::string& submit_url,
const base::FilePath& crashes_dir,
bool upload_to_server,
bool skip_system_crash_handler,
bool rate_limit,
bool compress) override;
void SetUploadToServer(bool upload_to_server) override;
void SetUploadParameters() override;
bool GetUploadToServer() override;
void AddExtraParameter(const std::string& key,
const std::string& value) override;
void RemoveExtraParameter(const std::string& key) override;
private:
friend struct base::DefaultSingletonTraits<CrashReporterLinux>;
CrashReporterLinux();
~CrashReporterLinux() override;
void EnableCrashDumping(const base::FilePath& crashes_dir);
static bool CrashDone(const google_breakpad::MinidumpDescriptor& minidump,
void* context,
const bool succeeded);
std::unique_ptr<google_breakpad::ExceptionHandler> breakpad_;
std::unique_ptr<CrashKeyStorage> crash_keys_;
uint64_t process_start_time_ = 0;
pid_t pid_ = 0;
std::string upload_url_;
bool upload_to_server_ = true;
DISALLOW_COPY_AND_ASSIGN(CrashReporterLinux);
};
} // namespace crash_reporter
#endif // SHELL_COMMON_CRASH_REPORTER_CRASH_REPORTER_LINUX_H_

View file

@ -1,43 +0,0 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef SHELL_COMMON_CRASH_REPORTER_CRASH_REPORTER_MAC_H_
#define SHELL_COMMON_CRASH_REPORTER_CRASH_REPORTER_MAC_H_
#include <memory>
#include <string>
#include "shell/common/crash_reporter/crash_reporter_crashpad.h"
namespace base {
template <typename T>
struct DefaultSingletonTraits;
}
namespace crash_reporter {
class CrashReporterMac : public CrashReporterCrashpad {
public:
static CrashReporterMac* GetInstance();
void Init(const std::string& submit_url,
const base::FilePath& crashes_dir,
bool upload_to_server,
bool skip_system_crash_handler,
bool rate_limit,
bool compress) override;
void SetUploadParameters() override;
private:
friend struct base::DefaultSingletonTraits<CrashReporterMac>;
CrashReporterMac();
~CrashReporterMac() override;
DISALLOW_COPY_AND_ASSIGN(CrashReporterMac);
};
} // namespace crash_reporter
#endif // SHELL_COMMON_CRASH_REPORTER_CRASH_REPORTER_MAC_H_

View file

@ -1,85 +0,0 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "shell/common/crash_reporter/crash_reporter_mac.h"
#include <algorithm>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "base/mac/bundle_locations.h"
#include "base/mac/mac_util.h"
#include "base/memory/singleton.h"
#include "third_party/crashpad/crashpad/client/crashpad_client.h"
#include "third_party/crashpad/crashpad/client/crashpad_info.h"
namespace crash_reporter {
CrashReporterMac::CrashReporterMac() {}
CrashReporterMac::~CrashReporterMac() {}
void CrashReporterMac::Init(const std::string& submit_url,
const base::FilePath& crashes_dir,
bool upload_to_server,
bool skip_system_crash_handler,
bool rate_limit,
bool compress) {
// check whether crashpad has been initialized.
// Only need to initialize once.
if (simple_string_dictionary_)
return;
if (process_type_.empty()) { // browser process
@autoreleasepool {
base::FilePath framework_bundle_path = base::mac::FrameworkBundlePath();
base::FilePath handler_path =
framework_bundle_path.Append("Resources").Append("crashpad_handler");
std::vector<std::string> args;
if (!rate_limit)
args.emplace_back("--no-rate-limit");
if (!compress)
args.emplace_back("--no-upload-gzip");
crashpad::CrashpadClient crashpad_client;
crashpad_client.StartHandler(handler_path, crashes_dir, crashes_dir,
submit_url, StringMap(), args, true, false);
} // @autoreleasepool
}
crashpad::CrashpadInfo* crashpad_info =
crashpad::CrashpadInfo::GetCrashpadInfo();
if (skip_system_crash_handler) {
crashpad_info->set_system_crash_reporter_forwarding(
crashpad::TriState::kDisabled);
}
simple_string_dictionary_.reset(new crashpad::SimpleStringDictionary());
crashpad_info->set_simple_annotations(simple_string_dictionary_.get());
SetInitialCrashKeyValues();
if (process_type_.empty()) { // browser process
database_ = crashpad::CrashReportDatabase::Initialize(crashes_dir);
SetUploadToServer(upload_to_server);
}
}
void CrashReporterMac::SetUploadParameters() {
upload_parameters_["platform"] = "darwin";
}
// static
CrashReporterMac* CrashReporterMac::GetInstance() {
return base::Singleton<CrashReporterMac>::get();
}
// static
CrashReporter* CrashReporter::GetInstance() {
return CrashReporterMac::GetInstance();
}
} // namespace crash_reporter

View file

@ -1,151 +0,0 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "shell/common/crash_reporter/crash_reporter_win.h"
#include <memory>
#include <vector>
#include "base/environment.h"
#include "base/memory/singleton.h"
#include "base/path_service.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "electron/shell/common/api/api.mojom.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "shell/browser/ui/inspectable_web_contents_impl.h"
#include "shell/common/electron_constants.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/crashpad/crashpad/client/crashpad_client.h"
#include "third_party/crashpad/crashpad/client/crashpad_info.h"
#if defined(_WIN64)
#include "gin/public/debug.h"
#endif
namespace {
#if defined(_WIN64)
int CrashForException(EXCEPTION_POINTERS* info) {
auto* reporter = crash_reporter::CrashReporterWin::GetInstance();
if (reporter->IsInitialized()) {
reporter->GetCrashpadClient().DumpAndCrash(info);
return EXCEPTION_CONTINUE_SEARCH;
}
// When there is exception and we do not have crashReporter set up, we just
// let the execution continue and crash, which is the default behavior.
//
// We must not return EXCEPTION_CONTINUE_SEARCH here, as it would end up with
// busy loop when there is no exception handler in the program.
return EXCEPTION_CONTINUE_EXECUTION;
}
#endif
} // namespace
namespace crash_reporter {
CrashReporterWin::CrashReporterWin() {}
CrashReporterWin::~CrashReporterWin() {}
#if defined(_WIN64)
void CrashReporterWin::SetUnhandledExceptionFilter() {
gin::Debug::SetUnhandledExceptionCallback(&CrashForException);
}
#endif
void CrashReporterWin::Init(const std::string& submit_url,
const base::FilePath& crashes_dir,
bool upload_to_server,
bool skip_system_crash_handler,
bool rate_limit,
bool compress) {
// check whether crashpad has been initialized.
// Only need to initialize once.
if (simple_string_dictionary_)
return;
if (process_type_.empty()) { // browser process
base::FilePath handler_path;
base::PathService::Get(base::FILE_EXE, &handler_path);
std::vector<std::string> args;
if (!rate_limit)
args.emplace_back("--no-rate-limit");
if (!compress)
args.emplace_back("--no-upload-gzip");
args.push_back(base::StringPrintf("--type=%s", kCrashpadProcess));
args.push_back(
base::StringPrintf("--%s=%s", kCrashesDirectoryKey,
base::UTF16ToUTF8(crashes_dir.value()).c_str()));
crashpad_client_.StartHandler(handler_path, crashes_dir, crashes_dir,
submit_url, StringMap(), args, true, false);
UpdatePipeName();
} else {
std::unique_ptr<base::Environment> env(base::Environment::Create());
std::string pipe_name_utf8;
if (env->GetVar(electron::kCrashpadPipeName, &pipe_name_utf8)) {
base::string16 pipe_name = base::UTF8ToUTF16(pipe_name_utf8);
if (!crashpad_client_.SetHandlerIPCPipe(pipe_name))
LOG(ERROR) << "Failed to set handler IPC pipe name: " << pipe_name;
} else {
LOG(ERROR) << "Unable to get pipe name for crashpad";
}
}
crashpad::CrashpadInfo* crashpad_info =
crashpad::CrashpadInfo::GetCrashpadInfo();
if (skip_system_crash_handler) {
crashpad_info->set_system_crash_reporter_forwarding(
crashpad::TriState::kDisabled);
}
simple_string_dictionary_.reset(new crashpad::SimpleStringDictionary());
crashpad_info->set_simple_annotations(simple_string_dictionary_.get());
SetInitialCrashKeyValues();
if (process_type_.empty()) { // browser process
database_ = crashpad::CrashReportDatabase::Initialize(crashes_dir);
SetUploadToServer(upload_to_server);
}
}
void CrashReporterWin::SetUploadParameters() {
upload_parameters_["platform"] = "win32";
}
crashpad::CrashpadClient& CrashReporterWin::GetCrashpadClient() {
return crashpad_client_;
}
void CrashReporterWin::UpdatePipeName() {
std::string pipe_name =
base::UTF16ToUTF8(crashpad_client_.GetHandlerIPCPipe());
std::unique_ptr<base::Environment> env(base::Environment::Create());
env->SetVar(electron::kCrashpadPipeName, pipe_name);
// Notify all WebContents of the pipe name.
const auto& pages = electron::InspectableWebContentsImpl::GetAll();
for (auto* page : pages) {
auto* frame_host = page->GetWebContents()->GetMainFrame();
if (!frame_host)
continue;
mojo::AssociatedRemote<electron::mojom::ElectronRenderer> electron_renderer;
frame_host->GetRemoteAssociatedInterfaces()->GetInterface(
&electron_renderer);
electron_renderer->UpdateCrashpadPipeName(pipe_name);
}
}
// static
CrashReporterWin* CrashReporterWin::GetInstance() {
return base::Singleton<CrashReporterWin>::get();
}
// static
CrashReporter* CrashReporter::GetInstance() {
return CrashReporterWin::GetInstance();
}
} // namespace crash_reporter

View file

@ -1,52 +0,0 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef SHELL_COMMON_CRASH_REPORTER_CRASH_REPORTER_WIN_H_
#define SHELL_COMMON_CRASH_REPORTER_CRASH_REPORTER_WIN_H_
#include <memory>
#include <string>
#include "shell/common/crash_reporter/crash_reporter_crashpad.h"
#include "third_party/crashpad/crashpad/client/crashpad_client.h"
namespace base {
template <typename T>
struct DefaultSingletonTraits;
}
namespace crash_reporter {
class CrashReporterWin : public CrashReporterCrashpad {
public:
static CrashReporterWin* GetInstance();
#if defined(_WIN64)
static void SetUnhandledExceptionFilter();
#endif
void Init(const std::string& submit_url,
const base::FilePath& crashes_dir,
bool upload_to_server,
bool skip_system_crash_handler,
bool rate_limit,
bool compress) override;
void SetUploadParameters() override;
crashpad::CrashpadClient& GetCrashpadClient();
private:
friend struct base::DefaultSingletonTraits<CrashReporterWin>;
CrashReporterWin();
~CrashReporterWin() override;
void UpdatePipeName();
crashpad::CrashpadClient crashpad_client_;
DISALLOW_COPY_AND_ASSIGN(CrashReporterWin);
};
} // namespace crash_reporter
#endif // SHELL_COMMON_CRASH_REPORTER_CRASH_REPORTER_WIN_H_

View file

@ -1,753 +0,0 @@
// Copyright (c) 2014 GitHub, Inc.
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
// For linux_syscall_support.h. This makes it safe to call embedded system
// calls when in seccomp mode.
#include "shell/common/crash_reporter/linux/crash_dump_handler.h"
#include <poll.h>
#include <algorithm>
#include "base/posix/eintr_wrapper.h"
#include "breakpad/src/client/linux/minidump_writer/directory_reader.h"
#include "breakpad/src/common/linux/linux_libc_support.h"
#include "breakpad/src/common/memory_allocator.h"
#include "third_party/lss/linux_syscall_support.h"
// Some versions of gcc are prone to warn about unused return values. In cases
// where we either a) know the call cannot fail, or b) there is nothing we
// can do when a call fails, we mark the return code as ignored. This avoids
// spurious compiler warnings.
#define IGNORE_RET(x) \
do { \
if (x) \
; \
} while (0)
namespace crash_reporter {
namespace {
// String buffer size to use to convert a uint64_t to string.
const size_t kUint64StringSize = 21;
// Writes the value |v| as 16 hex characters to the memory pointed at by
// |output|.
void write_uint64_hex(char* output, uint64_t v) {
static const char hextable[] = "0123456789abcdef";
for (int i = 15; i >= 0; --i) {
output[i] = hextable[v & 15];
v >>= 4;
}
}
// uint64_t version of my_int_len() from
// breakpad/src/common/linux/linux_libc_support.h. Return the length of the
// given, non-negative integer when expressed in base 10.
unsigned my_uint64_len(uint64_t i) {
if (!i)
return 1;
unsigned len = 0;
while (i) {
len++;
i /= 10;
}
return len;
}
// uint64_t version of my_uitos() from
// breakpad/src/common/linux/linux_libc_support.h. Convert a non-negative
// integer to a string (not null-terminated).
void my_uint64tos(char* output, uint64_t i, unsigned i_len) {
for (unsigned index = i_len; index; --index, i /= 10)
output[index - 1] = '0' + (i % 10);
}
// Converts a struct timeval to milliseconds.
uint64_t kernel_timeval_to_ms(struct kernel_timeval* tv) {
uint64_t ret = tv->tv_sec; // Avoid overflow by explicitly using a uint64_t.
ret *= 1000;
ret += tv->tv_usec / 1000;
return ret;
}
bool my_isxdigit(char c) {
return (c >= '0' && c <= '9') || ((c | 0x20) >= 'a' && (c | 0x20) <= 'f');
}
size_t LengthWithoutTrailingSpaces(const char* str, size_t len) {
while (len > 0 && str[len - 1] == ' ') {
len--;
}
return len;
}
// MIME substrings.
const char g_rn[] = "\r\n";
const char g_form_data_msg[] = "Content-Disposition: form-data; name=\"";
const char g_quote_msg[] = "\"";
const char g_dashdash_msg[] = "--";
const char g_dump_msg[] = "upload_file_minidump\"; filename=\"dump\"";
const char g_content_type_msg[] = "Content-Type: application/octet-stream";
// MimeWriter manages an iovec for writing MIMEs to a file.
class MimeWriter {
public:
static const int kIovCapacity = 30;
static const size_t kMaxCrashChunkSize = 64;
MimeWriter(int fd, const char* const mime_boundary);
~MimeWriter();
// Append boundary.
virtual void AddBoundary();
// Append end of file boundary.
virtual void AddEnd();
// Append key/value pair with specified sizes.
virtual void AddPairData(const char* msg_type,
size_t msg_type_size,
const char* msg_data,
size_t msg_data_size);
// Append key/value pair.
void AddPairString(const char* msg_type, const char* msg_data) {
AddPairData(msg_type, my_strlen(msg_type), msg_data, my_strlen(msg_data));
}
// Append key/value pair, splitting value into chunks no larger than
// |chunk_size|. |chunk_size| cannot be greater than |kMaxCrashChunkSize|.
// The msg_type string will have a counter suffix to distinguish each chunk.
virtual void AddPairDataInChunks(const char* msg_type,
size_t msg_type_size,
const char* msg_data,
size_t msg_data_size,
size_t chunk_size,
bool strip_trailing_spaces);
// Add binary file contents to be uploaded with the specified filename.
virtual void AddFileContents(const char* filename_msg,
uint8_t* file_data,
size_t file_size);
// Flush any pending iovecs to the output file.
void Flush() {
IGNORE_RET(sys_writev(fd_, iov_, iov_index_));
iov_index_ = 0;
}
protected:
void AddItem(const void* base, size_t size);
// Minor performance trade-off for easier-to-maintain code.
void AddString(const char* str) { AddItem(str, my_strlen(str)); }
void AddItemWithoutTrailingSpaces(const void* base, size_t size);
struct kernel_iovec iov_[kIovCapacity];
int iov_index_ = 0;
// Output file descriptor.
int fd_ = -1;
const char* const mime_boundary_;
private:
DISALLOW_COPY_AND_ASSIGN(MimeWriter);
};
MimeWriter::MimeWriter(int fd, const char* const mime_boundary)
: fd_(fd), mime_boundary_(mime_boundary) {}
MimeWriter::~MimeWriter() = default;
void MimeWriter::AddBoundary() {
AddString(mime_boundary_);
AddString(g_rn);
}
void MimeWriter::AddEnd() {
AddString(mime_boundary_);
AddString(g_dashdash_msg);
AddString(g_rn);
}
void MimeWriter::AddPairData(const char* msg_type,
size_t msg_type_size,
const char* msg_data,
size_t msg_data_size) {
AddString(g_form_data_msg);
AddItem(msg_type, msg_type_size);
AddString(g_quote_msg);
AddString(g_rn);
AddString(g_rn);
AddItem(msg_data, msg_data_size);
AddString(g_rn);
}
void MimeWriter::AddPairDataInChunks(const char* msg_type,
size_t msg_type_size,
const char* msg_data,
size_t msg_data_size,
size_t chunk_size,
bool strip_trailing_spaces) {
if (chunk_size > kMaxCrashChunkSize)
return;
unsigned i = 0;
size_t done = 0, msg_length = msg_data_size;
while (msg_length) {
char num[kUint64StringSize];
const unsigned num_len = my_uint_len(++i);
my_uitos(num, i, num_len);
size_t chunk_len = std::min(chunk_size, msg_length);
AddString(g_form_data_msg);
AddItem(msg_type, msg_type_size);
AddItem(num, num_len);
AddString(g_quote_msg);
AddString(g_rn);
AddString(g_rn);
if (strip_trailing_spaces) {
AddItemWithoutTrailingSpaces(msg_data + done, chunk_len);
} else {
AddItem(msg_data + done, chunk_len);
}
AddString(g_rn);
AddBoundary();
Flush();
done += chunk_len;
msg_length -= chunk_len;
}
}
void MimeWriter::AddFileContents(const char* filename_msg,
uint8_t* file_data,
size_t file_size) {
AddString(g_form_data_msg);
AddString(filename_msg);
AddString(g_rn);
AddString(g_content_type_msg);
AddString(g_rn);
AddString(g_rn);
AddItem(file_data, file_size);
AddString(g_rn);
}
void MimeWriter::AddItem(const void* base, size_t size) {
// Check if the iovec is full and needs to be flushed to output file.
if (iov_index_ == kIovCapacity) {
Flush();
}
iov_[iov_index_].iov_base = const_cast<void*>(base);
iov_[iov_index_].iov_len = size;
++iov_index_;
}
void MimeWriter::AddItemWithoutTrailingSpaces(const void* base, size_t size) {
AddItem(base,
LengthWithoutTrailingSpaces(static_cast<const char*>(base), size));
}
void LoadDataFromFD(google_breakpad::PageAllocator* allocator,
int fd,
bool close_fd,
uint8_t** file_data,
size_t* size) {
struct kernel_stat st;
if (sys_fstat(fd, &st) != 0) {
static const char msg[] = "Cannot upload crash dump: stat failed\n";
WriteLog(msg, sizeof(msg) - 1);
if (close_fd)
IGNORE_RET(sys_close(fd));
return;
}
*file_data = reinterpret_cast<uint8_t*>(allocator->Alloc(st.st_size));
if (!(*file_data)) {
static const char msg[] = "Cannot upload crash dump: cannot alloc\n";
WriteLog(msg, sizeof(msg) - 1);
if (close_fd)
IGNORE_RET(sys_close(fd));
return;
}
my_memset(*file_data, 0xf, st.st_size);
*size = st.st_size;
int byte_read = sys_read(fd, *file_data, *size);
if (byte_read == -1) {
static const char msg[] = "Cannot upload crash dump: read failed\n";
WriteLog(msg, sizeof(msg) - 1);
if (close_fd)
IGNORE_RET(sys_close(fd));
return;
}
if (close_fd)
IGNORE_RET(sys_close(fd));
}
void LoadDataFromFile(google_breakpad::PageAllocator* allocator,
const char* filename,
int* fd,
uint8_t** file_data,
size_t* size) {
// WARNING: this code runs in a compromised context. It may not call into
// libc nor allocate memory normally.
*fd = sys_open(filename, O_RDONLY, 0);
*size = 0;
if (*fd < 0) {
static const char msg[] = "Cannot upload crash dump: failed to open\n";
WriteLog(msg, sizeof(msg) - 1);
return;
}
LoadDataFromFD(allocator, *fd, true, file_data, size);
}
// Spawn the appropriate upload process for the current OS:
// - generic Linux invokes wget.
// - ChromeOS invokes crash_reporter.
// |dumpfile| is the path to the dump data file.
// |mime_boundary| is only used on Linux.
// |exe_buf| is only used on CrOS and is the crashing process' name.
void ExecUploadProcessOrTerminate(const BreakpadInfo& info,
const char* dumpfile,
const char* mime_boundary,
const char* exe_buf,
google_breakpad::PageAllocator* allocator) {
// The --header argument to wget looks like:
// --header=Content-Type: multipart/form-data; boundary=XYZ
// where the boundary has two fewer leading '-' chars
static const char header_msg[] =
"--header=Content-Type: multipart/form-data; boundary=";
char* const header = reinterpret_cast<char*>(
allocator->Alloc(sizeof(header_msg) - 1 + strlen(mime_boundary) - 2 + 1));
memcpy(header, header_msg, sizeof(header_msg) - 1);
memcpy(header + sizeof(header_msg) - 1, mime_boundary + 2,
strlen(mime_boundary) - 2);
// We grab the NUL byte from the end of |mime_boundary|.
// The --post-file argument to wget looks like:
// --post-file=/tmp/...
static const char post_file_msg[] = "--post-file=";
char* const post_file = reinterpret_cast<char*>(
allocator->Alloc(sizeof(post_file_msg) - 1 + strlen(dumpfile) + 1));
memcpy(post_file, post_file_msg, sizeof(post_file_msg) - 1);
memcpy(post_file + sizeof(post_file_msg) - 1, dumpfile, strlen(dumpfile));
static const char kWgetBinary[] = "/usr/bin/wget";
const char* args[] = {
kWgetBinary, header, post_file, info.upload_url,
"--timeout=60", // Set a timeout so we don't hang forever.
"--tries=1", // Don't retry if the upload fails.
"--quiet", // Be silent.
"-O", // output reply to /dev/null.
"/dev/fd/3", nullptr,
};
static const char msg[] =
"Cannot upload crash dump: cannot exec "
"/usr/bin/wget\n";
execve(args[0], const_cast<char**>(args), environ);
WriteLog(msg, sizeof(msg) - 1);
sys__exit(1);
}
// Runs in the helper process to wait for the upload process running
// ExecUploadProcessOrTerminate() to finish. Returns the number of bytes written
// to |fd| and save the written contents to |buf|.
// |buf| needs to be big enough to hold |bytes_to_read| + 1 characters.
size_t WaitForCrashReportUploadProcess(int fd,
size_t bytes_to_read,
char* buf) {
size_t bytes_read = 0;
// Upload should finish in about 10 seconds. Add a few more 500 ms
// internals to account for process startup time.
for (size_t wait_count = 0; wait_count < 24; ++wait_count) {
struct kernel_pollfd poll_fd;
poll_fd.fd = fd;
poll_fd.events = POLLIN | POLLPRI | POLLERR;
int ret = sys_poll(&poll_fd, 1, 500);
if (ret < 0) {
// Error
break;
} else if (ret > 0) {
// There is data to read.
ssize_t len = HANDLE_EINTR(
sys_read(fd, buf + bytes_read, bytes_to_read - bytes_read));
if (len < 0)
break;
bytes_read += len;
if (bytes_read == bytes_to_read)
break;
}
// |ret| == 0 -> timed out, continue waiting.
// or |bytes_read| < |bytes_to_read| still, keep reading.
}
buf[bytes_to_read] = 0; // Always NUL terminate the buffer.
return bytes_read;
}
// |buf| should be |expected_len| + 1 characters in size and NULL terminated.
bool IsValidCrashReportId(const char* buf,
size_t bytes_read,
size_t expected_len) {
if (bytes_read != expected_len)
return false;
for (size_t i = 0; i < bytes_read; ++i) {
if (!my_isxdigit(buf[i]) && buf[i] != '-')
return false;
}
return true;
}
// |buf| should be |expected_len| + 1 characters in size and NULL terminated.
void HandleCrashReportId(const char* buf,
size_t bytes_read,
size_t expected_len) {
if (!IsValidCrashReportId(buf, bytes_read, expected_len)) {
static const char msg[] = "Failed to get crash dump id.";
WriteLog(msg, sizeof(msg) - 1);
WriteNewline();
static const char id_msg[] = "Report Id: ";
WriteLog(id_msg, sizeof(id_msg) - 1);
WriteLog(buf, bytes_read);
WriteNewline();
return;
}
// Write crash dump id to stderr.
static const char msg[] = "Crash dump id: ";
WriteLog(msg, sizeof(msg) - 1);
WriteLog(buf, my_strlen(buf));
WriteNewline();
// Write crash dump id to crash log as: seconds_since_epoch,crash_id
struct kernel_timeval tv;
if (!sys_gettimeofday(&tv, nullptr)) {
uint64_t time = kernel_timeval_to_ms(&tv) / 1000;
char time_str[kUint64StringSize];
const unsigned time_len = my_uint64_len(time);
my_uint64tos(time_str, time, time_len);
const int kLogOpenFlags = O_CREAT | O_WRONLY | O_APPEND | O_CLOEXEC;
int log_fd = sys_open(g_crash_log_path, kLogOpenFlags, 0600);
if (log_fd > 0) {
sys_write(log_fd, time_str, time_len);
sys_write(log_fd, ",", 1);
sys_write(log_fd, buf, my_strlen(buf));
sys_write(log_fd, "\n", 1);
IGNORE_RET(sys_close(log_fd));
}
}
}
} // namespace
char g_crash_log_path[256];
void HandleCrashDump(const BreakpadInfo& info) {
int dumpfd;
bool keep_fd = false;
size_t dump_size;
uint8_t* dump_data;
google_breakpad::PageAllocator allocator;
const char* exe_buf = nullptr;
if (info.fd != -1) {
// Dump is provided with an open FD.
keep_fd = true;
dumpfd = info.fd;
// The FD is pointing to the end of the file.
// Rewind, we'll read the data next.
if (lseek(dumpfd, 0, SEEK_SET) == -1) {
static const char msg[] =
"Cannot upload crash dump: failed to "
"reposition minidump FD\n";
WriteLog(msg, sizeof(msg) - 1);
IGNORE_RET(sys_close(dumpfd));
return;
}
LoadDataFromFD(&allocator, info.fd, false, &dump_data, &dump_size);
} else {
// Dump is provided with a path.
keep_fd = false;
LoadDataFromFile(&allocator, info.filename, &dumpfd, &dump_data,
&dump_size);
}
// We need to build a MIME block for uploading to the server. Since we are
// going to fork and run wget, it needs to be written to a temp file.
const int ufd = sys_open("/dev/urandom", O_RDONLY, 0);
if (ufd < 0) {
static const char msg[] =
"Cannot upload crash dump because /dev/urandom"
" is missing\n";
WriteLog(msg, sizeof(msg) - 1);
return;
}
static const char temp_file_template[] =
"/tmp/chromium-upload-XXXXXXXXXXXXXXXX";
char temp_file[sizeof(temp_file_template)];
int temp_file_fd = -1;
if (keep_fd) {
temp_file_fd = dumpfd;
// Rewind the destination, we are going to overwrite it.
if (lseek(dumpfd, 0, SEEK_SET) == -1) {
static const char msg[] =
"Cannot upload crash dump: failed to "
"reposition minidump FD (2)\n";
WriteLog(msg, sizeof(msg) - 1);
IGNORE_RET(sys_close(dumpfd));
return;
}
} else {
if (info.upload) {
memcpy(temp_file, temp_file_template, sizeof(temp_file_template));
for (unsigned i = 0; i < 10; ++i) {
uint64_t t;
sys_read(ufd, &t, sizeof(t));
write_uint64_hex(temp_file + sizeof(temp_file) - (16 + 1), t);
temp_file_fd = sys_open(temp_file, O_WRONLY | O_CREAT | O_EXCL, 0600);
if (temp_file_fd >= 0)
break;
}
if (temp_file_fd < 0) {
static const char msg[] =
"Failed to create temporary file in /tmp: "
"cannot upload crash dump\n";
WriteLog(msg, sizeof(msg) - 1);
IGNORE_RET(sys_close(ufd));
return;
}
} else {
temp_file_fd = sys_open(info.filename, O_WRONLY, 0600);
if (temp_file_fd < 0) {
static const char msg[] = "Failed to save crash dump: failed to open\n";
WriteLog(msg, sizeof(msg) - 1);
IGNORE_RET(sys_close(ufd));
return;
}
}
}
// The MIME boundary is 28 hyphens, followed by a 64-bit nonce and a NUL.
char mime_boundary[28 + 16 + 1];
my_memset(mime_boundary, '-', 28);
uint64_t boundary_rand;
sys_read(ufd, &boundary_rand, sizeof(boundary_rand));
write_uint64_hex(mime_boundary + 28, boundary_rand);
mime_boundary[28 + 16] = 0;
IGNORE_RET(sys_close(ufd));
// The MIME block looks like this:
// BOUNDARY \r\n
// Content-Disposition: form-data; name="prod" \r\n \r\n
// Chrome_Linux \r\n
// BOUNDARY \r\n
// Content-Disposition: form-data; name="ver" \r\n \r\n
// 1.2.3.4 \r\n
// BOUNDARY \r\n
//
// zero or one:
// Content-Disposition: form-data; name="ptime" \r\n \r\n
// abcdef \r\n
// BOUNDARY \r\n
//
// zero or one:
// Content-Disposition: form-data; name="ptype" \r\n \r\n
// abcdef \r\n
// BOUNDARY \r\n
//
// zero or one:
// Content-Disposition: form-data; name="lsb-release" \r\n \r\n
// abcdef \r\n
// BOUNDARY \r\n
//
// zero or one:
// Content-Disposition: form-data; name="oom-size" \r\n \r\n
// 1234567890 \r\n
// BOUNDARY \r\n
//
// zero or more (up to CrashKeyStorage::num_entries = 64):
// Content-Disposition: form-data; name=crash-key-name \r\n
// crash-key-value \r\n
// BOUNDARY \r\n
//
// Content-Disposition: form-data; name="dump"; filename="dump" \r\n
// Content-Type: application/octet-stream \r\n \r\n
// <dump contents>
// \r\n BOUNDARY -- \r\n
MimeWriter writer(temp_file_fd, mime_boundary);
{
writer.AddBoundary();
if (info.pid > 0) {
char pid_value_buf[kUint64StringSize];
uint64_t pid_value_len = my_uint64_len(info.pid);
my_uint64tos(pid_value_buf, info.pid, pid_value_len);
static const char pid_key_name[] = "pid";
writer.AddPairData(pid_key_name, sizeof(pid_key_name) - 1, pid_value_buf,
pid_value_len);
writer.AddBoundary();
}
writer.Flush();
}
if (info.process_start_time > 0) {
struct kernel_timeval tv;
if (!sys_gettimeofday(&tv, nullptr)) {
uint64_t time = kernel_timeval_to_ms(&tv);
if (time > info.process_start_time) {
time -= info.process_start_time;
char time_str[kUint64StringSize];
const unsigned time_len = my_uint64_len(time);
my_uint64tos(time_str, time, time_len);
static const char process_time_msg[] = "ptime";
writer.AddPairData(process_time_msg, sizeof(process_time_msg) - 1,
time_str, time_len);
writer.AddBoundary();
writer.Flush();
}
}
}
if (info.distro_length) {
static const char distro_msg[] = "lsb-release";
writer.AddPairString(distro_msg, info.distro);
writer.AddBoundary();
writer.Flush();
}
if (info.oom_size) {
char oom_size_str[kUint64StringSize];
const unsigned oom_size_len = my_uint64_len(info.oom_size);
my_uint64tos(oom_size_str, info.oom_size, oom_size_len);
static const char oom_size_msg[] = "oom-size";
writer.AddPairData(oom_size_msg, sizeof(oom_size_msg) - 1, oom_size_str,
oom_size_len);
writer.AddBoundary();
writer.Flush();
}
if (info.crash_keys) {
CrashKeyStorage::Iterator crash_key_iterator(*info.crash_keys);
const CrashKeyStorage::Entry* entry;
while ((entry = crash_key_iterator.Next())) {
writer.AddPairString(entry->key, entry->value);
writer.AddBoundary();
writer.Flush();
}
}
writer.AddFileContents(g_dump_msg, dump_data, dump_size);
writer.AddEnd();
writer.Flush();
IGNORE_RET(sys_close(temp_file_fd));
if (!info.upload)
return;
const pid_t child = sys_fork();
if (!child) {
// Spawned helper process.
//
// This code is called both when a browser is crashing (in which case,
// nothing really matters any more) and when a renderer/plugin crashes, in
// which case we need to continue.
//
// Since we are a multithreaded app, if we were just to fork(), we might
// grab file descriptors which have just been created in another thread and
// hold them open for too long.
//
// Thus, we have to loop and try and close everything.
const int fd = sys_open("/proc/self/fd", O_DIRECTORY | O_RDONLY, 0);
if (fd < 0) {
for (unsigned i = 3; i < 8192; ++i)
IGNORE_RET(sys_close(i));
} else {
google_breakpad::DirectoryReader reader(fd);
const char* name;
while (reader.GetNextEntry(&name)) {
int i;
if (my_strtoui(&i, name) && i > 2 && i != fd)
IGNORE_RET(sys_close(i));
reader.PopEntry();
}
IGNORE_RET(sys_close(fd));
}
IGNORE_RET(sys_setsid());
// Leave one end of a pipe in the upload process and watch for it getting
// closed by the upload process exiting.
int fds[2];
if (sys_pipe(fds) >= 0) {
const pid_t upload_child = sys_fork();
if (!upload_child) {
// Upload process.
IGNORE_RET(sys_close(fds[0]));
IGNORE_RET(sys_dup2(fds[1], 3));
ExecUploadProcessOrTerminate(info, temp_file, mime_boundary, exe_buf,
&allocator);
}
// Helper process.
if (upload_child > 0) {
IGNORE_RET(sys_close(fds[1]));
const size_t kCrashIdLength = 36;
char id_buf[kCrashIdLength + 1];
size_t bytes_read =
WaitForCrashReportUploadProcess(fds[0], kCrashIdLength, id_buf);
HandleCrashReportId(id_buf, bytes_read, kCrashIdLength);
if (sys_waitpid(upload_child, nullptr, WNOHANG) == 0) {
// Upload process is still around, kill it.
sys_kill(upload_child, SIGKILL);
}
}
}
// Helper process.
IGNORE_RET(sys_unlink(info.filename));
IGNORE_RET(sys_unlink(temp_file));
sys__exit(0);
}
// Main browser process.
if (child <= 0)
return;
(void)HANDLE_EINTR(sys_waitpid(child, nullptr, 0));
}
size_t WriteLog(const char* buf, size_t nbytes) {
return sys_write(2, buf, nbytes);
}
size_t WriteNewline() {
return WriteLog("\n", 1);
}
} // namespace crash_reporter

View file

@ -1,46 +0,0 @@
// Copyright (c) 2014 GitHub, Inc.
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef SHELL_COMMON_CRASH_REPORTER_LINUX_CRASH_DUMP_HANDLER_H_
#define SHELL_COMMON_CRASH_REPORTER_LINUX_CRASH_DUMP_HANDLER_H_
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
#include "base/macros.h"
#include "breakpad/src/common/simple_string_dictionary.h"
namespace crash_reporter {
typedef google_breakpad::NonAllocatingMap<256, 256, 64> CrashKeyStorage;
// BreakpadInfo describes a crash report.
// The minidump information can either be contained in a file descriptor (fd) or
// in a file (whose path is in filename).
struct BreakpadInfo {
int fd; // File descriptor to the Breakpad dump data.
const char* filename; // Path to the Breakpad dump data.
const char* distro; // Linux distro string.
unsigned distro_length; // Length of |distro|.
bool upload; // Whether to upload or save crash dump.
uint64_t process_start_time; // Uptime of the crashing process.
size_t oom_size; // Amount of memory requested if OOM.
uint64_t pid; // PID where applicable.
const char* upload_url; // URL to upload the minidump.
CrashKeyStorage* crash_keys;
};
void HandleCrashDump(const BreakpadInfo& info);
size_t WriteLog(const char* buf, size_t nbytes);
size_t WriteNewline();
// Global variable storing the path of upload log.
extern char g_crash_log_path[256];
} // namespace crash_reporter
#endif // SHELL_COMMON_CRASH_REPORTER_LINUX_CRASH_DUMP_HANDLER_H_

View file

@ -1,80 +0,0 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "shell/common/crash_reporter/win/crash_service_main.h"
#include <string>
#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "shell/common/crash_reporter/crash_reporter.h"
#include "third_party/crashpad/crashpad/handler/handler_main.h"
namespace crash_service {
namespace {
const wchar_t kStandardLogFile[] = L"operation_log.txt";
void InvalidParameterHandler(const wchar_t*,
const wchar_t*,
const wchar_t*,
unsigned int,
uintptr_t) {
// noop.
}
bool CreateCrashServiceDirectory(const base::FilePath& temp_dir) {
if (!base::PathExists(temp_dir)) {
if (!base::CreateDirectory(temp_dir))
return false;
}
return true;
}
void RemoveArgs(std::vector<char*>* args) {
args->erase(
std::remove_if(args->begin(), args->end(), [](const std::string& str) {
return base::StartsWith(str, "--type", base::CompareCase::SENSITIVE) ||
base::StartsWith(
str,
std::string("--") + crash_reporter::kCrashesDirectoryKey,
base::CompareCase::INSENSITIVE_ASCII);
}));
}
} // namespace.
int Main(std::vector<char*>* args) {
// Ignore invalid parameter errors.
_set_invalid_parameter_handler(InvalidParameterHandler);
// Initialize all Chromium things.
base::AtExitManager exit_manager;
base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
// We use/create a directory under the user's temp folder, for logging.
base::FilePath operating_dir(
cmd_line->GetSwitchValueNative(crash_reporter::kCrashesDirectoryKey));
CreateCrashServiceDirectory(operating_dir);
base::FilePath log_file_path = operating_dir.Append(kStandardLogFile);
// Logging to stderr (to help with debugging failures on the
// buildbots) and to a file.
logging::LoggingSettings settings;
settings.logging_dest = logging::LOG_TO_ALL;
settings.log_file_path = log_file_path.value().c_str();
logging::InitLogging(settings);
// Logging with pid, tid and timestamp.
logging::SetLogItems(true, true, true, false);
// Crashpad cannot handle unknown arguments, so we need to remove it
RemoveArgs(args);
return crashpad::HandlerMain(args->size(), args->data(), nullptr);
}
} // namespace crash_service

View file

@ -1,17 +0,0 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef SHELL_COMMON_CRASH_REPORTER_WIN_CRASH_SERVICE_MAIN_H_
#define SHELL_COMMON_CRASH_REPORTER_WIN_CRASH_SERVICE_MAIN_H_
#include <vector>
namespace crash_service {
// Program entry, should be called by main();
int Main(std::vector<char*>* args);
} // namespace crash_service
#endif // SHELL_COMMON_CRASH_REPORTER_WIN_CRASH_SERVICE_MAIN_H_

View file

@ -25,10 +25,6 @@ const char kSecureProtocolDescription[] =
"The connection to this site is using a strong protocol version "
"and cipher suite.";
#if defined(OS_WIN)
const char kCrashpadPipeName[] = "ELECTRON_CRASHPAD_PIPE_NAME";
#endif
#if BUILDFLAG(ENABLE_RUN_AS_NODE)
const char kRunAsNode[] = "ELECTRON_RUN_AS_NODE";
#endif

View file

@ -25,11 +25,6 @@ extern const char kValidCertificateDescription[];
extern const char kSecureProtocol[];
extern const char kSecureProtocolDescription[];
#if defined(OS_WIN)
// Crashpad pipe name.
extern const char kCrashpadPipeName[];
#endif
#if BUILDFLAG(ENABLE_RUN_AS_NODE)
extern const char kRunAsNode[];
#endif

View file

@ -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 SHELL_BROWSER_ELECTRON_PATHS_H_
#define SHELL_BROWSER_ELECTRON_PATHS_H_
#ifndef SHELL_COMMON_ELECTRON_PATHS_H_
#define SHELL_COMMON_ELECTRON_PATHS_H_
#include "base/base_paths.h"
@ -30,6 +30,8 @@ enum {
DIR_APP_DATA, // Application Data directory under the user profile.
#endif
DIR_CRASH_DUMPS, // c.f. chrome::DIR_CRASH_DUMPS
PATH_END, // End of new paths. Those that follow redirect to base::DIR_*
#if !defined(OS_LINUX)
@ -47,4 +49,4 @@ static_assert(PATH_START < PATH_END, "invalid PATH boundaries");
} // namespace electron
#endif // SHELL_BROWSER_ELECTRON_PATHS_H_
#endif // SHELL_COMMON_ELECTRON_PATHS_H_

View file

@ -0,0 +1,21 @@
// Copyright (c) 2020 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "shell/common/gin_converters/time_converter.h"
#include "base/time/time.h"
namespace gin {
v8::Local<v8::Value> Converter<base::Time>::ToV8(v8::Isolate* isolate,
const base::Time& val) {
v8::Local<v8::Value> date;
if (v8::Date::New(isolate->GetCurrentContext(), val.ToJsTime())
.ToLocal(&date))
return date;
else
return v8::Null(isolate);
}
} // namespace gin

View file

@ -0,0 +1,25 @@
// Copyright (c) 2020 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef SHELL_COMMON_GIN_CONVERTERS_TIME_CONVERTER_H_
#define SHELL_COMMON_GIN_CONVERTERS_TIME_CONVERTER_H_
#include <utility>
#include "gin/converter.h"
namespace base {
class Time;
}
namespace gin {
template <>
struct Converter<base::Time> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, const base::Time& val);
};
} // namespace gin
#endif // SHELL_COMMON_GIN_CONVERTERS_TIME_CONVERTER_H_

View file

@ -37,6 +37,7 @@
V(electron_browser_auto_updater) \
V(electron_browser_browser_view) \
V(electron_browser_content_tracing) \
V(electron_browser_crash_reporter) \
V(electron_browser_dialog) \
V(electron_browser_event) \
V(electron_browser_event_emitter) \
@ -52,15 +53,14 @@
V(electron_browser_system_preferences) \
V(electron_browser_top_level_window) \
V(electron_browser_tray) \
V(electron_browser_view) \
V(electron_browser_web_contents) \
V(electron_browser_web_contents_view) \
V(electron_browser_view) \
V(electron_browser_web_view_manager) \
V(electron_browser_window) \
V(electron_common_asar) \
V(electron_common_clipboard) \
V(electron_common_command_line) \
V(electron_common_crash_reporter) \
V(electron_common_features) \
V(electron_common_native_image) \
V(electron_common_native_theme) \
@ -69,6 +69,7 @@
V(electron_common_shell) \
V(electron_common_v8_util) \
V(electron_renderer_context_bridge) \
V(electron_renderer_crash_reporter) \
V(electron_renderer_ipc) \
V(electron_renderer_web_frame)

View file

@ -286,6 +286,8 @@ const char kEnableSpellcheck[] = "enable-spellcheck";
const char kEnableRemoteModule[] = "enable-remote-module";
#endif
const char kGlobalCrashKeys[] = "global-crash-keys";
} // namespace switches
} // namespace electron

View file

@ -149,6 +149,8 @@ extern const char kEnableSpellcheck[];
extern const char kEnableRemoteModule[];
#endif
extern const char kGlobalCrashKeys[];
} // namespace switches
} // namespace electron

View file

@ -0,0 +1,42 @@
// Copyright (c) 2020 Slack Technologies, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "base/bind.h"
#include "shell/common/crash_keys.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/node_includes.h"
namespace {
v8::Local<v8::Value> GetParameters(v8::Isolate* isolate) {
std::map<std::string, std::string> keys;
#if !defined(MAS_BUILD)
electron::crash_keys::GetCrashKeys(&keys);
#endif
return gin::ConvertToV8(isolate, keys);
}
#if defined(MAS_BUILD)
void SetCrashKeyStub(const std::string& key, const std::string& value) {}
void ClearCrashKeyStub(const std::string& key) {}
#endif
void Initialize(v8::Local<v8::Object> exports,
v8::Local<v8::Value> unused,
v8::Local<v8::Context> context,
void* priv) {
gin_helper::Dictionary dict(context->GetIsolate(), exports);
#if defined(MAS_BUILD)
dict.SetMethod("addExtraParameter", &SetCrashKeyStub);
dict.SetMethod("removeExtraParameter", &ClearCrashKeyStub);
#else
dict.SetMethod("addExtraParameter", &electron::crash_keys::SetCrashKey);
dict.SetMethod("removeExtraParameter", &electron::crash_keys::ClearCrashKey);
#endif
dict.SetMethod("getParameters", &GetParameters);
}
} // namespace
NODE_LINKED_MODULE_CONTEXT_AWARE(electron_renderer_crash_reporter, Initialize)

View file

@ -236,14 +236,6 @@ void ElectronApiServiceImpl::DereferenceRemoteJSCallback(
}
#endif
void ElectronApiServiceImpl::UpdateCrashpadPipeName(
const std::string& pipe_name) {
#if defined(OS_WIN)
std::unique_ptr<base::Environment> env(base::Environment::Create());
env->SetVar(kCrashpadPipeName, pipe_name);
#endif
}
void ElectronApiServiceImpl::TakeHeapSnapshot(
mojo::ScopedHandle file,
TakeHeapSnapshotCallback callback) {

View file

@ -39,7 +39,6 @@ class ElectronApiServiceImpl : public mojom::ElectronRenderer,
void DereferenceRemoteJSCallback(const std::string& context_id,
int32_t object_id) override;
#endif
void UpdateCrashpadPipeName(const std::string& pipe_name) override;
void TakeHeapSnapshot(mojo::ScopedHandle file,
TakeHeapSnapshotCallback callback) override;

View file

@ -4,17 +4,16 @@ import * as http from 'http';
import * as Busboy from 'busboy';
import * as path from 'path';
import { ifdescribe, ifit } from './spec-helpers';
import * as temp from 'temp';
import { app } from 'electron/main';
import { crashReporter } from 'electron/common';
import { AddressInfo } from 'net';
import { EventEmitter } from 'events';
import * as fs from 'fs';
import * as v8 from 'v8';
temp.track();
import * as uuid from 'uuid';
const isWindowsOnArm = process.platform === 'win32' && process.arch === 'arm64';
const isLinuxOnArm = process.platform === 'linux' && process.arch.includes('arm');
const afterTest: ((() => void) | (() => Promise<void>))[] = [];
async function cleanup () {
@ -29,37 +28,41 @@ type CrashInfo = {
prod: string
ver: string
process_type: string // eslint-disable-line camelcase
ptype: string
platform: string
extra1: string
extra2: string
extra3: undefined
_productName: string
_companyName: string
_version: string
upload_file_minidump: Buffer // eslint-disable-line camelcase
mainProcessSpecific: 'mps' | undefined
rendererSpecific: 'rs' | undefined
globalParam: 'globalValue' | undefined
addedThenRemoved: 'to-be-removed' | undefined
longParam: string | undefined
}
function checkCrash (expectedProcessType: string, fields: CrashInfo) {
expect(String(fields.prod)).to.equal('Electron');
expect(String(fields.ver)).to.equal(process.versions.electron);
expect(String(fields.process_type)).to.equal(expectedProcessType);
expect(String(fields.platform)).to.equal(process.platform);
expect(String(fields._productName)).to.equal('Zombies');
expect(String(fields._companyName)).to.equal('Umbrella Corporation');
expect(String(fields._version)).to.equal(app.getVersion());
expect(String(fields.prod)).to.equal('Electron', 'prod');
expect(String(fields.ver)).to.equal(process.versions.electron, 'ver');
expect(String(fields.ptype)).to.equal(expectedProcessType, 'ptype');
expect(String(fields.process_type)).to.equal(expectedProcessType, 'process_type');
expect(String(fields.platform)).to.equal(process.platform, 'platform');
expect(String(fields._productName)).to.equal('Zombies', '_productName');
expect(String(fields._version)).to.equal(app.getVersion(), '_version');
expect(fields.upload_file_minidump).to.be.an.instanceOf(Buffer);
expect(fields.upload_file_minidump.length).to.be.greaterThan(0);
}
function checkCrashExtra (fields: CrashInfo) {
expect(String(fields.extra1)).to.equal('extra1');
expect(String(fields.extra2)).to.equal('extra2');
expect(fields.extra3).to.be.undefined();
// TODO(nornagon): minidumps are sometimes (not always) turning up empty on
// 32-bit Linux. Figure out why.
if (!(process.platform === 'linux' && process.arch === 'ia32')) {
expect(fields.upload_file_minidump.length).to.be.greaterThan(0);
}
}
const startRemoteControlApp = async () => {
const appPath = path.join(__dirname, 'fixtures', 'apps', 'remote-control');
const appProcess = childProcess.spawn(process.execPath, [appPath]);
appProcess.stderr.on('data', d => {
process.stderr.write(d);
});
const port = await new Promise<number>(resolve => {
appProcess.stdout.on('data', d => {
const m = /Listening: (\d+)/.exec(d.toString());
@ -90,8 +93,11 @@ const startRemoteControlApp = async () => {
req.end();
});
}
function remotely (script: Function, ...args: any[]): Promise<any> {
return remoteEval(`(${script})(...${JSON.stringify(args)})`);
}
afterTest.push(() => { appProcess.kill('SIGINT'); });
return { remoteEval };
return { remoteEval, remotely };
};
const startServer = async () => {
@ -123,7 +129,8 @@ const startServer = async () => {
fields[fieldname] = val;
});
busboy.on('finish', () => {
const reportId = 'abc-123-def-456-abc-789-abc-123-abcd';
// breakpad id must be 16 hex digits.
const reportId = Math.random().toString(16).split('.')[1].padStart(16, '0');
res.end(reportId, async () => {
req.socket.destroy();
emitter.emit('crash', { ...fields, ...files });
@ -179,189 +186,400 @@ function waitForNewFileInDir (dir: string): Promise<string[]> {
});
}
// TODO(alexeykuzmin): [Ch66] This test fails on Linux. Fix it and enable back.
ifdescribe(!process.mas && !process.env.DISABLE_CRASH_REPORTER_TESTS && process.platform !== 'linux')('crashReporter module', function () {
// TODO(nornagon): Fix tests on linux/arm.
ifdescribe(!isLinuxOnArm && !process.mas && !process.env.DISABLE_CRASH_REPORTER_TESTS)('crashReporter module', function () {
afterEach(cleanup);
it('should send minidump when renderer crashes', async () => {
const { port, waitForCrash } = await startServer();
runCrashApp('renderer', port);
const crash = await waitForCrash();
checkCrash('renderer', crash);
describe('should send minidump', () => {
it('when renderer crashes', async () => {
const { port, waitForCrash } = await startServer();
runCrashApp('renderer', port);
const crash = await waitForCrash();
checkCrash('renderer', crash);
expect(crash.mainProcessSpecific).to.be.undefined();
});
it('when sandboxed renderer crashes', async () => {
const { port, waitForCrash } = await startServer();
runCrashApp('sandboxed-renderer', port);
const crash = await waitForCrash();
checkCrash('renderer', crash);
expect(crash.mainProcessSpecific).to.be.undefined();
});
// TODO(nornagon): Minidump generation in main/node process on Linux/Arm is
// broken (//components/crash prints "Failed to generate minidump"). Figure
// out why.
ifit(!isLinuxOnArm)('when main process crashes', async () => {
const { port, waitForCrash } = await startServer();
runCrashApp('main', port);
const crash = await waitForCrash();
checkCrash('browser', crash);
expect(crash.mainProcessSpecific).to.equal('mps');
});
ifit(!isLinuxOnArm)('when a node process crashes', async () => {
const { port, waitForCrash } = await startServer();
runCrashApp('node', port);
const crash = await waitForCrash();
checkCrash('node', crash);
expect(crash.mainProcessSpecific).to.be.undefined();
expect(crash.rendererSpecific).to.be.undefined();
});
describe('with extra parameters', () => {
it('when renderer crashes', async () => {
const { port, waitForCrash } = await startServer();
runCrashApp('renderer', port, ['--set-extra-parameters-in-renderer']);
const crash = await waitForCrash();
checkCrash('renderer', crash);
expect(crash.mainProcessSpecific).to.be.undefined();
expect(crash.rendererSpecific).to.equal('rs');
expect(crash.addedThenRemoved).to.be.undefined();
});
it('when sandboxed renderer crashes', async () => {
const { port, waitForCrash } = await startServer();
runCrashApp('sandboxed-renderer', port, ['--set-extra-parameters-in-renderer']);
const crash = await waitForCrash();
checkCrash('renderer', crash);
expect(crash.mainProcessSpecific).to.be.undefined();
expect(crash.rendererSpecific).to.equal('rs');
expect(crash.addedThenRemoved).to.be.undefined();
});
});
});
it('should send minidump when sandboxed renderer crashes', async () => {
const { port, waitForCrash } = await startServer();
runCrashApp('sandboxed-renderer', port);
const crash = await waitForCrash();
checkCrash('renderer', crash);
checkCrashExtra(crash);
ifdescribe(!isLinuxOnArm)('extra parameter limits', () => {
it('should truncate extra values longer than 127 characters', async () => {
const { port, waitForCrash } = await startServer();
const { remotely } = await startRemoteControlApp();
remotely((port: number) => {
require('electron').crashReporter.start({
submitURL: `http://127.0.0.1:${port}`,
ignoreSystemCrashHandler: true,
extra: { 'longParam': 'a'.repeat(130) }
});
setTimeout(() => process.crash());
}, port);
const crash = await waitForCrash();
expect(crash).to.have.property('longParam', 'a'.repeat(127));
});
it('should omit extra keys with names longer than the maximum', async () => {
const kKeyLengthMax = 39;
const { port, waitForCrash } = await startServer();
const { remotely } = await startRemoteControlApp();
remotely((port: number, kKeyLengthMax: number) => {
require('electron').crashReporter.start({
submitURL: `http://127.0.0.1:${port}`,
ignoreSystemCrashHandler: true,
extra: {
['a'.repeat(kKeyLengthMax + 10)]: 'value',
['b'.repeat(kKeyLengthMax)]: 'value',
'not-long': 'not-long-value'
}
});
require('electron').crashReporter.addExtraParameter('c'.repeat(kKeyLengthMax + 10), 'value');
setTimeout(() => process.crash());
}, port, kKeyLengthMax);
const crash = await waitForCrash();
expect(crash).not.to.have.property('a'.repeat(kKeyLengthMax + 10));
expect(crash).not.to.have.property('a'.repeat(kKeyLengthMax));
expect(crash).to.have.property('b'.repeat(kKeyLengthMax), 'value');
expect(crash).to.have.property('not-long', 'not-long-value');
expect(crash).not.to.have.property('c'.repeat(kKeyLengthMax + 10));
expect(crash).not.to.have.property('c'.repeat(kKeyLengthMax));
});
});
it('should send minidump with updated parameters when renderer crashes', async () => {
const { port, waitForCrash } = await startServer();
runCrashApp('renderer', port, ['--set-extra-parameters-in-renderer']);
const crash = await waitForCrash();
checkCrash('renderer', crash);
expect(crash.extra1).to.be.undefined();
expect(crash.extra2).to.equal('extra2');
expect(crash.extra3).to.equal('added');
describe('globalExtra', () => {
ifit(!isLinuxOnArm)('should be sent with main process dumps', async () => {
const { port, waitForCrash } = await startServer();
runCrashApp('main', port, ['--add-global-param=globalParam:globalValue']);
const crash = await waitForCrash();
expect(crash.globalParam).to.equal('globalValue');
});
it('should be sent with renderer process dumps', async () => {
const { port, waitForCrash } = await startServer();
runCrashApp('renderer', port, ['--add-global-param=globalParam:globalValue']);
const crash = await waitForCrash();
expect(crash.globalParam).to.equal('globalValue');
});
it('should be sent with sandboxed renderer process dumps', async () => {
const { port, waitForCrash } = await startServer();
runCrashApp('sandboxed-renderer', port, ['--add-global-param=globalParam:globalValue']);
const crash = await waitForCrash();
expect(crash.globalParam).to.equal('globalValue');
});
ifit(!isLinuxOnArm)('should not be overridden by extra in main process', async () => {
const { port, waitForCrash } = await startServer();
runCrashApp('main', port, ['--add-global-param=mainProcessSpecific:global']);
const crash = await waitForCrash();
expect(crash.mainProcessSpecific).to.equal('global');
});
ifit(!isLinuxOnArm)('should not be overridden by extra in renderer process', async () => {
const { port, waitForCrash } = await startServer();
runCrashApp('main', port, ['--add-global-param=rendererSpecific:global']);
const crash = await waitForCrash();
expect(crash.rendererSpecific).to.equal('global');
});
});
it('should send minidump with updated parameters when sandboxed renderer crashes', async () => {
const { port, waitForCrash } = await startServer();
runCrashApp('sandboxed-renderer', port, ['--set-extra-parameters-in-renderer']);
const crash = await waitForCrash();
checkCrash('renderer', crash);
expect(crash.extra1).to.be.undefined();
expect(crash.extra2).to.equal('extra2');
expect(crash.extra3).to.equal('added');
});
it('should send minidump when main process crashes', async () => {
const { port, waitForCrash } = await startServer();
runCrashApp('main', port);
const crash = await waitForCrash();
checkCrash('browser', crash);
checkCrashExtra(crash);
});
it('should send minidump when a node process crashes', async () => {
const { port, waitForCrash } = await startServer();
runCrashApp('node', port);
const crash = await waitForCrash();
checkCrash('node', crash);
checkCrashExtra(crash);
});
// TODO(jeremy): re-enable on woa
// TODO(nornagon): also test crashing main / sandboxed renderers.
ifit(!isWindowsOnArm)('should not send a minidump when uploadToServer is false', async () => {
const { port, getCrashes } = await startServer();
const crashesDir = path.join(app.getPath('temp'), 'Zombies Crashes');
const completedCrashesDir = path.join(crashesDir, 'completed');
const crashAppeared = waitForNewFileInDir(completedCrashesDir);
const { port, waitForCrash, getCrashes } = await startServer();
waitForCrash().then(() => expect.fail('expected not to receive a dump'));
await runCrashApp('renderer', port, ['--no-upload']);
await crashAppeared;
// wait a sec in case crashpad is about to upload a crash
// wait a sec in case the crash reporter is about to upload a crash
await new Promise(resolve => setTimeout(resolve, 1000));
expect(getCrashes()).to.have.length(0);
});
describe('start() option validation', () => {
it('requires that the companyName option be specified', () => {
it('requires that the submitURL option be specified', () => {
expect(() => {
crashReporter.start({ companyName: 'dummy' } as any);
crashReporter.start({} as any);
}).to.throw('submitURL is a required option to crashReporter.start');
});
it('requires that the submitURL option be specified', () => {
expect(() => {
crashReporter.start({ submitURL: 'dummy' } as any);
}).to.throw('companyName is a required option to crashReporter.start');
});
it('can be called twice', async () => {
const { remoteEval } = await startRemoteControlApp();
await remoteEval(`require('electron').crashReporter.start({companyName: "Umbrella Corporation", submitURL: "http://127.0.0.1"})`);
await remoteEval(`require('electron').crashReporter.start({companyName: "Umbrella Corporation", submitURL: "http://127.0.0.1"})`);
});
});
describe('getCrashesDirectory', () => {
it('correctly returns the directory', async () => {
const { remoteEval } = await startRemoteControlApp();
await remoteEval(`require('electron').crashReporter.start({companyName: "Umbrella Corporation", submitURL: "http://127.0.0.1"})`);
const crashesDir = await remoteEval(`require('electron').crashReporter.getCrashesDirectory()`);
const dir = path.join(app.getPath('temp'), 'remote-control Crashes');
expect(crashesDir).to.equal(dir);
const { remotely } = await startRemoteControlApp();
await expect(remotely(() => {
const { crashReporter } = require('electron');
crashReporter.start({ submitURL: 'http://127.0.0.1' });
crashReporter.start({ submitURL: 'http://127.0.0.1' });
})).to.be.fulfilled();
});
});
describe('getUploadedReports', () => {
it('returns an array of reports', async () => {
const { remoteEval } = await startRemoteControlApp();
await remoteEval(`require('electron').crashReporter.start({companyName: "Umbrella Corporation", submitURL: "http://127.0.0.1"})`);
const reports = await remoteEval(`require('electron').crashReporter.getUploadedReports()`);
const { remotely } = await startRemoteControlApp();
await remotely(() => {
require('electron').crashReporter.start({ submitURL: 'http://127.0.0.1' });
});
const reports = await remotely(() => require('electron').crashReporter.getUploadedReports());
expect(reports).to.be.an('array');
});
});
// TODO(jeremy): re-enable on woa
// TODO(nornagon): re-enable on woa
ifdescribe(!isWindowsOnArm)('getLastCrashReport', () => {
it('returns the last uploaded report', async () => {
const { remoteEval } = await startRemoteControlApp();
const { remotely } = await startRemoteControlApp();
const { port, waitForCrash } = await startServer();
// 0. clear the crash reports directory.
const dir = path.join(app.getPath('temp'), 'remote-control Crashes');
const dir = await remotely(() => require('electron').app.getPath('crashDumps'));
try {
fs.rmdirSync(dir, { recursive: true });
fs.mkdirSync(dir);
} catch (e) { /* ignore */ }
// 1. start the crash reporter.
await remoteEval(`require('electron').crashReporter.start({companyName: "Umbrella Corporation", submitURL: "http://127.0.0.1:${port}", ignoreSystemCrashHandler: true})`);
// 2. generate a crash.
remoteEval(`(function() { const {BrowserWindow} = require('electron'); const bw = new BrowserWindow({show: false, webPreferences: {nodeIntegration: true}}); bw.loadURL('about:blank'); bw.webContents.executeJavaScript('process.crash()') })()`);
await remotely((port: number) => {
require('electron').crashReporter.start({
submitURL: `http://127.0.0.1:${port}`,
ignoreSystemCrashHandler: true
});
}, [port]);
// 2. generate a crash in the renderer.
remotely(() => {
const { BrowserWindow } = require('electron');
const bw = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } });
bw.loadURL('about:blank');
bw.webContents.executeJavaScript('process.crash()');
});
await waitForCrash();
// 3. get the crash from getLastCrashReport.
const firstReport = await remoteEval(`require('electron').crashReporter.getLastCrashReport()`);
const firstReport = await remotely(() => require('electron').crashReporter.getLastCrashReport());
expect(firstReport).to.not.be.null();
expect(firstReport.date).to.be.an.instanceOf(Date);
expect((+new Date()) - (+firstReport.date)).to.be.lessThan(30000);
});
});
describe('getUploadToServer()', () => {
it('returns true when uploadToServer is set to true (by default)', async () => {
const { remoteEval } = await startRemoteControlApp();
const { remotely } = await startRemoteControlApp();
await remoteEval(`require('electron').crashReporter.start({companyName: "Umbrella Corporation", submitURL: "http://127.0.0.1"})`);
const uploadToServer = await remoteEval(`require('electron').crashReporter.getUploadToServer()`);
await remotely(() => { require('electron').crashReporter.start({ submitURL: 'http://127.0.0.1' }); });
const uploadToServer = await remotely(() => require('electron').crashReporter.getUploadToServer());
expect(uploadToServer).to.be.true();
});
it('returns false when uploadToServer is set to false in init', async () => {
const { remoteEval } = await startRemoteControlApp();
await remoteEval(`require('electron').crashReporter.start({companyName: "Umbrella Corporation", submitURL: "http://127.0.0.1", uploadToServer: false})`);
const uploadToServer = await remoteEval(`require('electron').crashReporter.getUploadToServer()`);
const { remotely } = await startRemoteControlApp();
await remotely(() => { require('electron').crashReporter.start({ submitURL: 'http://127.0.0.1', uploadToServer: false }); });
const uploadToServer = await remotely(() => require('electron').crashReporter.getUploadToServer());
expect(uploadToServer).to.be.false();
});
it('is updated by setUploadToServer', async () => {
const { remoteEval } = await startRemoteControlApp();
await remoteEval(`require('electron').crashReporter.start({companyName: "Umbrella Corporation", submitURL: "http://127.0.0.1"})`);
await remoteEval(`require('electron').crashReporter.setUploadToServer(false)`);
expect(await remoteEval(`require('electron').crashReporter.getUploadToServer()`)).to.be.false();
await remoteEval(`require('electron').crashReporter.setUploadToServer(true)`);
expect(await remoteEval(`require('electron').crashReporter.getUploadToServer()`)).to.be.true();
const { remotely } = await startRemoteControlApp();
await remotely(() => { require('electron').crashReporter.start({ submitURL: 'http://127.0.0.1' }); });
await remotely(() => { require('electron').crashReporter.setUploadToServer(false); });
expect(await remotely(() => require('electron').crashReporter.getUploadToServer())).to.be.false();
await remotely(() => { require('electron').crashReporter.setUploadToServer(true); });
expect(await remotely(() => require('electron').crashReporter.getUploadToServer())).to.be.true();
});
});
describe('Parameters', () => {
describe('getParameters', () => {
it('returns all of the current parameters', async () => {
const { remoteEval } = await startRemoteControlApp();
await remoteEval(`require('electron').crashReporter.start({companyName: "Umbrella Corporation", submitURL: "http://127.0.0.1", extra: {"extra1": "hi"}})`);
const parameters = await remoteEval(`require('electron').crashReporter.getParameters()`);
expect(parameters).to.be.an('object');
expect(parameters.extra1).to.equal('hi');
const { remotely } = await startRemoteControlApp();
await remotely(() => {
require('electron').crashReporter.start({
submitURL: 'http://127.0.0.1',
extra: { 'extra1': 'hi' }
});
});
const parameters = await remotely(() => require('electron').crashReporter.getParameters());
expect(parameters).to.have.property('extra1', 'hi');
});
it('adds and removes parameters', async () => {
const { remoteEval } = await startRemoteControlApp();
await remoteEval(`require('electron').crashReporter.start({companyName: "Umbrella Corporation", submitURL: "http://127.0.0.1"})`);
await remoteEval(`require('electron').crashReporter.addExtraParameter('hello', 'world')`);
it('reflects added and removed parameters', async () => {
const { remotely } = await startRemoteControlApp();
await remotely(() => {
require('electron').crashReporter.start({ submitURL: 'http://127.0.0.1' });
require('electron').crashReporter.addExtraParameter('hello', 'world');
});
{
const parameters = await remoteEval(`require('electron').crashReporter.getParameters()`);
expect(parameters).to.have.property('hello');
expect(parameters.hello).to.equal('world');
const parameters = await remotely(() => require('electron').crashReporter.getParameters());
expect(parameters).to.have.property('hello', 'world');
}
await remotely(() => { require('electron').crashReporter.removeExtraParameter('hello'); });
{
await remoteEval(`require('electron').crashReporter.removeExtraParameter('hello')`);
const parameters = await remoteEval(`require('electron').crashReporter.getParameters()`);
const parameters = await remotely(() => require('electron').crashReporter.getParameters());
expect(parameters).not.to.have.property('hello');
}
});
it('can be called in the renderer', async () => {
const { remotely } = await startRemoteControlApp();
const rendererParameters = await remotely(async () => {
const { crashReporter, BrowserWindow } = require('electron');
crashReporter.start({ submitURL: 'http://' });
const bw = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } });
bw.loadURL('about:blank');
await bw.webContents.executeJavaScript(`require('electron').crashReporter.addExtraParameter('hello', 'world')`);
return bw.webContents.executeJavaScript(`require('electron').crashReporter.getParameters()`);
});
if (process.platform === 'linux') {
// On Linux, 'getParameters' will also include the global parameters,
// because breakpad doesn't support global parameters.
expect(rendererParameters).to.have.property('hello', 'world');
} else {
expect(rendererParameters).to.deep.equal({ hello: 'world' });
}
});
it('can be called in a node child process', async () => {
function slurp (stream: NodeJS.ReadableStream): Promise<string> {
return new Promise((resolve, reject) => {
const chunks: Buffer[] = [];
stream.on('data', chunk => { chunks.push(chunk); });
stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
stream.on('error', e => reject(e));
});
}
const child = childProcess.fork(path.join(__dirname, 'fixtures', 'module', 'print-crash-parameters.js'), [], { silent: true });
const output = await slurp(child.stdout!);
expect(JSON.parse(output)).to.deep.equal({ hello: 'world' });
});
});
describe('crash dumps directory', () => {
it('is set by default', () => {
expect(app.getPath('crashDumps')).to.be.a('string');
});
it('is inside the user data dir', () => {
expect(app.getPath('crashDumps')).to.include(app.getPath('userData'));
});
it('matches getCrashesDirectory', async () => {
expect(app.getPath('crashDumps')).to.equal(require('electron').crashReporter.getCrashesDirectory());
});
function crash (processType: string, remotely: Function) {
if (processType === 'main') {
return remotely(() => {
setTimeout(() => { process.crash(); });
});
} else if (processType === 'renderer') {
return remotely(() => {
const { BrowserWindow } = require('electron');
const bw = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } });
bw.loadURL('about:blank');
bw.webContents.executeJavaScript('process.crash()');
});
} else if (processType === 'sandboxed-renderer') {
const preloadPath = path.join(__dirname, 'fixtures', 'apps', 'crash', 'sandbox-preload.js');
return remotely((preload: string) => {
const { BrowserWindow } = require('electron');
const bw = new BrowserWindow({ show: false, webPreferences: { sandbox: true, preload } });
bw.loadURL('about:blank');
}, preloadPath);
} else if (processType === 'node') {
const crashScriptPath = path.join(__dirname, 'fixtures', 'apps', 'crash', 'node-crash.js');
return remotely((crashScriptPath: string) => {
const { app } = require('electron');
const childProcess = require('child_process');
const version = app.getVersion();
const url = 'http://127.0.0.1';
childProcess.fork(crashScriptPath, [url, version], { silent: true });
}, crashScriptPath);
}
}
for (const crashingProcess of ['main', 'renderer', 'sandboxed-renderer', 'node']) {
// TODO(nornagon): breakpad on linux disables itself when uploadToServer
// is false, so we should figure out a different way to test the crash
// dump dir on linux.
ifdescribe(process.platform !== 'linux')(`when ${crashingProcess} crashes`, () => {
it('stores crashes in the crash dump directory when uploadToServer: false', async () => {
const { remotely } = await startRemoteControlApp();
const crashesDir = await remotely(() => {
const { crashReporter } = require('electron');
crashReporter.start({ submitURL: 'http://127.0.0.1', uploadToServer: false, ignoreSystemCrashHandler: true });
return crashReporter.getCrashesDirectory();
});
const reportsDir = process.platform === 'darwin' ? path.join(crashesDir, 'completed') : path.join(crashesDir, 'reports');
const newFileAppeared = waitForNewFileInDir(reportsDir);
crash(crashingProcess, remotely);
const newFiles = await newFileAppeared;
expect(newFiles).to.have.length(1);
expect(newFiles[0]).to.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.dmp$/);
});
it('respects an overridden crash dump directory', async () => {
const { remotely } = await startRemoteControlApp();
const crashesDir = path.join(app.getPath('temp'), uuid.v4());
const remoteCrashesDir = await remotely((crashesDir: string) => {
const { crashReporter, app } = require('electron');
app.setPath('crashDumps', crashesDir);
crashReporter.start({ submitURL: 'http://127.0.0.1', uploadToServer: false, ignoreSystemCrashHandler: true });
return crashReporter.getCrashesDirectory();
}, crashesDir);
expect(remoteCrashesDir).to.equal(crashesDir);
const reportsDir = process.platform === 'darwin' ? path.join(crashesDir, 'completed') : path.join(crashesDir, 'reports');
const newFileAppeared = waitForNewFileInDir(reportsDir);
crash(crashingProcess, remotely);
const newFiles = await newFileAppeared;
expect(newFiles).to.have.length(1, `Files that appeared: ${JSON.stringify(newFiles)}`);
expect(newFiles[0]).to.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.dmp$/);
});
});
}
});
describe('when not started', () => {

View file

@ -7,6 +7,7 @@ app.setVersion('0.1.0');
const url = app.commandLine.getSwitchValue('crash-reporter-url');
const uploadToServer = !app.commandLine.hasSwitch('no-upload');
const setExtraParameters = app.commandLine.hasSwitch('set-extra-parameters-in-renderer');
const addGlobalParam = app.commandLine.getSwitchValue('add-global-param')?.split(':');
crashReporter.start({
productName: 'Zombies',
@ -15,9 +16,9 @@ crashReporter.start({
submitURL: url,
ignoreSystemCrashHandler: true,
extra: {
extra1: 'extra1',
extra2: 'extra2'
}
mainProcessSpecific: 'mps'
},
globalExtra: addGlobalParam[0] ? { [addGlobalParam[0]]: addGlobalParam[1] } : {}
});
app.whenReady().then(() => {
@ -28,21 +29,11 @@ app.whenReady().then(() => {
} else if (crashType === 'renderer') {
const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } });
w.loadURL('about:blank');
w.webContents.executeJavaScript(`require('electron').crashReporter.start({
productName: 'Zombies',
companyName: 'Umbrella Corporation',
uploadToServer: true,
ignoreSystemCrashHandler: true,
submitURL: '',
extra: {
'extra1': 'extra1',
'extra2': 'extra2',
}
})`);
if (setExtraParameters) {
w.webContents.executeJavaScript(`
require('electron').crashReporter.addExtraParameter('extra3', 'added');
require('electron').crashReporter.removeExtraParameter('extra1');
require('electron').crashReporter.addExtraParameter('rendererSpecific', 'rs');
require('electron').crashReporter.addExtraParameter('addedThenRemoved', 'to-be-removed');
require('electron').crashReporter.removeExtraParameter('addedThenRemoved');
`);
}
w.webContents.executeJavaScript('process.crash()');

View file

@ -1,20 +1,10 @@
process.crashReporter.start({
productName: 'Zombies',
companyName: 'Umbrella Corporation',
crashesDirectory: process.argv[4],
submitURL: process.argv[2],
ignoreSystemCrashHandler: true,
extra: {
extra1: 'extra1',
extra2: 'extra2',
_version: process.argv[3]
}
});
if (process.platform !== 'linux') {
process.crashReporter.addExtraParameter('newExtra', 'newExtra');
process.crashReporter.addExtraParameter('removeExtra', 'removeExtra');
process.crashReporter.removeExtraParameter('removeExtra');
if (process.platform === 'linux') {
process.crashReporter.start({
submitURL: process.argv[2],
productName: 'Zombies',
globalExtra: {
_version: process.argv[3]
}
});
}
process.nextTick(() => process.crash());

View file

@ -1,18 +1,10 @@
const { crashReporter } = require('electron');
crashReporter.start({
productName: 'Zombies',
companyName: 'Umbrella Corporation',
uploadToServer: true,
ignoreSystemCrashHandler: true,
submitURL: '',
extra: {
'extra1': 'extra1',
'extra2': 'extra2'
}
});
const params = new URLSearchParams(location.search);
if (params.get('set_extra') === '1') {
crashReporter.addExtraParameter('extra3', 'added');
crashReporter.removeExtraParameter('extra1');
crashReporter.addExtraParameter('rendererSpecific', 'rs');
crashReporter.addExtraParameter('addedThenRemoved', 'to-be-removed');
crashReporter.removeExtraParameter('addedThenRemoved');
}
process.crash();

View file

@ -6,12 +6,14 @@ const server = http.createServer((req, res) => {
req.on('data', chunk => { chunks.push(chunk); });
req.on('end', () => {
const js = Buffer.concat(chunks).toString('utf8');
try {
const result = eval(js); // eslint-disable-line no-eval
res.end(v8.serialize({ result }));
} catch (e) {
res.end(v8.serialize({ error: e.stack }));
}
(async () => {
try {
const result = await Promise.resolve(eval(js)); // eslint-disable-line no-eval
res.end(v8.serialize({ result }));
} catch (e) {
res.end(v8.serialize({ error: e.stack }));
}
})();
});
}).listen(0, '127.0.0.1', () => {
process.stdout.write(`Listening: ${server.address().port}\n`);

View file

@ -0,0 +1,2 @@
process.crashReporter.addExtraParameter('hello', 'world');
process.stdout.write(JSON.stringify(process.crashReporter.getParameters()) + '\n');