Crash Reporting

Draft Community Group Report,

This version:
https://wicg.github.io/crash-reporting/
Version History:
https://github.com/WICG/crash-reporting/commits/gh-pages
Editor:
(Google Inc.)
Participate:
File an issue (open issues)
Test Suite:
https://wpt.fyi/results/reporting/

Abstract

This document defines mechanism for reporing browser crashes to site owners through the use of the Reporting API.

Status of this document

This specification was published by the Web Platform Incubator Community Group. It is not a W3C Standard nor is it on the W3C Standards Track. Please note that under the W3C Community Contributor License Agreement (CLA) there is a limited opt-out and other conditions apply. Learn more about W3C Community and Business Groups.

1. Introduction

[INTRODUCTION GOES HERE]

1.1. Examples

Unstable, Inc. wants to understand better how often its website is crashing in the wild. It can do this by delivering the following header to define a default reporting endpoint, which will direct crash reports there:
Reporting-Endpoints: default="https://example.com/reports"

2. Concepts

2.1. Crash

2.2. Out-of-Memory

2.3. Unresponsive

3. Crash Reports

Crash reports indicate that the user was unable to continue using the page because the browser (or one of its processes necessary for the page) crashed. For security reasons, no details of the crash are communicated except for a unique identifier (which can be interpreted by the browser vendor), and optionally the reason for the crash (such as "oom").

Crash reports are a type of report.

Crash reports have the report type "crash".

dictionary CrashReportBody : ReportBody {
  DOMString reason;
  DOMString stack;
  boolean is_top_level;
  DocumentVisibilityState visibility_state;
  object crash_report_api;
};

A crash report’s body, represented in JavaScript by CrashReportBody, contains the following fields:

Reason Description
oom The page ran out of memory.
unresponsive The page was killed due to being unresponsive.

Note: Crash reports are not observable to JavaScript, as the page which would receive them is, by definition, not able to. The IDL description of CrashReportBody exists in this spec to provide a JSON-seriaizable interface whose serialization can be embedded in the out-of-band reports.

3.1. Crash Reports Delivery Priority

If a crash-reporting endpoint is specified, crash reports are delivered to it.

Otherwise, if a default endpoint is specified, crash reports are delivered to it.

If neither endpoint is specified, crash reports are not delivered.

Reporting-Endpoints: crash-reporting="https://example.com/reports"

4. The CrashReportContext interface

partial interface Window {
  readonly attribute CrashReportContext crashReport;
};

Each Window object has an associated crashReport, which is a CrashReportContext instance created alongside the Window.

The crashReport getter steps are:
  1. If this’s relevant global object’s associated Document is not fully active, then return null.

  2. Return this’s relevant global object’s crashReport object.

[Exposed=Window]
interface CrashReportContext {
    Promise<undefined> initialize(unsigned long long length);
    undefined set(DOMString key, DOMString value);
    undefined delete(DOMString key);
};

Each CrashReportContext object has an associated internal map, which is a map whose keys and values are both DOMStrings.

The user agent has an associated crash report buffers, which is a map whose keys are unique internal values and whose values are implementation-defined values.

Note: The internal map is a map-representation of the data that will eventually appear in a crash report, and it exists to ergonomically interact with this data via map semantics. But implementations of this specification, especially those supporting site isolation, will likely ultimately serialize its entire contents to a backing shared memory buffer tracked by the user agent’s crash report buffers map. The values of that map are expected to be those memory buffers that span the web process and whatever OS process is responsible for sending crash reports when a web process crashes, and its internal map is no longer available to read from.

Each CrashReportContext object has an associated backing buffer ID, which is a null-or-unique internal value; it is initially null.

Each CrashReportContext object has an associated is buffer initialized boolean, which is initially false.

Note: This member is asynchronously set to true when the backing memory buffer associated with this’s backing buffer ID is initialized.

Each CrashReportContext object has an associated unsigned long long buffer length, which is initially 0.

Note: This gets assigned once in initialize(), and tracks the maximum number of bytes that any call to set() can write to the memory backing internal map.

The internal map is used to hold all keys/values that will be supplied to the developer via CrashReportBodys. In browsers with site isolation, the physical process that writes to this data is different from the one that reads from it and sends the crash reports. Therefore, it is expected that implementations maintain this map as a shared memory buffer spanning the two processes. This is what the Chromium implementation of this API does.

