Bug 1310703 - Make the crashreporter client send a crash ping via the ping sender executable; r=ted

MozReview-Commit-ID: JV6HkUZYkUk
This commit is contained in:
Gabriele Svelto 2017-01-16 18:14:17 +01:00
Родитель 276f7423bb
Коммит d1f13764e1
11 изменённых файлов: 589 добавлений и 54 удалений

Просмотреть файл

@ -22,6 +22,7 @@ Cu.import("resource://gre/modules/Timer.jsm");
Cu.import("resource://gre/modules/TelemetrySend.jsm", this);
Cu.import("resource://gre/modules/TelemetryUtils.jsm", this);
Cu.import("resource://gre/modules/AppConstants.jsm");
Cu.import("resource://gre/modules/ClientID.jsm");
const Utils = TelemetryUtils;
@ -66,7 +67,7 @@ const PREF_PREVIOUS_BUILDID = PREF_BRANCH + "previousBuildID";
const PREF_FHR_UPLOAD_ENABLED = "datareporting.healthreport.uploadEnabled";
const PREF_ASYNC_PLUGIN_INIT = "dom.ipc.plugins.asyncInit.enabled";
const PREF_UNIFIED = PREF_BRANCH + "unified";
const PREF_SERVER = PREF_BRANCH + "server";
const MESSAGE_TELEMETRY_PAYLOAD = "Telemetry:Payload";
const MESSAGE_TELEMETRY_THREAD_HANGS = "Telemetry:ChildThreadHangs";
@ -203,12 +204,25 @@ function getPingType(aPayload) {
/**
* Annotate the current session ID with the crash reporter to map potential
* crash pings with the related main ping.
*
* @param sessionId {String} The telemetry session ID
* @param clientId {String} The telemetry client ID
* @param telemetryServer {String} The URL of the telemetry server
*/
function annotateCrashReport(sessionId) {
function annotateCrashReport(sessionId, clientId, telemetryServer) {
try {
const cr = Cc["@mozilla.org/toolkit/crash-reporter;1"];
if (cr) {
cr.getService(Ci.nsICrashReporter).setTelemetrySessionId(sessionId);
const crs = cr.getService(Ci.nsICrashReporter);
crs.setTelemetrySessionId(sessionId);
// We do not annotate the crash if telemetry is disabled to prevent the
// crashreporter client from sending the crash ping.
if (Utils.isTelemetryEnabled) {
crs.annotateCrashReport("TelemetryClientId", clientId);
crs.annotateCrashReport("TelemetryServerURL", telemetryServer);
}
}
} catch (e) {
// Ignore errors when crash reporting is disabled
@ -1471,7 +1485,10 @@ var Impl = {
// the very same value for |_sessionStartDate|.
this._sessionStartDate = this._subsessionStartDate;
annotateCrashReport(this._sessionId);
// Annotate crash reports using the cached client ID which can be accessed
// synchronously. If it is not available yet, we'll update it later.
annotateCrashReport(this._sessionId, ClientID.getCachedClientID(),
Preferences.get(PREF_SERVER, undefined));
// Initialize some probes that are kept in their own modules
this._thirdPartyCookies = new ThirdPartyCookieProbe();
@ -1498,13 +1515,13 @@ var Impl = {
ppml.addMessageListener(MESSAGE_TELEMETRY_PAYLOAD, this);
ppml.addMessageListener(MESSAGE_TELEMETRY_THREAD_HANGS, this);
ppml.addMessageListener(MESSAGE_TELEMETRY_USS, this);
},
},
/**
* Does the "heavy" Telemetry initialization later on, so we
* don't impact startup performance.
* @return {Promise} Resolved when the initialization completes.
*/
/**
* Does the "heavy" Telemetry initialization later on, so we
* don't impact startup performance.
* @return {Promise} Resolved when the initialization completes.
*/
delayedInit() {
this._log.trace("delayedInit");
@ -1526,6 +1543,10 @@ var Impl = {
Telemetry.asyncFetchTelemetryData(function() {});
// Update the crash annotation with the proper client ID.
annotateCrashReport(this._sessionId, yield ClientID.getClientID(),
Preferences.get(PREF_SERVER, undefined));
if (IS_UNIFIED_TELEMETRY) {
// Check for a previously written aborted session ping.
yield TelemetryController.checkAbortedSessionPing();

Просмотреть файл

@ -2,7 +2,18 @@
"crash" ping
============
This ping is captured after the main Firefox process crashes, whether or not the crash report is submitted to crash-stats.mozilla.org. It includes non-identifying metadata about the crash.
This ping is captured after the main Firefox process crashes or after a content
process crashes, whether or not the crash report is submitted to
crash-stats.mozilla.org. It includes non-identifying metadata about the crash.
This ping is sent either by the ```CrashManager``` or by the crash reporter
client. The ```CrashManager``` is responsible for sending crash pings for the
content process crashes, which are sent right after the crash is detected,
as well as for main process crashes, which are sent after Firefox restarts
successfully. The crash reporter client sends crash pings only for main process
crashes whether or not the user also reports the crash. The crash reporter
client will not send the crash ping if telemetry has been disabled in Firefox
though.
The environment block that is sent with this ping varies: if Firefox was running long enough to record the environment block before the crash, then the environment at the time of the crash will be recorded and ``hasCrashEnvironment`` will be true. If Firefox crashed before the environment was recorded, ``hasCrashEnvironment`` will be false and the recorded environment will be the environment at time of submission.
@ -13,7 +24,6 @@ Structure:
.. code-block:: js
{
version: 1,
type: "crash",
... common ping data
clientId: <UUID>,
@ -21,6 +31,7 @@ Structure:
processType: <type>, // Type of process that crashed, see below for a list of types
payload: {
crashDate: "YYYY-MM-DD",
version: 1,
sessionId: <UUID>, // may be missing for crashes that happen early
// in startup. Added in Firefox 48 with the
// intention of uplifting to Firefox 46

Просмотреть файл

@ -13,9 +13,10 @@
#include <fstream>
#include <sstream>
#include <memory>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <ctime>
#include <cstdlib>
#include <cstring>
#include <string>
using std::string;
using std::istream;
@ -196,9 +197,9 @@ static string GetDumpLocalID()
return localId.substr(0, dot);
}
// This appends the StackTraces entry generated by the minidump analyzer to the
// main crash event so that it can be picked up by Firefox once it restarts
static void AppendStackTracesToEventFile(const string& aStackTraces)
// This appends the aKey/aValue entry to the main crash event so that it can
// be picked up by Firefox once it restarts
static void AppendToEventFile(const string& aKey, const string& aValue)
{
if (gEventsPath.empty()) {
// If there is no path for finding the crash event, skip this step.
@ -210,7 +211,7 @@ static void AppendStackTracesToEventFile(const string& aStackTraces)
ofstream* f = UIOpenWrite(path.c_str(), true);
if (f->is_open()) {
*f << "StackTraces=" << aStackTraces;
*f << aKey << "=" << aValue << std::endl;
f->close();
}
@ -265,8 +266,9 @@ static void OpenLogFile()
static bool ReadConfig()
{
string iniPath;
if (!UIGetIniPath(iniPath))
if (!UIGetIniPath(iniPath)) {
return false;
}
if (!ReadStringsFromFile(iniPath, gStrings, true))
return false;
@ -513,21 +515,17 @@ bool CheckEndOfLifed(string version)
return UIFileExists(reportPath);
}
#ifndef RELEASE_OR_BETA
static string
GetMinidumpAnalyzerPath()
GetProgramPath(const string& exename)
{
string path = gArgv[0];
size_t pos = path.rfind(UI_CRASH_REPORTER_FILENAME BIN_SUFFIX);
path.erase(pos);
path.append(UI_MINIDUMP_ANALYZER_FILENAME BIN_SUFFIX);
path.append(exename + BIN_SUFFIX);
return path;
}
#endif
int main(int argc, char** argv)
{
gArgc = argc;
@ -552,7 +550,9 @@ int main(int argc, char** argv)
#ifndef RELEASE_OR_BETA
// start by running minidump analyzer, this is currently enabled only in
// nightly and aurora
UIRunMinidumpAnalyzer(GetMinidumpAnalyzerPath(), gReporterDumpFile);
string empty;
UIRunProgram(GetProgramPath(UI_MINIDUMP_ANALYZER_FILENAME),
gReporterDumpFile, empty, /* wait */ true);
#endif
// go ahead with the crash reporter
@ -640,10 +640,17 @@ int main(int argc, char** argv)
gEventsPath.clear();
}
// Assemble and send the crash ping
string pingUuid;
if (SendCrashPing(queryParameters, pingUuid)) {
AppendToEventFile("CrashPingUUID", pingUuid);
}
// Update the crash event with stacks if they are present
auto stackTracesItr = queryParameters.find("StackTraces");
if (stackTracesItr != queryParameters.end()) {
AppendStackTracesToEventFile(stackTracesItr->second);
AppendToEventFile(stackTracesItr->first, stackTracesItr->second);
}
if (!UIFileExists(gReporterDumpFile)) {

Просмотреть файл

@ -39,6 +39,11 @@ std::string WideToUTF8(const std::wstring& wide, bool* success = 0);
#define UI_CRASH_REPORTER_FILENAME "crashreporter"
#define UI_MINIDUMP_ANALYZER_FILENAME "minidump-analyzer"
#ifndef XP_MACOSX
#define UI_PING_SENDER_FILENAME "pingsender"
#else
#define UI_PING_SENDER_FILENAME "../../../pingsender"
#endif
typedef std::map<std::string, std::string> StringTable;
@ -79,7 +84,7 @@ typedef std::map<std::string, std::string> StringTable;
#define ST_ERROR_ENDOFLIFE "ErrorEndOfLife"
//=============================================================================
// implemented in crashreporter.cpp
// implemented in crashreporter.cpp and ping.cpp
//=============================================================================
namespace CrashReporter {
@ -112,6 +117,9 @@ namespace CrashReporter {
void DeleteDump();
bool ShouldEnableSending();
// Telemetry ping
bool SendCrashPing(StringTable& strings, std::string& pingUuid);
static const unsigned int kSaveCount = 10;
}
@ -148,8 +156,16 @@ std::ofstream* UIOpenWrite(const std::string& filename,
bool append=false,
bool binary=false);
void UIPruneSavedDumps(const std::string& directory);
void UIRunMinidumpAnalyzer(const std::string& exename,
const std::string& filename);
// Run the program specified by exename, passing it the parameter in arg and
// then sending it the contents of data (if any) via a pipe tied to its stdin.
// If wait is true, wait for the program to terminate execution before
// returning. Returns true if the program was launched correctly, false
// otherwise.
bool UIRunProgram(const std::string& exename,
const std::string& arg,
const std::string& data,
bool wait = false);
#ifdef _MSC_VER
# pragma warning( pop )

Просмотреть файл

@ -70,16 +70,67 @@ void UIPruneSavedDumps(const std::string& directory)
}
}
void UIRunMinidumpAnalyzer(const string& exename, const string& filename)
bool UIRunProgram(const std::string& exename, const std::string& arg,
const std::string& data, bool wait)
{
// Run the minidump analyzer and wait for it to finish
bool usePipes = !data.empty();
int fd[2];
if (usePipes && (pipe(fd) != 0)) {
return false;
}
pid_t pid = fork();
if (pid == -1) {
return; // Nothing to do upon failure
return false;
} else if (pid == 0) {
execl(exename.c_str(), exename.c_str(), filename.c_str(), nullptr);
// Child
if (usePipes) {
if (dup2(fd[0], STDIN_FILENO) == -1) {
exit(EXIT_FAILURE);
}
close(fd[0]);
close(fd[1]);
}
char* argv[] = {
const_cast<char*>(exename.c_str()),
const_cast<char*>(arg.c_str()),
nullptr
};
// Run the program
int rv = execv(exename.c_str(), argv);
if (rv == -1) {
exit(EXIT_FAILURE);
}
} else {
waitpid(pid, nullptr, 0);
// Parent
if (usePipes) {
ssize_t rv;
size_t offset = 0;
size_t size = data.size();
do {
rv = write(fd[1], data.c_str() + offset, size);
if (rv > 0) {
size -= rv;
offset += rv;
}
} while (size && ((rv > 0) || ((rv == -1) && errno == EINTR)));
close(fd[0]);
close(fd[1]);
}
if (wait) {
waitpid(pid, nullptr, 0);
}
}
return true;
}

Просмотреть файл

@ -1545,24 +1545,81 @@ void UIPruneSavedDumps(const std::string& directory)
}
}
void UIRunMinidumpAnalyzer(const string& exename, const string& filename)
bool UIRunProgram(const string& exename, const string& arg,
const string& data, bool wait)
{
wstring cmdLine;
bool usePipes = !data.empty();
HANDLE childStdinPipeRead = nullptr;
HANDLE childStdinPipeWrite = nullptr;
cmdLine += L"\"" + UTF8ToWide(exename) + L"\" ";
cmdLine += L"\"" + UTF8ToWide(filename) + L"\" ";
if (usePipes) {
// Set up the pipes
SECURITY_ATTRIBUTES sa = {};
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = nullptr;
sa.bInheritHandle = true;
// Create a pipe for the child process's stdin and ensure its writable end is
// not inherited by the child process.
if (!CreatePipe(&childStdinPipeRead, &childStdinPipeWrite, &sa, 0)) {
return false;
}
if (!SetHandleInformation(childStdinPipeWrite, HANDLE_FLAG_INHERIT, 0)) {
return false;
}
}
wstring cmdLine = L"\"" + UTF8ToWide(exename) + L"\" " +
L"\"" + UTF8ToWide(arg) + L"\" ";
STARTUPINFO si = {};
si.cb = sizeof(si);
if (usePipes) {
si.dwFlags |= STARTF_USESTDHANDLES;
si.hStdInput = childStdinPipeRead;
si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
}
PROCESS_INFORMATION pi = {};
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOWNORMAL;
if (CreateProcess(nullptr, (LPWSTR)cmdLine.c_str(), nullptr, nullptr, FALSE,
0, nullptr, nullptr, &si, &pi)) {
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
if (!CreateProcess(/* lpApplicationName */ nullptr,
(LPWSTR)cmdLine.c_str(),
/* lpProcessAttributes */ nullptr,
/* lpThreadAttributes */ nullptr,
usePipes,
NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW,
/* lpEnvironment */ nullptr,
/* lpCurrentDirectory */ nullptr,
&si, &pi)) {
return false;
}
if (usePipes) {
size_t offset = 0;
size_t size = data.size();
while (size > 0) {
DWORD written = 0;
bool success = WriteFile(childStdinPipeWrite, data.data() + offset, size,
&written, nullptr);
if (!success) {
break;
} else {
size -= written;
offset += written;
}
}
CloseHandle(childStdinPipeWrite);
}
if (wait) {
WaitForSingleObject(pi.hProcess, INFINITE);
}
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return true;
}

Просмотреть файл

@ -9,6 +9,15 @@ if CONFIG['OS_TARGET'] != 'Android':
UNIFIED_SOURCES += [
'crashreporter.cpp',
'ping.cpp',
]
LOCAL_INCLUDES += [
'/toolkit/crashreporter/jsoncpp/include',
]
USE_LIBS += [
'jsoncpp',
]
if CONFIG['OS_ARCH'] == 'WINNT':
@ -22,6 +31,7 @@ if CONFIG['OS_ARCH'] == 'WINNT':
]
OS_LIBS += [
'comctl32',
'ole32',
'shell32',
'wininet',
'shlwapi',

Просмотреть файл

@ -0,0 +1,323 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "crashreporter.h"
#include <cstring>
#include <string>
#if defined(XP_LINUX)
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#elif defined(XP_MACOSX)
#include <CoreFoundation/CoreFoundation.h>
#elif defined(XP_WIN)
#include <Objbase.h>
#endif
#include "json/json.h"
using std::string;
namespace CrashReporter {
struct UUID {
uint32_t m0;
uint16_t m1;
uint16_t m2;
uint8_t m3[8];
};
// Generates an UUID; the code here is mostly copied from nsUUIDGenerator.cpp
static string
GenerateUUID()
{
UUID id = {};
#if defined(XP_WIN) // Windows
HRESULT hr = CoCreateGuid((GUID*)&id);
if (FAILED(hr)) {
return "";
}
#elif defined(XP_MACOSX) // MacOS X
CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
if (!uuid) {
return "";
}
CFUUIDBytes bytes = CFUUIDGetUUIDBytes(uuid);
memcpy(&id, &bytes, sizeof(UUID));
CFRelease(uuid);
#elif defined(HAVE_ARC4RANDOM_BUF) // Android, BSD, ...
arc4random_buf(id, sizeof(UUID));
#else // Linux
int fd = open("/dev/urandom", O_RDONLY);
if (fd == -1) {
return "";
}
if (read(fd, &id, sizeof(UUID)) != sizeof(UUID)) {
close(fd);
return "";
}
close(fd);
#endif
/* Put in the version */
id.m2 &= 0x0fff;
id.m2 |= 0x4000;
/* Put in the variant */
id.m3[0] &= 0x3f;
id.m3[0] |= 0x80;
const char* kUUIDFormatString =
"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x";
const size_t kUUIDFormatStringLength = 36;
char str[kUUIDFormatStringLength + 1] = { '\0' };
int num = snprintf(str, kUUIDFormatStringLength + 1, kUUIDFormatString,
id.m0, id.m1, id.m2, id.m3[0], id.m3[1], id.m3[2],
id.m3[3], id.m3[4], id.m3[5], id.m3[6], id.m3[7]);
if (num != kUUIDFormatStringLength) {
return "";
}
return str;
}
const char kISO8601Date[] = "%F";
const char kISO8601FullDate[] = "%FT%T.000Z";
// Return the current date as a string in the specified format, the following
// constants are provided:
// - kISO8601Date, the ISO 8601 date format, YYYY-MM-DD
// - kISO8601FullDate, the ISO 8601 full date format, YYYY-MM-DDTHH:MM:SS.000Z
static string
CurrentDate(string format)
{
time_t now;
time(&now);
char buf[64]; // This should be plenty
strftime(buf, sizeof buf, format.c_str(), gmtime(&now));
return buf;
}
const char kTelemetryClientId[] = "TelemetryClientId";
const char kTelemetryUrl[] = "TelemetryServerURL";
const char kTelemetrySessionId[] = "TelemetrySessionId";
const int kTelemetryVersion = 4;
// Create the payload.metadata node of the crash ping using fields extracted
// from the .extra file
static Json::Value
CreateMetadataNode(StringTable& strings)
{
// The following list should be kept in sync with the one in CrashManager.jsm
const char *entries[] = {
"AvailablePageFile",
"AvailablePhysicalMemory",
"AvailableVirtualMemory",
"BlockedDllList",
"BlocklistInitFailed",
"BuildID",
"ContainsMemoryReport",
"CrashTime",
"EventLoopNestingLevel",
"IsGarbageCollecting",
"MozCrashReason",
"OOMAllocationSize",
"ProductID",
"ProductName",
"ReleaseChannel",
"SecondsSinceLastCrash",
"StartupCrash",
"SystemMemoryUsePercentage",
"TelemetrySessionId",
"TextureUsage",
"TotalPageFile",
"TotalPhysicalMemory",
"TotalVirtualMemory",
"UptimeTS",
"User32BeforeBlocklist",
"Version",
};
Json::Value node;
for (auto entry : entries) {
if ((strings.find(entry) != strings.end()) && !strings[entry].empty()) {
node[entry] = strings[entry];
}
}
return node;
}
// Create the payload node of the crash ping
static Json::Value
CreatePayloadNode(StringTable& strings, const string& aSessionId)
{
Json::Value payload;
payload["sessionId"] = aSessionId;
payload["version"] = 1;
payload["crashDate"] = CurrentDate(kISO8601Date);
payload["hasCrashEnvironment"] = true;
payload["crashId"] = GetDumpLocalID();
payload["processType"] = "main"; // This is always a main crash
// Parse the stack traces
Json::Value stackTracesValue;
Json::Reader reader;
if (reader.parse(strings["StackTraces"], stackTracesValue,
/* collectComments */ false)) {
payload["stackTraces"] = stackTracesValue;
}
// Assemble the payload metadata
payload["metadata"] = CreateMetadataNode(strings);
return payload;
}
// Create the application node of the crash ping
static Json::Value
CreateApplicationNode(const string& aVendor, const string& aName,
const string& aVersion, const string& aChannel,
const string& aBuildId, const string& aArchitecture,
const string& aXpcomAbi)
{
Json::Value application;
application["vendor"] = aVendor;
application["name"] = aName;
application["buildId"] = aBuildId;
application["displayVersion"] = aVersion;
application["platformVersion"] = aVersion;
application["version"] = aVersion;
application["channel"] = aChannel;
if (!aArchitecture.empty()) {
application["architecture"] = aArchitecture;
}
if (!aXpcomAbi.empty()) {
application["xpcomAbi"] = aXpcomAbi;
}
return application;
}
// Create the root node of the crash ping
static Json::Value
CreateRootNode(StringTable& strings, const string& aUuid,
const string& aClientId, const string& aSessionId,
const string& aName, const string& aVersion,
const string& aChannel, const string& aBuildId)
{
Json::Value root;
root["type"] = "crash"; // This is a crash ping
root["id"] = aUuid;
root["version"] = kTelemetryVersion;
root["creationDate"] = CurrentDate(kISO8601FullDate);
root["clientId"] = aClientId;
// Parse the telemetry environment
Json::Value environment;
Json::Reader reader;
string architecture;
string xpcomAbi;
if (reader.parse(strings["TelemetryEnvironment"], environment,
/* collectComments */ false)) {
if (environment.isMember("build") && environment["build"].isObject()) {
Json::Value build = environment["build"];
if (build.isMember("architecture") && build["architecture"].isString()) {
architecture = build["architecture"].asString();
}
if (build.isMember("xpcomAbi") && build["xpcomAbi"].isString()) {
xpcomAbi = build["xpcomAbi"].asString();
}
}
root["environment"] = environment;
}
root["payload"] = CreatePayloadNode(strings, aSessionId);
root["application"] = CreateApplicationNode(strings["Vendor"], aName,
aVersion, aChannel, aBuildId,
architecture, xpcomAbi);
return root;
}
// Generates the URL used to submit the crash ping, see TelemetrySend.jsm
string
GenerateSubmissionUrl(const string& aUrl, const string& aId,
const string& aName, const string& aVersion,
const string& aChannel, const string& aBuildId)
{
return aUrl + "/submit/telemetry/" + aId + "/crash/" + aName + "/" +
aVersion + "/" + aChannel + "/" + aBuildId +
"?v=" + std::to_string(kTelemetryVersion);
}
// Assembles the crash ping using the strings extracted from the .extra file
// and sends it using the crash sender. All the telemetry specific data but the
// environment will be stripped from the annotations so that it won't be sent
// together with the crash report.
//
// Note that the crash ping sender is invoked in a fire-and-forget way so this
// won't block waiting for the ping to be delivered.
//
// Returns true if the ping was assembled and handed over to the pingsender
// correctly, false otherwise and populates the aUUID field with the ping UUID.
bool
SendCrashPing(StringTable& strings, string& pingUuid)
{
string clientId = strings[kTelemetryClientId];
string serverUrl = strings[kTelemetryUrl];
string sessionId = strings[kTelemetrySessionId];
// Remove the telemetry-related data from the crash annotations
strings.erase(kTelemetryClientId);
strings.erase(kTelemetryUrl);
strings.erase(kTelemetrySessionId);
string buildId = strings["BuildID"];
string channel = strings["ReleaseChannel"];
string name = strings["ProductName"];
string version = strings["Version"];
string uuid = GenerateUUID();
string url = GenerateSubmissionUrl(serverUrl, uuid, name, version,
channel, buildId);
if (serverUrl.empty() || uuid.empty()) {
return false;
}
Json::Value root = CreateRootNode(strings, uuid, clientId, sessionId,
name, version, channel, buildId);
// Write out the result
Json::FastWriter writer;
string ping = writer.write(root);
// Hand over the ping to the sender
if (UIRunProgram(GetProgramPath(UI_PING_SENDER_FILENAME), url, ping)) {
pingUuid = uuid;
return true;
} else {
return false;
}
}
} // namespace crashreporter

Просмотреть файл

@ -38,6 +38,12 @@ Minidump Analyzer
the dump files generated during a crash. It appends the stack traces to the
.extra file associated with the crash dump.
Ping Sender
The ping sender is a standalone executable that is launched by the crash
reporter client to deliver a crash ping to our telemetry servers. The ping
sender is used to speed up delivery of the crash ping which would otherwise
have to wait for Firefox to be restarted in order to be sent.
How Main-Process Crash Handling Works
=====================================
@ -90,7 +96,13 @@ argument.
The *crash reporter client* performs a number of roles. There's a lot going
on, so you may want to look at ``main()`` in ``crashreporter.cpp``. First,
stack traces are extracted from the dump via the *minidump analyzer* tool.
The resulting traces are appended to the .extra file of the crash. Then, the
The resulting traces are appended to the .extra file of the crash. Once this
is done a crash ping is assembled holding the same information as the one
generated by the ```CrashManager``` and it's sent to the telemetry servers via
the *ping sender* program. The UUID of the ping is then stored in the extra
file; the ```CrashManager``` will later pick it up and generate a new ping
with the same UUID so that the telemetry server can deduplicate both pings.
Then, the
*crash reporter client* verifies that the dump data is sane. If it isn't
(e.g. required metadata is missing), the dump data is ignored. If dump data
looks sane, the dump data

Просмотреть файл

@ -1097,9 +1097,6 @@ 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());
}
@ -1114,6 +1111,12 @@ bool MinidumpCallback(
#endif
apiData.WriteBuffer(crashReporterAPIData->get(), crashReporterAPIData->Length());
}
if (currentSessionId) {
WriteAnnotation(apiData, "TelemetrySessionId", crashTimeString);
WriteAnnotation(eventFile, "TelemetrySessionId", currentSessionId);
}
WriteAnnotation(apiData, "CrashTime", crashTimeString);
WriteAnnotation(eventFile, "CrashTime", crashTimeString);

Просмотреть файл

@ -45,6 +45,30 @@ function run_test() {
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));
do_check_true("TelemetrySessionId" in extra);
Assert.ok(
"TelemetryServerURL" in extra,
"The TelemetryServerURL field is omitted when telemetry is off"
);
});
do_crash(function() {
// Enable telemetry
let prefs = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
// Turn on telemetry and specify a telemetry server
prefs.setCharPref("toolkit.telemetry.server", "http://a.telemetry.server");
prefs.setBoolPref("toolkit.telemetry.enabled", true);
// TelemetrySession setup will trigger the session annotation
let scope = {};
Components.utils.import("resource://gre/modules/TelemetryController.jsm", scope);
scope.TelemetryController.testSetup();
}, function(mdump, extra) {
Assert.ok("TelemetryServerURL" in extra,
"The TelemetryServerURL field is present in the extra file");
Assert.equal(extra.TelemetryServerURL, "http://a.telemetry.server",
"The TelemetryServerURL field is properly set");
});
}