зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1345153 - When the pingsender fails to send a ping, persist it to disk so that it can be sent later via regular telemetry; r=chutten,ted
Currently we hand over a crash ping to the pingsender via a pipe; if the pingsender fails to send the ping we rely on the CrashManager assembling and sending one instead. Since the crashmanager is not aware of whether the ping was sent or not this causes duplication on the server side. To solve this problem we save the ping to disk instead, read it from the pingsender and delete the file only if the ping was sent. In this scenario the CrashManager will know that a ping was already sent and will not send a new one. This patch removes all the code used to deal with pipes between the telemetry, crashreporter and pingsender code and also tries to cut down the amount of platform-specific code we have in this machinery. MozReview-Commit-ID: ASm2jnDagCK --HG-- extra : rebase_source : 9091d7897d17dad68e05e945e777e42998d020d0
This commit is contained in:
Родитель
bdbb254b53
Коммит
4ee88feef8
|
@ -655,8 +655,6 @@ this.CrashManager.prototype = Object.freeze({
|
|||
let stackTraces = parseAndRemoveField(reportMeta, "StackTraces");
|
||||
let minidumpSha256Hash = getAndRemoveField(reportMeta,
|
||||
"MinidumpSha256Hash");
|
||||
// If CrashPingUUID is not present then Telemetry will generate a new UUID
|
||||
let pingId = getAndRemoveField(reportMeta, "CrashPingUUID");
|
||||
|
||||
// Filter the remaining annotations to remove privacy-sensitive ones
|
||||
reportMeta = this._filterAnnotations(reportMeta);
|
||||
|
@ -678,7 +676,6 @@ this.CrashManager.prototype = Object.freeze({
|
|||
addClientId: true,
|
||||
addEnvironment: true,
|
||||
overrideEnvironment: crashEnvironment,
|
||||
overridePingId: pingId
|
||||
}
|
||||
);
|
||||
},
|
||||
|
@ -704,7 +701,14 @@ this.CrashManager.prototype = Object.freeze({
|
|||
store.addCrash(this.PROCESS_TYPE_MAIN, this.CRASH_TYPE_CRASH,
|
||||
crashID, date, metadata);
|
||||
|
||||
this._sendCrashPing(crashID, this.PROCESS_TYPE_MAIN, date, metadata);
|
||||
if (!("CrashPingUUID" in metadata)) {
|
||||
// If CrashPingUUID is not present then a ping was not generated
|
||||
// by the crashreporter for this crash so we need to send one from
|
||||
// here.
|
||||
this._sendCrashPing(crashID, this.PROCESS_TYPE_MAIN, date,
|
||||
metadata);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "crash.submission.1":
|
||||
|
|
|
@ -306,45 +306,6 @@ add_task(function* test_main_crash_event_file_noenv() {
|
|||
Assert.equal(count, 0);
|
||||
});
|
||||
|
||||
add_task(function* test_main_crash_event_file_override_ping_uuid() {
|
||||
let ac = new TelemetryArchiveTesting.Checker();
|
||||
yield ac.promiseInit();
|
||||
|
||||
let m = yield getManager();
|
||||
const fileContent = crashId + "\n" +
|
||||
"ProductName=" + productName + "\n" +
|
||||
"ProductID=" + productId + "\n" +
|
||||
"CrashPingUUID=" + crashPingUuid + "\n";
|
||||
|
||||
yield m.createEventsFile(crashId, "crash.main.2", DUMMY_DATE, fileContent);
|
||||
let count = yield m.aggregateEventsFiles();
|
||||
Assert.equal(count, 1);
|
||||
|
||||
let crashes = yield m.getCrashes();
|
||||
Assert.equal(crashes.length, 1);
|
||||
Assert.equal(crashes[0].id, crashId);
|
||||
Assert.equal(crashes[0].type, "main-crash");
|
||||
Assert.deepEqual(crashes[0].metadata, {
|
||||
CrashPingUUID: crashPingUuid,
|
||||
ProductName: productName,
|
||||
ProductID: productId
|
||||
});
|
||||
Assert.deepEqual(crashes[0].crashDate, DUMMY_DATE);
|
||||
|
||||
let found = yield ac.promiseFindPing("crash", [
|
||||
[["id"], crashPingUuid],
|
||||
[["payload", "hasCrashEnvironment"], false],
|
||||
[["payload", "metadata", "ProductName"], productName],
|
||||
[["payload", "metadata", "ProductID"], productId],
|
||||
]);
|
||||
Assert.ok(found, "Telemetry ping submitted for found crash");
|
||||
Assert.equal(found.payload.metadata.CrashPingUUID, undefined,
|
||||
"Crash ping UUID should be filtered out");
|
||||
|
||||
count = yield m.aggregateEventsFiles();
|
||||
Assert.equal(count, 0);
|
||||
});
|
||||
|
||||
add_task(function* test_crash_submission_event_file() {
|
||||
let m = yield getManager();
|
||||
yield m.createEventsFile("1", "crash.main.2", DUMMY_DATE, "crash1");
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/Likely.h"
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
#include "mozilla/Scoped.h"
|
||||
#include "mozilla/Unused.h"
|
||||
|
||||
#include "base/pickle.h"
|
||||
|
@ -80,6 +79,7 @@
|
|||
#include "mozilla/HangMonitor.h"
|
||||
#include "nsNativeCharsetUtils.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsDirectoryServiceDefs.h"
|
||||
|
||||
#if defined(MOZ_GECKO_PROFILER)
|
||||
#include "shared-libraries.h"
|
||||
|
@ -88,13 +88,6 @@
|
|||
#include "nsPrintfCString.h"
|
||||
#endif // MOZ_GECKO_PROFILER
|
||||
|
||||
namespace mozilla {
|
||||
// Scoped auto-close for PRFileDesc file descriptors
|
||||
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRFileDesc,
|
||||
PRFileDesc,
|
||||
PR_Close);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace mozilla;
|
||||
|
@ -2855,27 +2848,34 @@ TelemetryImpl::FlushBatchedChildTelemetry()
|
|||
static nsresult
|
||||
LocatePingSender(nsAString& aPath)
|
||||
{
|
||||
nsCOMPtr<nsIFile> xreAppDistDir;
|
||||
nsresult rv = NS_GetSpecialDirectory(XRE_APP_DISTRIBUTION_DIR,
|
||||
getter_AddRefs(xreAppDistDir));
|
||||
nsCOMPtr<nsIFile> xreGreBinDir;
|
||||
nsresult rv = NS_GetSpecialDirectory(NS_GRE_BIN_DIR,
|
||||
getter_AddRefs(xreGreBinDir));
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
xreAppDistDir->AppendNative(NS_LITERAL_CSTRING("pingsender" BIN_SUFFIX));
|
||||
xreAppDistDir->GetPath(aPath);
|
||||
// Make sure that the executable exists. Otherwise, quit.
|
||||
bool exists = false;
|
||||
if (NS_FAILED(xreGreBinDir->Exists(&exists)) || !exists) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
xreGreBinDir->AppendNative(NS_LITERAL_CSTRING("pingsender" BIN_SUFFIX));
|
||||
xreGreBinDir->GetPath(aPath);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#endif // MOZ_WIDGET_ANDROID
|
||||
|
||||
NS_IMETHODIMP
|
||||
TelemetryImpl::RunPingSender(const nsACString& aUrl, const nsACString& aPing)
|
||||
TelemetryImpl::RunPingSender(const nsACString& aUrl,
|
||||
const nsACString& aPingFilePath)
|
||||
{
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
Unused << aUrl;
|
||||
Unused << aPing;
|
||||
Unused << aPingFilePath;
|
||||
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
#else // Windows, Mac, Linux, etc...
|
||||
|
@ -2887,53 +2887,30 @@ TelemetryImpl::RunPingSender(const nsACString& aUrl, const nsACString& aPing)
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Create a pipe to send the ping contents to the ping sender
|
||||
ScopedPRFileDesc pipeRead;
|
||||
ScopedPRFileDesc pipeWrite;
|
||||
|
||||
if (PR_CreatePipe(&pipeRead.rwget(), &pipeWrite.rwget()) != PR_SUCCESS) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if ((PR_SetFDInheritable(pipeRead, PR_TRUE) != PR_SUCCESS) ||
|
||||
(PR_SetFDInheritable(pipeWrite, PR_FALSE) != PR_SUCCESS)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
PRProcessAttr* attr = PR_NewProcessAttr();
|
||||
if (!attr) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Connect the pingsender standard input to the pipe and launch it
|
||||
PR_ProcessAttrSetStdioRedirect(attr, PR_StandardInput, pipeRead);
|
||||
// We pretend we're redirecting stdout to force NSPR not to show a
|
||||
// command prompt when launching the program.
|
||||
PR_ProcessAttrSetStdioRedirect(attr, PR_StandardOutput, PR_STDOUT);
|
||||
|
||||
UniquePtr<char[]> arg0(ToNewCString(path));
|
||||
UniquePtr<char[]> arg1(ToNewCString(aUrl));
|
||||
UniquePtr<char[]> asciiPath(ToNewCString(aPingFilePath));
|
||||
UniquePtr<char[]> pingSenderPath(ToNewCString(path));
|
||||
UniquePtr<char[]> serverURL(ToNewCString(aUrl));
|
||||
|
||||
// The next lines are needed as |args| is not const.
|
||||
char* args[] = {
|
||||
arg0.get(),
|
||||
arg1.get(),
|
||||
pingSenderPath.get(),
|
||||
serverURL.get(),
|
||||
asciiPath.get(),
|
||||
nullptr,
|
||||
};
|
||||
|
||||
Unused << NS_WARN_IF(PR_CreateProcessDetached(args[0], args, nullptr, attr));
|
||||
PR_DestroyProcessAttr(attr);
|
||||
|
||||
// Send the ping contents to the ping sender
|
||||
size_t length = aPing.Length();
|
||||
const char* s = aPing.BeginReading();
|
||||
|
||||
while (length > 0) {
|
||||
int result = PR_Write(pipeWrite, s, length);
|
||||
|
||||
if (result <= 0) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
s += result;
|
||||
length -= result;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -197,8 +197,6 @@ this.TelemetryController = Object.freeze({
|
|||
* @param {Boolean} [aOptions.addEnvironment=false] true if the ping should contain the
|
||||
* environment data.
|
||||
* @param {Object} [aOptions.overrideEnvironment=null] set to override the environment data.
|
||||
* @param {String} [aOptions.overridePingId=null] set to override the
|
||||
* generated ping id.
|
||||
* @returns {Promise} Test-only - a promise that resolves with the ping id once the ping is stored or sent.
|
||||
*/
|
||||
submitExternalPing(aType, aPayload, aOptions = {}) {
|
||||
|
@ -231,8 +229,6 @@ this.TelemetryController = Object.freeze({
|
|||
* @param {Boolean} [aOptions.overwrite=false] true overwrites a ping with the same name,
|
||||
* if found.
|
||||
* @param {Object} [aOptions.overrideEnvironment=null] set to override the environment data.
|
||||
* @param {String} [aOptions.overridePingId=null] set to override the
|
||||
* generated ping id.
|
||||
*
|
||||
* @returns {Promise} A promise that resolves with the ping id when the ping is saved to
|
||||
* disk.
|
||||
|
@ -290,8 +286,6 @@ this.TelemetryController = Object.freeze({
|
|||
* @param {Boolean} [aOptions.overwrite=false] true overwrites a ping with the same name,
|
||||
* if found.
|
||||
* @param {Object} [aOptions.overrideEnvironment=null] set to override the environment data.
|
||||
* @param {String} [aOptions.overridePingId=null] set to override the
|
||||
* generated ping id.
|
||||
*
|
||||
* @returns {Promise} A promise that resolves with the ping id when the ping is saved to
|
||||
* disk.
|
||||
|
@ -401,8 +395,6 @@ var Impl = {
|
|||
* @param {Boolean} aOptions.addEnvironment true if the ping should contain the
|
||||
* environment data.
|
||||
* @param {Object} [aOptions.overrideEnvironment=null] set to override the environment data.
|
||||
* @param {String} [aOptions.overridePingId=null] set to override the
|
||||
* generated ping id.
|
||||
*
|
||||
* @returns {Object} An object that contains the assembled ping data.
|
||||
*/
|
||||
|
@ -417,7 +409,7 @@ var Impl = {
|
|||
// Fill the common ping fields.
|
||||
let pingData = {
|
||||
type: aType,
|
||||
id: aOptions.overridePingId || Policy.generatePingId(),
|
||||
id: Policy.generatePingId(),
|
||||
creationDate: (Policy.now()).toISOString(),
|
||||
version: PING_FORMAT_VERSION,
|
||||
application: this._getApplicationSection(),
|
||||
|
@ -459,8 +451,6 @@ var Impl = {
|
|||
* @param {Boolean} [aOptions.addEnvironment=false] true if the ping should contain the
|
||||
* environment data.
|
||||
* @param {Object} [aOptions.overrideEnvironment=null] set to override the environment data.
|
||||
* @param {String} [aOptions.overridePingId=null] set to override the
|
||||
* generated ping id.
|
||||
* @returns {Promise} Test-only - a promise that is resolved with the ping id once the ping is stored or sent.
|
||||
*/
|
||||
_submitPingLogic: Task.async(function* (aType, aPayload, aOptions) {
|
||||
|
@ -499,8 +489,6 @@ var Impl = {
|
|||
* @param {Boolean} [aOptions.addEnvironment=false] true if the ping should contain the
|
||||
* environment data.
|
||||
* @param {Object} [aOptions.overrideEnvironment=null] set to override the environment data.
|
||||
* @param {String} [aOptions.overridePingId=null] set to override the
|
||||
* generated ping id.
|
||||
* @returns {Promise} Test-only - a promise that is resolved with the ping id once the ping is stored or sent.
|
||||
*/
|
||||
submitExternalPing: function send(aType, aPayload, aOptions) {
|
||||
|
@ -546,8 +534,6 @@ var Impl = {
|
|||
* environment data.
|
||||
* @param {Boolean} aOptions.overwrite true overwrites a ping with the same name, if found.
|
||||
* @param {Object} [aOptions.overrideEnvironment=null] set to override the environment data.
|
||||
* @param {String} [aOptions.overridePingId=null] set to override the
|
||||
* generated ping id.
|
||||
*
|
||||
* @returns {Promise} A promise that resolves with the ping id when the ping is saved to
|
||||
* disk.
|
||||
|
@ -584,8 +570,6 @@ var Impl = {
|
|||
* environment data.
|
||||
* @param {Boolean} aOptions.overwrite true overwrites a ping with the same name, if found.
|
||||
* @param {Object} [aOptions.overrideEnvironment=null] set to override the environment data.
|
||||
* @param {String} [aOptions.overridePingId=null] set to override the
|
||||
* generated ping id.
|
||||
*
|
||||
* @returns {Promise} A promise that resolves with the ping id when the ping is saved to
|
||||
* disk.
|
||||
|
|
|
@ -2,11 +2,13 @@ Ping Sender
|
|||
===========
|
||||
|
||||
The ping sender is a minimalistic program whose sole purpose is to deliver a
|
||||
telemetry ping. It accepts a single parameter which is the URL the ping will
|
||||
be sent to (as an HTTP POST command) and once started it will wait to read the
|
||||
ping contents on its stdin stream. Once the ping has been read from stdin the
|
||||
ping sender will try to post it once, exiting with a non-zero value if it
|
||||
fails.
|
||||
telemetry ping. It accepts the following parameters:
|
||||
|
||||
- the URL the ping will be sent to (as an HTTP POST command);
|
||||
- the path to an uncompressed file holding the ping contents.
|
||||
|
||||
Once the ping has been read from disk the ping sender will try to post it once, exiting
|
||||
with a non-zero value if it fails. If sending the ping succeeds then the ping file is removed.
|
||||
|
||||
The content of the HTTP request is *gzip* encoded. The request comes with a few
|
||||
additional headers:
|
||||
|
@ -18,15 +20,17 @@ additional headers:
|
|||
sent using the ping sender and the ones sent using the normal flow, at the end of the
|
||||
ingestion pipeline, for validation purposes.
|
||||
|
||||
The ping sender relies on libcurl for Linux and Mac build and on WinInet for
|
||||
Windows ones for its HTTP functionality. It currently ignores Firefox or the
|
||||
system proxy configuration.
|
||||
.. note::
|
||||
|
||||
The ping sender relies on libcurl for Linux and Mac build and on WinInet for
|
||||
Windows ones for its HTTP functionality. It currently ignores Firefox or the
|
||||
system proxy configuration.
|
||||
|
||||
In non-debug mode the ping sender doesn't print anything, not even on error,
|
||||
this is done deliberately to prevent startling the user on architectures such
|
||||
as Windows that would open a seperate console window just to display the
|
||||
program output. If you need runtime information to be printed out compile the
|
||||
ping sender with debuggging enabled.
|
||||
ping sender with debugging enabled.
|
||||
|
||||
The pingsender is not supported on Firefox for Android at the moment
|
||||
(see `bug 1335917 <https://bugzilla.mozilla.org/show_bug.cgi?id=1335917>`_)
|
||||
|
|
|
@ -549,11 +549,11 @@ interface nsITelemetry : nsISupports
|
|||
* as soon as the contents of the ping have been handed over to the ping
|
||||
* sender.
|
||||
*
|
||||
* @param aUrl The telemetry server URL
|
||||
* @param aPing A string holding the ping contents
|
||||
* @param {String} aUrl The telemetry server URL
|
||||
* @param {String} aPingFilePath The path to the file holding the ping
|
||||
* contents, if if sent successfully the pingsender will delete it.
|
||||
*
|
||||
* @throws NS_ERROR_FAILURE if we couldn't run the pingsender or if we
|
||||
* couldn't hand it the ping data
|
||||
* @throws NS_ERROR_FAILURE if we couldn't find or run the pingsender executable.
|
||||
*/
|
||||
void runPingSender(in ACString aUrl, in ACString aPing);
|
||||
void runPingSender(in ACString aUrl, in ACString aPingFilePath);
|
||||
};
|
||||
|
|
|
@ -5,13 +5,15 @@
|
|||
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "pingsender.h"
|
||||
|
||||
using std::ifstream;
|
||||
using std::ios;
|
||||
using std::string;
|
||||
|
||||
namespace PingSender {
|
||||
|
@ -34,26 +36,34 @@ GenerateDateHeader()
|
|||
}
|
||||
|
||||
/**
|
||||
* Read the ping contents from stdin as a string
|
||||
* Read the ping contents from the specified file
|
||||
*/
|
||||
static std::string
|
||||
ReadPingFromStdin()
|
||||
ReadPing(const string& aPingPath)
|
||||
{
|
||||
const size_t kBufferSize = 32768;
|
||||
char buff[kBufferSize];
|
||||
string ping;
|
||||
size_t readBytes = 0;
|
||||
ifstream file;
|
||||
|
||||
do {
|
||||
readBytes = fread(buff, 1, kBufferSize, stdin);
|
||||
ping.append(buff, readBytes);
|
||||
} while (!feof(stdin) && !ferror(stdin));
|
||||
file.open(aPingPath.c_str(), ios::in | ios::binary);
|
||||
|
||||
if (ferror(stdin)) {
|
||||
PINGSENDER_LOG("ERROR: Could not read from stdin\n");
|
||||
if (!file.is_open()) {
|
||||
PINGSENDER_LOG("ERROR: Could not open ping file\n");
|
||||
return "";
|
||||
}
|
||||
|
||||
do {
|
||||
char buff[4096];
|
||||
|
||||
file.read(buff, sizeof(buff));
|
||||
|
||||
if (file.bad()) {
|
||||
PINGSENDER_LOG("ERROR: Could not read ping contents\n");
|
||||
return "";
|
||||
}
|
||||
|
||||
ping.append(buff, file.gcount());
|
||||
} while (!file.eof());
|
||||
|
||||
return ping;
|
||||
}
|
||||
|
||||
|
@ -136,22 +146,20 @@ using namespace PingSender;
|
|||
int main(int argc, char* argv[])
|
||||
{
|
||||
string url;
|
||||
string pingPath;
|
||||
|
||||
if (argc == 2) {
|
||||
if (argc == 3) {
|
||||
url = argv[1];
|
||||
pingPath = argv[2];
|
||||
} else {
|
||||
PINGSENDER_LOG("Usage: pingsender URL\n"
|
||||
"Send the payload passed via stdin to the specified URL "
|
||||
"using an HTTP POST message\n");
|
||||
PINGSENDER_LOG("Usage: pingsender URL PATH\n"
|
||||
"Send the payload stored in PATH to the specified URL using "
|
||||
"an HTTP POST message\n"
|
||||
"then delete the file after a successful send.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (url.empty()) {
|
||||
PINGSENDER_LOG("ERROR: No URL was provided\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
string ping(ReadPingFromStdin());
|
||||
string ping(ReadPing(pingPath));
|
||||
|
||||
if (ping.empty()) {
|
||||
PINGSENDER_LOG("ERROR: Ping payload is empty\n");
|
||||
|
@ -173,5 +181,11 @@ int main(int argc, char* argv[])
|
|||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// If the ping was successfully sent, delete the file.
|
||||
if (!pingPath.empty() && std::remove(pingPath.c_str())) {
|
||||
// We failed to remove the pending ping file.
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
|
|
@ -6,29 +6,50 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource://gre/modules/TelemetryUtils.jsm", this);
|
||||
Cu.import("resource://gre/modules/Services.jsm", this);
|
||||
Cu.import("resource://gre/modules/osfile.jsm", this);
|
||||
Cu.import("resource://gre/modules/Preferences.jsm", this);
|
||||
Cu.import("resource://gre/modules/PromiseUtils.jsm", this);
|
||||
Cu.import("resource://gre/modules/Services.jsm", this);
|
||||
Cu.import("resource://gre/modules/TelemetryStorage.jsm", this);
|
||||
Cu.import("resource://gre/modules/TelemetryUtils.jsm", this);
|
||||
Cu.import("resource://gre/modules/Timer.jsm", this);
|
||||
|
||||
const PREF_FHR_UPLOAD_ENABLED = "datareporting.healthreport.uploadEnabled";
|
||||
const PREF_TELEMETRY_SERVER = "toolkit.telemetry.server";
|
||||
|
||||
/**
|
||||
* Wait for a ping file to be deleted from the pending pings directory.
|
||||
*/
|
||||
function waitForPingDeletion(pingId) {
|
||||
const path = OS.Path.join(TelemetryStorage.pingDirectoryPath, pingId);
|
||||
|
||||
let checkFn = (resolve, reject) => setTimeout(() => {
|
||||
OS.File.exists(path).then(exists => {
|
||||
if (!exists) {
|
||||
Assert.ok(true, pingId + " was deleted");
|
||||
resolve();
|
||||
} else {
|
||||
checkFn(resolve, reject);
|
||||
}
|
||||
}, reject);
|
||||
}, 250);
|
||||
|
||||
return new Promise((resolve, reject) => checkFn(resolve, reject));
|
||||
}
|
||||
|
||||
add_task(function* setup() {
|
||||
// Init the profile.
|
||||
do_get_profile(true);
|
||||
|
||||
Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true);
|
||||
Services.prefs.setBoolPref(PREF_FHR_UPLOAD_ENABLED, true);
|
||||
|
||||
// Start the ping server and let Telemetry know about it.
|
||||
PingServer.start();
|
||||
});
|
||||
|
||||
add_task(function* test_pingSender() {
|
||||
// Make sure the code can find the pingsender executable
|
||||
const XRE_APP_DISTRIBUTION_DIR = "XREAppDist";
|
||||
let dir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
|
||||
dir.initWithPath(OS.Constants.Path.libDir);
|
||||
|
||||
Services.dirsvc.registerProvider({
|
||||
getFile(aProp, aPersistent) {
|
||||
aPersistent.value = true;
|
||||
if (aProp == XRE_APP_DISTRIBUTION_DIR) {
|
||||
return dir.clone();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
PingServer.start();
|
||||
|
||||
const url = "http://localhost:" + PingServer.port + "/submit/telemetry/";
|
||||
// Generate a new ping and save it among the pending pings.
|
||||
const data = {
|
||||
type: "test-pingsender-type",
|
||||
id: TelemetryUtils.generateUUID(),
|
||||
|
@ -38,8 +59,43 @@ add_task(function* test_pingSender() {
|
|||
dummy: "stuff"
|
||||
}
|
||||
};
|
||||
yield TelemetryStorage.savePing(data, true);
|
||||
|
||||
Telemetry.runPingSender(url, JSON.stringify(data));
|
||||
// Get the local path of the saved ping.
|
||||
const pingPath = OS.Path.join(TelemetryStorage.pingDirectoryPath, data.id);
|
||||
|
||||
// Spawn an HTTP server that returns an error. We will be running the
|
||||
// PingSender twice, trying to send the ping to this server. After the
|
||||
// second time, we will resolve |deferred404Hit|.
|
||||
let failingServer = new HttpServer();
|
||||
let deferred404Hit = PromiseUtils.defer();
|
||||
let hitCount = 0;
|
||||
failingServer.registerPathHandler("/lookup_fail", (metadata, response) => {
|
||||
response.setStatusLine("1.1", 404, "Not Found");
|
||||
hitCount++;
|
||||
|
||||
if (hitCount >= 2) {
|
||||
// Resolve the promise on the next tick.
|
||||
Services.tm.currentThread.dispatch(() => deferred404Hit.resolve(),
|
||||
Ci.nsIThread.DISPATCH_NORMAL);
|
||||
}
|
||||
});
|
||||
failingServer.start(-1);
|
||||
|
||||
// Try to send the ping twice using the pingsender (we expect 404 both times).
|
||||
const errorUrl = "http://localhost:" + failingServer.identity.primaryPort + "/lookup_fail";
|
||||
Telemetry.runPingSender(errorUrl, pingPath);
|
||||
Telemetry.runPingSender(errorUrl, pingPath);
|
||||
|
||||
// Wait until we hit the 404 server twice. After that, make sure that the ping
|
||||
// still exists locally.
|
||||
yield deferred404Hit.promise;
|
||||
Assert.ok((yield OS.File.exists(pingPath)),
|
||||
"The pending ping must not be deleted if we fail to send using the PingSender");
|
||||
|
||||
// Try to send it using the pingsender.
|
||||
const url = "http://localhost:" + PingServer.port + "/submit/telemetry/";
|
||||
Telemetry.runPingSender(url, pingPath);
|
||||
|
||||
let req = yield PingServer.promiseNextRequest();
|
||||
let ping = decodeRequestPayload(req);
|
||||
|
@ -56,6 +112,13 @@ add_task(function* test_pingSender() {
|
|||
"Should have received the correct ping type.");
|
||||
Assert.deepEqual(ping.payload, data.payload,
|
||||
"Should have received the correct payload.");
|
||||
|
||||
// Check that the PingSender removed the pending ping.
|
||||
yield waitForPingDeletion(data.id);
|
||||
|
||||
// Shut down the failing server. We do this now, and not right after using it,
|
||||
// to make sure we're not interfering with the test.
|
||||
yield new Promise(r => failingServer.stop(r));
|
||||
});
|
||||
|
||||
add_task(function* cleanup() {
|
||||
|
|
|
@ -36,7 +36,7 @@ const PREF_UNIFIED = PREF_BRANCH + "unified";
|
|||
|
||||
var gClientID = null;
|
||||
|
||||
function sendPing(aSendClientId, aSendEnvironment, aOverridePingId) {
|
||||
function sendPing(aSendClientId, aSendEnvironment) {
|
||||
if (PingServer.started) {
|
||||
TelemetrySend.setServer("http://localhost:" + PingServer.port);
|
||||
} else {
|
||||
|
@ -46,7 +46,6 @@ function sendPing(aSendClientId, aSendEnvironment, aOverridePingId) {
|
|||
let options = {
|
||||
addClientId: aSendClientId,
|
||||
addEnvironment: aSendEnvironment,
|
||||
overridePingId: aOverridePingId || null,
|
||||
};
|
||||
return TelemetryController.submitExternalPing(TEST_PING_TYPE, {}, options);
|
||||
}
|
||||
|
@ -288,18 +287,6 @@ add_task(function* test_pingHasEnvironmentAndClientId() {
|
|||
Assert.equal(ping.clientId, gClientID, "The correct clientId must be reported.");
|
||||
});
|
||||
|
||||
add_task(function* test_pingIdCanBeOverridden() {
|
||||
// Send a ping with an overridden id
|
||||
const myPingId = TelemetryUtils.generateUUID();
|
||||
yield sendPing(/* aSendClientId */ false,
|
||||
/* aSendEnvironment */ false,
|
||||
myPingId);
|
||||
let ping = yield PingServer.promiseNextPing();
|
||||
checkPingFormat(ping, TEST_PING_TYPE, false, false);
|
||||
|
||||
Assert.equal(ping.id, myPingId, "The ping id must be the one we provided.");
|
||||
});
|
||||
|
||||
add_task(function* test_archivePings() {
|
||||
let now = new Date(2009, 10, 18, 12, 0, 0);
|
||||
fakeNow(now);
|
||||
|
|
|
@ -41,6 +41,7 @@ namespace CrashReporter {
|
|||
StringTable gStrings;
|
||||
string gSettingsPath;
|
||||
string gEventsPath;
|
||||
string gPingPath;
|
||||
int gArgc;
|
||||
char** gArgv;
|
||||
|
||||
|
@ -667,9 +668,10 @@ int main(int argc, char** argv)
|
|||
UIShowDefaultUI();
|
||||
} else {
|
||||
// Start by running minidump analyzer to gather stack traces.
|
||||
string empty;
|
||||
string reporterDumpFile = gReporterDumpFile;
|
||||
vector<string> args = { reporterDumpFile };
|
||||
UIRunProgram(GetProgramPath(UI_MINIDUMP_ANALYZER_FILENAME),
|
||||
gReporterDumpFile, empty, /* wait */ true);
|
||||
args, /* wait */ true);
|
||||
|
||||
// go ahead with the crash reporter
|
||||
gExtraFile = GetAdditionalFilename(gReporterDumpFile, kExtraDataExtension);
|
||||
|
@ -711,20 +713,8 @@ int main(int argc, char** argv)
|
|||
|
||||
// Hopefully the settings path exists in the environment. Try that before
|
||||
// asking the platform-specific code to guess.
|
||||
#ifdef XP_WIN32
|
||||
static const wchar_t kDataDirKey[] = L"MOZ_CRASHREPORTER_DATA_DIRECTORY";
|
||||
const wchar_t *settingsPath = _wgetenv(kDataDirKey);
|
||||
if (settingsPath && *settingsPath) {
|
||||
gSettingsPath = WideToUTF8(settingsPath);
|
||||
}
|
||||
#else
|
||||
static const char kDataDirKey[] = "MOZ_CRASHREPORTER_DATA_DIRECTORY";
|
||||
const char *settingsPath = getenv(kDataDirKey);
|
||||
if (settingsPath && *settingsPath) {
|
||||
gSettingsPath = settingsPath;
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
gSettingsPath = UIGetEnv("MOZ_CRASHREPORTER_DATA_DIRECTORY");
|
||||
if (gSettingsPath.empty()) {
|
||||
string product = queryParameters["ProductName"];
|
||||
string vendor = queryParameters["Vendor"];
|
||||
if (!UIGetSettingsPath(vendor, product, gSettingsPath)) {
|
||||
|
@ -739,22 +729,8 @@ int main(int argc, char** argv)
|
|||
|
||||
OpenLogFile();
|
||||
|
||||
#ifdef XP_WIN32
|
||||
static const wchar_t kEventsDirKey[] = L"MOZ_CRASHREPORTER_EVENTS_DIRECTORY";
|
||||
const wchar_t *eventsPath = _wgetenv(kEventsDirKey);
|
||||
if (eventsPath && *eventsPath) {
|
||||
gEventsPath = WideToUTF8(eventsPath);
|
||||
}
|
||||
#else
|
||||
static const char kEventsDirKey[] = "MOZ_CRASHREPORTER_EVENTS_DIRECTORY";
|
||||
const char *eventsPath = getenv(kEventsDirKey);
|
||||
if (eventsPath && *eventsPath) {
|
||||
gEventsPath = eventsPath;
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
gEventsPath.clear();
|
||||
}
|
||||
gEventsPath = UIGetEnv("MOZ_CRASHREPORTER_EVENTS_DIRECTORY");
|
||||
gPingPath = UIGetEnv("MOZ_CRASHREPORTER_PING_DIRECTORY");
|
||||
|
||||
// Assemble and send the crash ping
|
||||
string hash;
|
||||
|
@ -765,7 +741,7 @@ int main(int argc, char** argv)
|
|||
AppendToEventFile("MinidumpSha256Hash", hash);
|
||||
}
|
||||
|
||||
if (SendCrashPing(queryParameters, hash, pingUuid)) {
|
||||
if (SendCrashPing(queryParameters, hash, pingUuid, gPingPath)) {
|
||||
AppendToEventFile("CrashPingUUID", pingUuid);
|
||||
}
|
||||
|
||||
|
|
|
@ -119,7 +119,7 @@ namespace CrashReporter {
|
|||
|
||||
// Telemetry ping
|
||||
bool SendCrashPing(StringTable& strings, const std::string& hash,
|
||||
std::string& pingUuid);
|
||||
std::string& pingUuid, const std::string& pingDir);
|
||||
|
||||
static const unsigned int kSaveCount = 10;
|
||||
}
|
||||
|
@ -158,16 +158,17 @@ std::ofstream* UIOpenWrite(const std::string& filename,
|
|||
bool binary=false);
|
||||
void UIPruneSavedDumps(const std::string& directory);
|
||||
|
||||
// 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.
|
||||
// Run the program specified by exename, passing it the parameters in arg.
|
||||
// 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,
|
||||
const std::vector<std::string>& args,
|
||||
bool wait = false);
|
||||
|
||||
// Read the environment variable specified by name
|
||||
std::string UIGetEnv(const std::string name);
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning( pop )
|
||||
#endif
|
||||
|
|
|
@ -70,63 +70,36 @@ void UIPruneSavedDumps(const std::string& directory)
|
|||
}
|
||||
}
|
||||
|
||||
bool UIRunProgram(const std::string& exename, const std::string& arg,
|
||||
const std::string& data, bool wait)
|
||||
bool UIRunProgram(const std::string& exename,
|
||||
const std::vector<std::string>& args,
|
||||
bool wait)
|
||||
{
|
||||
bool usePipes = !data.empty();
|
||||
int fd[2];
|
||||
|
||||
if (usePipes && (pipe(fd) != 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid == -1) {
|
||||
return false;
|
||||
} else if (pid == 0) {
|
||||
// Child
|
||||
if (usePipes) {
|
||||
if (dup2(fd[0], STDIN_FILENO) == -1) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
size_t argvLen = args.size() + 2;
|
||||
char** argv = new char*[argvLen];
|
||||
|
||||
close(fd[0]);
|
||||
close(fd[1]);
|
||||
argv[0] = const_cast<char*>(exename.c_str());
|
||||
|
||||
for (size_t i = 0; i < args.size(); i++) {
|
||||
argv[i + 1] = const_cast<char*>(args[i].c_str());
|
||||
}
|
||||
|
||||
char* argv[] = {
|
||||
const_cast<char*>(exename.c_str()),
|
||||
const_cast<char*>(arg.c_str()),
|
||||
nullptr
|
||||
};
|
||||
argv[argvLen - 1] = nullptr;
|
||||
|
||||
// Run the program
|
||||
int rv = execv(exename.c_str(), argv);
|
||||
delete[] argv;
|
||||
|
||||
if (rv == -1) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
// 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);
|
||||
}
|
||||
|
@ -187,3 +160,13 @@ std::ofstream* UIOpenWrite(const string& filename,
|
|||
|
||||
return new std::ofstream(filename.c_str(), mode);
|
||||
}
|
||||
|
||||
string UIGetEnv(const string name)
|
||||
{
|
||||
const char *var = getenv(name.c_str());
|
||||
if (var && *var) {
|
||||
return var;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
|
|
@ -1550,51 +1550,25 @@ void UIPruneSavedDumps(const std::string& directory)
|
|||
}
|
||||
}
|
||||
|
||||
bool UIRunProgram(const string& exename, const string& arg,
|
||||
const string& data, bool wait)
|
||||
bool UIRunProgram(const string& exename,
|
||||
const std::vector<std::string>& args,
|
||||
bool wait)
|
||||
{
|
||||
bool usePipes = !data.empty();
|
||||
HANDLE childStdinPipeRead = nullptr;
|
||||
HANDLE childStdinPipeWrite = nullptr;
|
||||
wstring cmdLine = L"\"" + UTF8ToWide(exename) + 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;
|
||||
}
|
||||
for (auto arg : args) {
|
||||
cmdLine += L"\"" + UTF8ToWide(arg) + L"\" ";
|
||||
}
|
||||
|
||||
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 = {};
|
||||
|
||||
if (!CreateProcess(/* lpApplicationName */ nullptr,
|
||||
(LPWSTR)cmdLine.c_str(),
|
||||
/* lpProcessAttributes */ nullptr,
|
||||
/* lpThreadAttributes */ nullptr,
|
||||
usePipes,
|
||||
/* bInheritHandles */ false,
|
||||
NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW,
|
||||
/* lpEnvironment */ nullptr,
|
||||
/* lpCurrentDirectory */ nullptr,
|
||||
|
@ -1602,24 +1576,6 @@ bool UIRunProgram(const string& exename, const string& arg,
|
|||
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);
|
||||
}
|
||||
|
@ -1628,3 +1584,14 @@ bool UIRunProgram(const string& exename, const string& arg,
|
|||
CloseHandle(pi.hThread);
|
||||
return true;
|
||||
}
|
||||
|
||||
string
|
||||
UIGetEnv(const string name)
|
||||
{
|
||||
const wchar_t *var = _wgetenv(UTF8ToWide(name).c_str());
|
||||
if (var && *var) {
|
||||
return WideToUTF8(var);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
|
|
@ -273,6 +273,30 @@ GenerateSubmissionUrl(const string& aUrl, const string& aId,
|
|||
"?v=" + std::to_string(kTelemetryVersion);
|
||||
}
|
||||
|
||||
// Write out the ping into the specified file.
|
||||
//
|
||||
// Returns true if the ping was written out successfully, false otherwise.
|
||||
static bool
|
||||
WritePing(const string& aPath, const string& aPing)
|
||||
{
|
||||
ofstream* f = UIOpenWrite(aPath.c_str());
|
||||
bool success = false;
|
||||
|
||||
if (f->is_open()) {
|
||||
*f << aPing;
|
||||
f->flush();
|
||||
|
||||
if (f->good()) {
|
||||
success = true;
|
||||
}
|
||||
|
||||
f->close();
|
||||
}
|
||||
|
||||
delete f;
|
||||
return success;
|
||||
}
|
||||
|
||||
// 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
|
||||
|
@ -284,7 +308,8 @@ GenerateSubmissionUrl(const string& aUrl, const string& aId,
|
|||
// 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, const string& aHash, string& pingUuid)
|
||||
SendCrashPing(StringTable& strings, const string& aHash, string& pingUuid,
|
||||
const string& pingDir)
|
||||
{
|
||||
string clientId = strings[kTelemetryClientId];
|
||||
string serverUrl = strings[kTelemetryUrl];
|
||||
|
@ -310,12 +335,18 @@ SendCrashPing(StringTable& strings, const string& aHash, string& pingUuid)
|
|||
Json::Value root = CreateRootNode(strings, uuid, aHash, clientId, sessionId,
|
||||
name, version, channel, buildId);
|
||||
|
||||
// Write out the result
|
||||
// Write out the result to the pending pings directory
|
||||
Json::FastWriter writer;
|
||||
string ping = writer.write(root);
|
||||
string pingPath = pingDir + UI_DIR_SEPARATOR + uuid + ".json";
|
||||
|
||||
if (!WritePing(pingPath, ping)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Hand over the ping to the sender
|
||||
if (UIRunProgram(GetProgramPath(UI_PING_SENDER_FILENAME), url, ping)) {
|
||||
vector<string> args = { url, pingPath };
|
||||
if (UIRunProgram(GetProgramPath(UI_PING_SENDER_FILENAME), args)) {
|
||||
pingUuid = uuid;
|
||||
return true;
|
||||
} else {
|
||||
|
|
|
@ -1957,6 +1957,52 @@ EnsureDirectoryExists(nsIFile* dir)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
// Creates a directory that will be accessible by the crash reporter. The
|
||||
// directory will live under Firefox default data directory and will use the
|
||||
// specified name. The directory path will be passed to the crashreporter via
|
||||
// the specified environment variable.
|
||||
static nsresult
|
||||
SetupCrashReporterDirectory(nsIFile* aAppDataDirectory,
|
||||
const char* aDirName,
|
||||
const XP_CHAR* aEnvVarName,
|
||||
nsIFile** aDirectory = nullptr)
|
||||
{
|
||||
nsCOMPtr<nsIFile> directory;
|
||||
nsresult rv = aAppDataDirectory->Clone(getter_AddRefs(directory));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = directory->AppendNative(nsDependentCString(aDirName));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
EnsureDirectoryExists(directory);
|
||||
|
||||
xpstring dirEnv(aEnvVarName);
|
||||
dirEnv.append(XP_TEXT("="));
|
||||
|
||||
xpstring* directoryPath = CreatePathFromFile(directory);
|
||||
|
||||
if (!directoryPath) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
dirEnv.append(*directoryPath);
|
||||
delete directoryPath;
|
||||
|
||||
#if defined(XP_WIN32)
|
||||
_wputenv(dirEnv.c_str());
|
||||
#else
|
||||
XP_CHAR* str = new XP_CHAR[dirEnv.size() + 1];
|
||||
strncpy(str, dirEnv.c_str(), dirEnv.size() + 1);
|
||||
PR_SetEnv(str);
|
||||
#endif
|
||||
|
||||
if (aDirectory) {
|
||||
directory.forget(aDirectory);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Annotate the crash report with a Unique User ID and time
|
||||
// since install. Also do some prep work for recording
|
||||
// time since last crash, which must be calculated at
|
||||
|
@ -1966,39 +2012,26 @@ nsresult SetupExtraData(nsIFile* aAppDataDirectory,
|
|||
const nsACString& aBuildID)
|
||||
{
|
||||
nsCOMPtr<nsIFile> dataDirectory;
|
||||
nsresult rv = aAppDataDirectory->Clone(getter_AddRefs(dataDirectory));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsresult rv = SetupCrashReporterDirectory(
|
||||
aAppDataDirectory,
|
||||
"Crash Reports",
|
||||
XP_TEXT("MOZ_CRASHREPORTER_DATA_DIRECTORY"),
|
||||
getter_AddRefs(dataDirectory)
|
||||
);
|
||||
|
||||
rv = dataDirectory->AppendNative(NS_LITERAL_CSTRING("Crash Reports"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
EnsureDirectoryExists(dataDirectory);
|
||||
rv = SetupCrashReporterDirectory(
|
||||
aAppDataDirectory,
|
||||
"Pending Pings",
|
||||
XP_TEXT("MOZ_CRASHREPORTER_PING_DIRECTORY")
|
||||
);
|
||||
|
||||
#if defined(XP_WIN32)
|
||||
nsAutoString dataDirEnv(NS_LITERAL_STRING("MOZ_CRASHREPORTER_DATA_DIRECTORY="));
|
||||
|
||||
nsAutoString dataDirectoryPath;
|
||||
rv = dataDirectory->GetPath(dataDirectoryPath);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
dataDirEnv.Append(dataDirectoryPath);
|
||||
|
||||
_wputenv(dataDirEnv.get());
|
||||
#else
|
||||
// Save this path in the environment for the crash reporter application.
|
||||
nsAutoCString dataDirEnv("MOZ_CRASHREPORTER_DATA_DIRECTORY=");
|
||||
|
||||
nsAutoCString dataDirectoryPath;
|
||||
rv = dataDirectory->GetNativePath(dataDirectoryPath);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
dataDirEnv.Append(dataDirectoryPath);
|
||||
|
||||
char* env = ToNewCString(dataDirEnv);
|
||||
NS_ENSURE_TRUE(env, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
PR_SetEnv(env);
|
||||
#endif
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsAutoCString data;
|
||||
if(NS_SUCCEEDED(GetOrInit(dataDirectory,
|
||||
|
|
Загрузка…
Ссылка в новой задаче