Bug 1187270 - Add Telemetry session ID to crash annotations, r=gfritzsche

MozReview-Commit-ID: GwVI0dfsT4H

--HG--
extra : rebase_source : 0ae9efbce919efcd0f20314bd89848da017b7e57
This commit is contained in:
Benjamin Smedberg 2016-03-14 09:31:19 -04:00
parent aa5fda5bce
commit 702c087507
10 changed files with 84 additions and 2 deletions

View File

@ -531,6 +531,7 @@ this.CrashManager.prototype = Object.freeze({
// If we have a saved environment, use it. Otherwise report
// the current environment.
let crashEnvironment = null;
let sessionId = null;
let reportMeta = Cu.cloneInto(metadata, myScope);
if ('TelemetryEnvironment' in reportMeta) {
try {
@ -540,10 +541,15 @@ this.CrashManager.prototype = Object.freeze({
}
delete reportMeta.TelemetryEnvironment;
}
if ('TelemetrySessionId' in reportMeta) {
sessionId = reportMeta.TelemetrySessionId;
delete reportMeta.TelemetrySessionId;
}
TelemetryController.submitExternalPing("crash",
{
version: 1,
crashDate: date.toISOString().slice(0, 10), // YYYY-MM-DD
sessionId: sessionId,
metadata: reportMeta,
hasCrashEnvironment: (crashEnvironment !== null),
},

View File

@ -213,13 +213,18 @@ add_task(function* test_main_crash_event_file() {
let ac = new TelemetryArchiveTesting.Checker();
yield ac.promiseInit();
let theEnvironment = TelemetryEnvironment.currentEnvironment;
let sessionId = "be66af2f-2ee5-4330-ae95-44462dfbdf0c";
// To test proper escaping, add data to the environment with an embedded
// double-quote
theEnvironment.testValue = "MyValue\"";
let m = yield getManager();
yield m.createEventsFile("1", "crash.main.2", DUMMY_DATE, "id1\nk1=v1\nk2=v2\nTelemetryEnvironment=" + JSON.stringify(theEnvironment));
const fileContent = "id1\nk1=v1\nk2=v2\n" +
"TelemetryEnvironment=" + JSON.stringify(theEnvironment) + "\n" +
"TelemetrySessionId=" + sessionId;
yield m.createEventsFile("1", "crash.main.2", DUMMY_DATE, fileContent);
let count = yield m.aggregateEventsFiles();
Assert.equal(count, 1);
@ -230,12 +235,14 @@ add_task(function* test_main_crash_event_file() {
Assert.equal(crashes[0].metadata.k1, "v1");
Assert.equal(crashes[0].metadata.k2, "v2");
Assert.ok(crashes[0].metadata.TelemetryEnvironment);
Assert.equal(Object.getOwnPropertyNames(crashes[0].metadata).length, 3);
Assert.equal(Object.getOwnPropertyNames(crashes[0].metadata).length, 4);
Assert.equal(crashes[0].metadata.TelemetrySessionId, sessionId);
Assert.deepEqual(crashes[0].crashDate, DUMMY_DATE);
let found = yield ac.promiseFindPing("crash", [
[["payload", "hasCrashEnvironment"], true],
[["payload", "metadata", "k1"], "v1"],
[["payload", "sessionId"], sessionId],
]);
Assert.ok(found, "Telemetry ping submitted for found crash");
Assert.deepEqual(found.environment, theEnvironment, "The saved environment should be present");

View File

@ -218,6 +218,20 @@ function toLocalTimeISOString(date) {
+ ":" + padNumber(Math.abs(tzOffset % 60), 2);
}
/**
* Annotate the current session ID with the crash reporter to map potential
* crash pings with the related main ping.
*/
function annotateCrashReport(sessionId) {
try {
const cr = Cc["@mozilla.org/toolkit/crash-reporter;1"]
.getService(Ci.nsICrashReporter);
cr.setTelemetrySessionId(sessionId);
} catch (e) {
// Ignore errors when crash reporting is disabled
}
}
/**
* Read current process I/O counters.
*/
@ -1376,6 +1390,8 @@ var Impl = {
// the very same value for |_sessionStartDate|.
this._sessionStartDate = this._subsessionStartDate;
annotateCrashReport(this._sessionId);
// Initialize some probes that are kept in their own modules
this._thirdPartyCookies = new ThirdPartyCookieProbe();
this._thirdPartyCookies.init();

View File

@ -18,6 +18,9 @@ Structure::
environment: { ... },
payload: {
crashDate: "YYYY-MM-DD",
sessionId: <UUID>, // may be missing for crashes that happen early
// in startup. Added in Firefox 48 with the
// intention of uplifting to Firefox 46
metadata: {...}, // Annotations saved while Firefox was running. See nsExceptionHandler.cpp for more information
hasCrashEnvironment: bool
}

View File

@ -212,6 +212,9 @@ static XP_CHAR* memoryReportPath;
static XP_CHAR* eventsDirectory;
static char* eventsEnv = nullptr;
// The current telemetry session ID to write to the event file
static char* currentSessionId = nullptr;
// If this is false, we don't launch the crash reporter
static bool doReport = true;
@ -905,6 +908,9 @@ bool MinidumpCallback(
WriteLiteral(eventFile, "\n");
WriteString(eventFile, id_ascii);
WriteLiteral(eventFile, "\n");
if (currentSessionId) {
WriteAnnotation(eventFile, "TelemetrySessionId", currentSessionId);
}
if (crashEventAPIData) {
eventFile.WriteBuffer(crashEventAPIData->get(), crashEventAPIData->Length());
}
@ -1939,6 +1945,11 @@ nsresult UnsetExceptionHandler()
eventsDirectory = nullptr;
}
if (currentSessionId) {
free(currentSessionId);
currentSessionId = nullptr;
}
if (memoryReportPath) {
free(memoryReportPath);
memoryReportPath = nullptr;
@ -2648,6 +2659,19 @@ SetMemoryReportFile(nsIFile* aFile)
#endif
}
void
SetTelemetrySessionId(const nsACString& id)
{
if (!gExceptionHandler) {
return;
}
if (currentSessionId) {
free(currentSessionId);
}
currentSessionId = ToNewCString(id);
}
static void
FindPendingDir()
{

View File

@ -51,6 +51,7 @@ void SetUserAppDataDirectory(nsIFile* aDir);
void SetProfileDirectory(nsIFile* aDir);
void UpdateCrashEventsDir();
void SetMemoryReportFile(nsIFile* aFile);
void SetTelemetrySessionId(const nsACString& id);
/**
* Get the path where crash event files should be written.

View File

@ -42,10 +42,15 @@ function run_test()
crashReporter.annotateCrashReport("\u2665", "\u{1F4A9}");
crashReporter.appendAppNotesToCrashReport("Junk");
crashReporter.appendAppNotesToCrashReport("MoreJunk");
// TelemetrySession setup will trigger the session annotation
let scope = {};
Components.utils.import("resource://gre/modules/TelemetrySession.jsm", scope);
scope.TelemetrySession.setup();
},
function(mdump, extra) {
do_check_eq(extra.TestKey, "TestValue");
do_check_eq(extra["\u2665"], "\u{1F4A9}");
do_check_eq(extra.Notes, "JunkMoreJunk");
do_check_true(!("TelemetrySessionId" in extra));
});
}

View File

@ -26,6 +26,10 @@ add_task(function* test_main_process_crash() {
let deferred = Promise.defer();
do_crash(
function() {
// TelemetrySession setup will trigger the session annotation
let scope = {};
Components.utils.import("resource://gre/modules/TelemetrySession.jsm", scope);
scope.TelemetrySession.setup();
crashType = CrashTestUtils.CRASH_RUNTIMEABORT;
crashReporter.annotateCrashReport("ShutdownProgress", "event-test");
},
@ -44,4 +48,6 @@ add_task(function* test_main_process_crash() {
Assert.ok(crash.isOfType(cm.PROCESS_TYPE_MAIN, cm.CRASH_TYPE_CRASH));
Assert.equal(crash.id + ".dmp", basename, "ID recorded properly");
Assert.equal(crash.metadata.ShutdownProgress, "event-test");
Assert.ok("TelemetrySessionId" in crash.metadata);
Assert.ok(/^[0-9a-f]{8}\-([0-9a-f]{4}\-){3}[0-9a-f]{12}$/.test(crash.metadata.TelemetrySessionId));
});

View File

@ -1362,6 +1362,13 @@ nsXULAppInfo::SaveMemoryReport()
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::SetTelemetrySessionId(const nsACString& id)
{
CrashReporter::SetTelemetrySessionId(id);
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::Callback(nsISupports* aData)
{

View File

@ -125,4 +125,11 @@ interface nsICrashReporter : nsISupports
* @throws NS_ERROR_NOT_INITIALIZED if crash reporting is disabled.
*/
void saveMemoryReport();
/**
* Set the telemetry session ID which is recorded in crash metadata. This is
* saved in the crash manager and telemetry but is not submitted as a
* crash-stats annotation.
*/
void setTelemetrySessionId(in AUTF8String id);
};