Plugins:PluginBugReporting

From MozillaWiki
Jump to: navigation, search

Status

Tentative consideration.

Background and Summary

User agents (UAs) that load plugins, and execute plugins' code, in a separate OS process from web content and UI have an opportunity to more quickly and accurately pinpoint crasher bugs involving plugins. In the simplest case of one plugin module hosted per OS process, if the process hosting the plugin crashes, one might assume that the crash was probably due to a bug in the plugin module. The rationale is that the UA code hosting the plugin is likely to be simpler than the plugin's code.

The following proposal is really only directed at UAs that run plugins out of process (OOPP), because it is difficult to impossible to perform the same kind of crash forensics afforded by OOPP in real time after a single-process UA crash.

Even with OOPP, it would annoy users when the plugin host process unexpectedly quits (for whatever reason) because all open tabs containing instances of the dead plugin would stop functioning. Granted, this is a vast UX improvement compared to single-process UA crashes.

This proposal explores the following hypothetical.

If the new crash forensics afforded OOPP could be utilized by plugin vendors to improve plugin quality, how could NPAPI be extended to help plugin vendors access the new data?

So, the two main criteria for accepting this proposal seem to be

  1. Can plugin vendors compellingly demonstrate how better crash forensics facilitated by OOPP would improve plugin quality?
  2. Can UAs agree on an NPAPI extension to provide this better crash information to plugin vendors while still maintaining user privacy?

This proposal focuses on the second question by describing an NPAPI extension that allows plugin vendors to supply a custom bug-reporting URL. UAs would use this bug reporting URL to give users the option of sending a bug report to a plugin vendor upon a plugin-process crash, in addition to whatever crash reporting the UA does itself.

Proposed NPAPI Extension

Loosely, the extension allows a UA to ask a plugin, "Hey plugin, if we (the UA) think you've crashed, do you have a URL to which we send a GET request with information you want to report about the crash?" Please see the usage and UI examples below.

More concretely, the proposal is to

  • Add a new NPP variable NPPVbugReportURL that the browser can query with NPP_GetValue() and a plugin instance (NPP) can set with NPP_SetValue().
  • Optionally allow the bug report URL to contain special formatting strings expanded by the UA.

The full proposed extension is given below as a patch against Mozilla's npapi.h header.

--- npapi.h
+++ npapi.h
@@ -326,16 +326,47 @@ typedef enum {
   
   NPPVpluginUrlRequestsDisplayedBool = 17,
   
   /* Checks if the plugin is interested in receiving the http body of
    * all http requests (including failed ones, http status != 200).
    */
   NPPVpluginWantsAllNetworkStreams = 18
 
+  /* If Mozilla determines with reasonable certainty that this plugin
+   * might be responsible for a crash, and a plugin instance (NPP) has
+   * supplied a bug reporting URL through NPP_GetValue() or
+   * NPN_SetValue() of the NPPVariable below, then Mozilla will give
+   * the user the option of submitting a crash report to this URL in
+   * addition to the Mozilla crash report.
+   *
+   * The URL may contain any of the special "formatting strings" (a la
+   * printf() format strings) below
+   *
+   *   - ${url} : URL of the page in which the plugin instance was loaded
+   *   - ${stackString} : stringified crash backtrace
+   *   - ${stackURL} : link to crash report in external database
+   *
+   * For example, "http://crash-report.vendor.com/submit?url=${url}".
+   *
+   * Before a GET request is made to the expanded URL, it is
+   * escaped.
+   *
+   * The the vendor may optionally respond to the GET request with
+   * HTML to be shown to the user (e.g. with links to upgrade the
+   * plugin module itself).
+   *
+   * If an unknown format string (with syntax /\$\{[^}]\}/) is
+   * included in the URL, or the user chooses not to disclose the
+   * information described by a known format string (e.g., doesn't
+   * want the page's URL to be submitted), then the format string will
+   * be exanded with "".
+   *
+   * This variable can be used in the following two ways.
+   *
+   *   NPP_GetValue(NPPVbugReportURL, char** transferBugReportURL);
+   *   NPN_SetValue(NPPVbugReportURL, char** transferBugReportURL);
+   *
+   * In both cases the |char*| should be a NULL-terminated UTF8 string
+   * that is dynamically allocated.  For NPP_GetValue(), Mozilla 
+   * unconditionally assumes ownership of the string's memory if the
+   * returned string is non-NULL.  For NPP_SetValue(), the plugin
+   * may assume that Mozilla assumed ownership of the string only if
+   * if |*transferBugReportURL| is set to NULL.
+   */
+  NPPVbugReportURL,
+
 #ifdef XP_MACOSX
   /* Used for negotiating drawing models */
   , NPPVpluginDrawingModel = 1000
   /* Used for negotiating event models */
   , NPPVpluginEventModel = 1001
 #endif
 } NPPVariable;