The initialize(length) method steps are:
  1. If this’s relevant global object’s associated Document is not fully active, then throw a new "InvalidStateError" DOMException.

    Note: This will be surfaced to the user via a rejected Promise.

  2. If this’s backing buffer ID is non-null, then throw a new "InvalidStateError" DOMException.

  3. If length is > than 5 * 1024 * 1024, then throw a new "NotAllowedError" DOMException.

    Note: The limit for the crash report memory buffer is 5 megabytes.

  4. Set this’s buffer length to length.

  5. Set this’s backing buffer ID to the result of creating a new unique internal value.

  6. Let cachedThis be this.

  7. Let promise be a a new promise.

  8. Run these steps in parallel:

    1. Let backing buffer ID be cachedThis’s backing buffer ID.

    2. Run implementation-defined steps to create and initialize a backing memory store of length bytes.

    3. If such a buffer was created, then:

      1. Set the user agent’s crash report buffers[backing buffer ID] to the buffer.

      2. Set cachedThis’s is buffer initialized to true.

    4. Queue a global task on the DOM manipulation task source given cachedThis’s relevant global object, to resolve promise with undefined.

  9. Return promise.

The set(key, value) method steps are:
  1. If this’s relevant global object’s associated Document is not fully active, then throw a new "InvalidStateError" DOMException.

  2. If this’s is buffer initialized is false, then throw a new "InvalidStateError" DOMException.

    Note: This will be false both before initialize() is called, and after it is called but before its returned Promise resolves. It ensures that set() only works when the backing buffer identified by the backing buffer ID is fully initialized.

  3. Set this’s internal map[key] to value.

  4. Let serialization be the result of serializing a JavaScript value to JSON bytes, given this’s internal map.

  5. If serialization’s size is > this’s buffer length, then:

    1. Remove this’s internal map[key].

    2. Throw a new "NotAllowedError" DOMException.

  6. Let backing buffer ID be this’s backing buffer ID.

  7. Write serialization to the user agent’s crash report buffers[backing buffer ID].

The delete(key) method steps are:
  1. If this’s relevant global object’s associated Document is not fully active, then throw a new "InvalidStateError" DOMException.

  2. If this’s is buffer initialized is false, then throw a new "InvalidStateError" DOMException.

  3. Remove this’s internal map[key].

  4. Let serialization be the result of serializing a JavaScript value to JSON bytes, given this’s internal map.

  5. Let backing buffer ID be this’s backing buffer ID.

  6. Write serialization to the user agent’s crash report buffers[backing buffer ID].

5. Document Policy Integration

Site authors may opt in to recording the JavaScript call stack with some reports.

This spec defines a configuration point with the name include-js-call-stacks-in-crash-reports. Its type is boolean, and its default value is false. When configured as true in a Document for which crash reporting is enabled, this will cause a string representation of the JavaScript call stack, at the time of the crash, to be included in a crash report.

Document-Policy: include-js-call-stacks-in-crash-reports

6. Implementation Considerations

6.1. Delivery

[REPORTING], which defines the framework on which this specification depends, provides at most a best-efforts delivery mechanism. This is especially true when it comes to reporting crashes. There are probably always going to be certain crash conditions which simply cannot be reported on (for instance, if the crash occurs within the crash-monitoring code of the user agent, or if the computer hosting the user agent were to suddenly cease to exist). However, many crashes can be observed by modern browsers, and their immediate causes can be deduced.

A user agent implementing crash reports SHOULD attempt to monitor documents for crashes in a way that will continue to function even when the process which is responsible for that document crashes, or is terminated by the operating system.

There are multiple ways to implement such a monitor, with varying levels of reliability and robustness to specific crash causes, and this specification does not attempt to prescribe any particular such method.

7. Sample Reports

POST /reports HTTP/1.1
Host: example.com
...
Content-Type: application/reports+json

[{
  "type": "crash",
  "age": 42,
  "url": "https://example.com/",
  "user_agent": "Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0",
  "body": {
    "reason": "oom"
  }
}]

8. Security Considerations

For a discussion of security considerations surrounding out-of-band reporting in general, see Reporting API § 8 Security Considerations.

The remainder of this section discusses security considerations for crash reporting specifically.

9. Privacy Considerations

For a discussion of privacy considerations surrounding out-of-band reporting in general, see Reporting API § 9 Privacy Considerations.

The remainder of this section discusses privacy considerations for crash reporting specifically.

9.1. Cross-Process Contamination

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[DOCUMENT-POLICY]
Document Policy. Draft Community Group Report. URL: https://wicg.github.io/document-policy/
[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[REPORTING]
Douglas Creager; Ian Clelland; Mike West. Reporting API. URL: https://w3c.github.io/reporting/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/

IDL Index

dictionary CrashReportBody : ReportBody {
  DOMString reason;
  DOMString stack;
  boolean is_top_level;
  DocumentVisibilityState visibility_state;
  object crash_report_api;
};

partial interface Window {
  readonly attribute CrashReportContext crashReport;
};

[Exposed=Window]
interface CrashReportContext {
    Promise<undefined> initialize(unsigned long long length);
    undefined set(DOMString key, DOMString value);
    undefined delete(DOMString key);
};