Backed out changeset 5c7de47bcacb (bug 1927094) for causing Bug 1928689. a=backout
[gecko.git] / toolkit / modules / AsanReporter.sys.mjs
blob50929fbad970919ac90d40c1b0b42e4d749e2a21
1 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
8 const lazy = {};
10 ChromeUtils.defineESModuleGetters(lazy, {
11   Log: "resource://gre/modules/Log.sys.mjs",
12 });
14 // Define our prefs
15 const PREF_CLIENT_ID = "asanreporter.clientid";
16 const PREF_API_URL = "asanreporter.apiurl";
17 const PREF_AUTH_TOKEN = "asanreporter.authtoken";
18 const PREF_LOG_LEVEL = "asanreporter.loglevel";
20 // Reporter product map
21 const REPORTER_PRODUCT = {
22   firefox: "mozilla-central-asan-nightly",
23   thunderbird: "comm-central-asan-daily",
26 const LOGGER_NAME = "asanreporter";
28 let logger;
30 export const AsanReporter = {
31   init() {
32     if (this.initialized) {
33       return;
34     }
35     this.initialized = true;
37     // Setup logging
38     logger = lazy.Log.repository.getLogger(LOGGER_NAME);
39     logger.addAppender(
40       new lazy.Log.ConsoleAppender(new lazy.Log.BasicFormatter())
41     );
42     logger.addAppender(
43       new lazy.Log.DumpAppender(new lazy.Log.BasicFormatter())
44     );
45     logger.level = Services.prefs.getIntPref(
46       PREF_LOG_LEVEL,
47       lazy.Log.Level.Info
48     );
50     logger.info("Starting up...");
52     // Install a handler to observe tab crashes, so we can report those right
53     // after they happen instead of relying on the user to restart the browser.
54     Services.obs.addObserver(this, "ipc:content-shutdown");
56     processDirectory();
57   },
59   observe(aSubject, aTopic) {
60     if (aTopic == "ipc:content-shutdown") {
61       aSubject.QueryInterface(Ci.nsIPropertyBag2);
62       if (!aSubject.get("abnormal")) {
63         return;
64       }
65       processDirectory();
66     }
67   },
70 async function processDirectory() {
71   const asanDumpDir = PathUtils.join(PathUtils.profileDir, "asan");
72   const children = await IOUtils.getChildren(asanDumpDir);
74   const results = children.filter(function (path) {
75     const name = PathUtils.filename(path);
76     return name.startsWith("ff_asan_log.") && !name.includes("submitted");
77   });
79   logger.info(`Processing ${results.length} reports...`);
80   for (const result of results) {
81     try {
82       await submitReport(result);
83       logger.info(`Successfully submitted ${result.path}`);
84     } catch (e) {
85       logger.error(`Failed to submit ${result.path}. Reason: ${e}`);
86     }
87   }
89   logger.info("Done processing reports.");
92 async function submitReport(reportFile) {
93   logger.info("Processing " + reportFile);
94   const data = await IOUtils.read(reportFile);
95   await submitToServer(data);
96   // Mark as submitted only if we successfully submitted it to the server.
97   await IOUtils.move(reportFile, `${reportFile}.submitted`);
100 function submitToServer(data) {
101   return new Promise(function (resolve, reject) {
102     logger.debug("Setting up XHR request");
103     let client = Services.prefs.getStringPref(PREF_CLIENT_ID);
104     let api_url = Services.prefs.getStringPref(PREF_API_URL);
105     let auth_token = Services.prefs.getStringPref(PREF_AUTH_TOKEN, null);
107     let decoder = new TextDecoder();
109     if (!client) {
110       client = "unknown";
111     }
113     let versionArr = [
114       Services.appinfo.version,
115       Services.appinfo.appBuildID,
116       AppConstants.SOURCE_REVISION_URL || "unknown",
117     ];
119     // Concatenate all relevant information as our server only
120     // has one field available for version information.
121     let product_version = versionArr.join("-");
122     let os = AppConstants.platform;
123     let reporter_product = REPORTER_PRODUCT[AppConstants.MOZ_APP_NAME];
125     let reportObj = {
126       rawStdout: "",
127       rawStderr: "",
128       rawCrashData: decoder.decode(data),
129       // Hardcode platform as there is no other reasonable platform for ASan
130       platform: "x86-64",
131       product: reporter_product,
132       product_version,
133       os,
134       client,
135       tool: "asan-nightly-program",
136     };
138     var xhr = new XMLHttpRequest({ mozAnon: !auth_token });
139     xhr.open("POST", api_url, true);
140     xhr.setRequestHeader("Content-Type", "application/json");
142     // For internal testing purposes, an auth_token can be specified
143     if (auth_token) {
144       xhr.setRequestHeader("Authorization", "Token " + auth_token);
145     } else {
146       // Prevent privacy leaks
147       xhr.channel.loadFlags |= Ci.nsIRequest.LOAD_ANONYMOUS;
148     }
150     xhr.onreadystatechange = function () {
151       if (xhr.readyState == 4) {
152         if (xhr.status == "201") {
153           logger.debug("XHR: OK");
154           resolve(xhr);
155         } else {
156           logger.debug(
157             "XHR: Status: " + xhr.status + " Response: " + xhr.responseText
158           );
159           reject(xhr);
160         }
161       }
162     };
164     xhr.send(JSON.stringify(reportObj));
165   });