Special URL formatting strings

The bug report URL format could be extended with special "formatting strings" expanded by the browser. Please see full description above.

The intention is twofold

  1. Make common information more conveniently accessible to the plugin.
  2. Allow the browser to selectively expand format strings according to user preferences.

For example, if the user doesn't want to submit the crashed page's URL along with a browser crash report, then the browser would not expand the ${url} formatting string in the plugin's bug report (in the case where the user indeed chose to send a plugin bug report).

Example API usage

First case

// browser code: launching a new plugin instance
NPP_New(...);

char* bugReportURL = 0;
NPP_GetValue(NPPVbugReportURL, (void*) &bugReportURL);

if (bugReportURL)
  // save the bug report URL for use in case of a crash

Second case

// plugin code: a new event has occurred relevant to potential future crashes
char* myBugReportURL = malloc_printf("http://...");
NPN_SetValue(NPPVbugReportURL, (void*) &myBugReportURL);

if (myBugReportURL)
  free(myBugReportURL);  // browser didn't assume ownership

Example user-facing changes in UAs

Consider the following scenario in a UA that has implemented OOPP. An imaginary plugin module Foo is used for illustrative purposes.

  1. The UA launches an OS process to host libfooplugin.so
  2. The UA initializes Foo
  3. The user navigates to http://webpage.com
  4. http://webpage.com contains an object frame that loads an instance of Foo
  5. The UA loads Foo and invokes NPP_GetValue(NPPVbugReportURL). Foo returns the URL http://crash-report.foovendor.com/submit?url=${url}&crash=${stackURL}&args=...&otherinfo=...
  6. An important event occurs during the execution of the Foo instance. The Foo instance invokes NPN_SetValue(NPPVbugReportURL) with the new URL http://crash-report.foovendor.com/submit?url=${url}&crash=${stackURL}&args=...&otherinfo=...&newinfo=...
  7. The OS process hosting Foo dies unexpectedly. The UA suspects the unexpected exit was due to a bug in libfooplugin.so

At this point, the UA will want to submit a crash report. The UA may show the user a crash-reporting interface that looks something like

Sorry, your page has crashed.  [UA] would like to prevent this crash in
the future.  We would appreciate it if you allowed us to submit a crash
report to [UA].

[  ] Please check this box if you would like to submit http://webpage.com
along with crash information.

This crash appears to have been caused by "Foo Plugin" [libfooplugin.so].
If you would additionally like to submit a report to

  http://crash-report.foovendor.com/submit

containing the following information

  url : [ contingent on above checkbox ]
  crash : http://uacrashdatabase.com/id=12345
  args : ...
  otherinfo : ...
  newinfo : ...

[  ] check this box.

[ Click this button if you wish to submit your crash report ]

(Sorry, I'm not a user interface person, this is just a rough sketch.)

This UI gives the user the following options

  • Submit a bug report to UA
    • No : done
    • Yes
      • Include URL information in crash
        • No : allowURL = false
        • Yes : allowURL = true
      • Send a bug report to http://crash-report.foovendor.com
        • No
        • Yes : UA will expand in ${url} only if allowURL is true

In the case above, if the user (i) chooses to send a bug report; (ii) chooses to allow URL information to be sent; and (iii) chooses to additionally send a bug report to Foo's vendor, then the scenario concludes with

  1. Crash report sent to UA
  2. UA displays "Thank you" UI
  3. GET request made to http://crash-report.foovendor.com/submit?url=http://webpage.com&crash=http://uacrashdatabase.com/crash?id=12345&args=...&otherinfo=...&newinfo=...
  4. UA displays HTML reply to GET request in "new tab"

Example usage of bug reporting by plugin vendors

If the UA, plugin, and user opt in to sending a bug report to the plugin vendor on a crash, it will take the form of a GET request to the expanded, plugin-vendor-supplied bugReportURL. As in the example above

GET http://crash-report.foovendor.com/submit?url=http://webpage.com&crash=http://uacrashdatabase.com/crash?id=12345&args=...&otherinfo=...&newinfo=...

Some possible uses

  1. Crash database
  2. Analysis of crash resulting in HTML response to GET that contains instructions for the user on how to fix the crash's underlying cause (e.g., update to latest version)
  3.  ???

Conformance

Roughly speaking, the UA submitting a crash report on behalf of a plugin would be a privilege, not a right.

Minimal conformance for both UAs and plugins is not error'ing or crashing upon NPP_GetValue(NPPVbugReportURL) and NPN_SetValue(NPPVbugReportURL).

A UA may choose to reject a bugReportURL for any reason, i.e. never send it --- not even give the user the option. Possible reasons include

  • HTTP rather than HTTPS bugReportURL
  • Blacklisted plugin
  • User preference
  • Suspicious URL
  • Suspicious GET param

After "accepting" a bugReportURL, a UA may for any reason still not send a crash report. Possible reasons include

  • User chooses not to report crash at all
  • User chooses not to send crash report to plugin vendor
  • User preference

After the UA and user decide to send a crash report, a UA may choose not to expand any formatting string in bugReportURLs for any reason. Possible reasons include

  • Unknown formatting string
  • User chooses not to send information
  • User preference

Objections

  • Concerns about user privacy: have to trust plugin vendors not to send sensitive data in crash reports
    • Reply: UAs already must trust NPAPI plugins to protect user privacy. This API extension exposes no more "private" information than is available to even well-behaved plugins. And UAs are expected to give users the option of sending the report, and if so, what information known to the UA to send.
  • Some UAs already have crash databases, plugin vendors should use those instead
    • Reply: Plugin vendors need to reply to this point, see below. The idea is to offload from UAs some of the crash analysis burden, while additionally allowing plugin vendors to record information relevant to them (complementing what a UA finds relevant).
  • Google Chrome found that "a lot of" (?) OOPP crashes were UA bugs, not plugin bugs
    • Reply: UAs need to approximately quantify "a lot" for themselves. (any Google Chrome data available?) If the UA can't reliably determine when a plugin is indeed at fault, it probably shouldn't send plugin crash reports.
  • Potential for bad user experience because of a non-uniform crash report UI
    • Any responses from people with UI experience?
  • Risky API: even if implemented by plugin vendors and UAs, may not improve plugin quality
    • A definite risk. See below.

Lingering Questions

  1. Can plugin vendors compellingly demonstrate how better crash forensics facilitated by OOPP would improve plugin quality? Some sub-questions
    • What information would vendors include in specially-tailored reports that UAs don't already have in their own crash databases?
    • The bugReportURL can be updated arbitrarily frequently, but only before a crash: not at the time of a crash. What additional (not recorded by UAs) pre-crash data would help plugin vendors in debugging?
  2. The bugReportURL is created per plugin instance. However, there may be multiple plugin instances per web page. In this situation, if UAs can't reliable determine which instance had code executing on its behalf at the time of the crash, would plugin vendors still be able to extract useful data from a bugReportURL from any instance (not necessary the one executing at crash time)?