зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset 1603f5abc56e (bug 1420363) for perma fails on test_busy_hang.xul. CLOSED TREE
--HG-- extra : rebase_source : 02c0d2f9f92f0a01ef57e4f9b38a008f6bc0eb50
This commit is contained in:
Родитель
5e32e89575
Коммит
b90bde90fc
|
@ -111,7 +111,7 @@ function createPendingCrashReports(howMany, accessDate) {
|
|||
);
|
||||
// CrashSubmit expects there to be a ServerURL key-value
|
||||
// pair in the .extra file, so we'll satisfy it.
|
||||
let extraFileContents = JSON.stringify({ ServerURL: SERVER_URL });
|
||||
let extraFileContents = "ServerURL=" + SERVER_URL;
|
||||
|
||||
return (async function() {
|
||||
let uuids = [];
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
|
||||
const { parseKeyValuePairsFromFile } = ChromeUtils.import(
|
||||
"resource://gre/modules/KeyValueParser.jsm"
|
||||
);
|
||||
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
var success = false;
|
||||
var observerFired = false;
|
||||
var observerPromise = null;
|
||||
|
||||
var testObserver = {
|
||||
idleHang: true,
|
||||
|
@ -30,37 +31,32 @@ var testObserver = {
|
|||
pluginExtraFile.append(pluginId + ".extra");
|
||||
ok(pluginExtraFile.exists(), "plugin extra file exists");
|
||||
|
||||
observerPromise = OS.File.read(pluginExtraFile.path, {
|
||||
encoding: "utf-8",
|
||||
}).then(json => {
|
||||
let extraData = JSON.parse(json);
|
||||
let extraData = parseKeyValuePairsFromFile(pluginExtraFile);
|
||||
|
||||
// check additional dumps
|
||||
ok(
|
||||
"additional_minidumps" in extraData,
|
||||
"got field for additional minidumps"
|
||||
);
|
||||
let additionalDumps = extraData.additional_minidumps.split(",");
|
||||
ok(
|
||||
additionalDumps.includes("browser"),
|
||||
"browser in additional_minidumps"
|
||||
);
|
||||
// check additional dumps
|
||||
|
||||
for (let name of additionalDumps) {
|
||||
let file = profD.clone();
|
||||
file.append(pluginId + "-" + name + ".dmp");
|
||||
ok(file.exists(), "additional dump '" + name + "' exists");
|
||||
}
|
||||
ok(
|
||||
"additional_minidumps" in extraData,
|
||||
"got field for additional minidumps"
|
||||
);
|
||||
let additionalDumps = extraData.additional_minidumps.split(",");
|
||||
ok(additionalDumps.includes("browser"), "browser in additional_minidumps");
|
||||
|
||||
// check cpu usage field
|
||||
ok("PluginCpuUsage" in extraData, "got extra field for plugin cpu usage");
|
||||
let cpuUsage = parseFloat(extraData.PluginCpuUsage);
|
||||
if (this.idleHang) {
|
||||
ok(cpuUsage == 0, "plugin cpu usage is 0%");
|
||||
} else {
|
||||
ok(cpuUsage > 0, "plugin cpu usage is >0%");
|
||||
}
|
||||
});
|
||||
for (let name of additionalDumps) {
|
||||
let file = profD.clone();
|
||||
file.append(pluginId + "-" + name + ".dmp");
|
||||
ok(file.exists(), "additional dump '" + name + "' exists");
|
||||
}
|
||||
|
||||
// check cpu usage field
|
||||
|
||||
ok("PluginCpuUsage" in extraData, "got extra field for plugin cpu usage");
|
||||
let cpuUsage = parseFloat(extraData.PluginCpuUsage);
|
||||
if (this.idleHang) {
|
||||
ok(cpuUsage == 0, "plugin cpu usage is 0%");
|
||||
} else {
|
||||
ok(cpuUsage > 0, "plugin cpu usage is >0%");
|
||||
}
|
||||
},
|
||||
|
||||
QueryInterface: ChromeUtils.generateQI([
|
||||
|
@ -106,7 +102,5 @@ function onPluginCrashed(aEvent) {
|
|||
|
||||
Services.obs.removeObserver(testObserver, "plugin-crashed");
|
||||
|
||||
observerPromise.then(() => {
|
||||
SimpleTest.finish();
|
||||
});
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
</body>
|
||||
<script class="testbody" type="application/javascript">
|
||||
<![CDATA[
|
||||
var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.expectChildProcessCrash();
|
||||
SpecialPowers.pushPrefEnv({"set": [["security.allow_eval_with_system_principal",
|
||||
|
|
|
@ -347,7 +347,7 @@ package org.mozilla.geckoview {
|
|||
method @AnyThread @NonNull public static GeckoResult<String> sendCrashReport(@NonNull Context, @NonNull Intent, @NonNull String);
|
||||
method @AnyThread @NonNull public static GeckoResult<String> sendCrashReport(@NonNull Context, @NonNull Bundle, @NonNull String);
|
||||
method @AnyThread @NonNull public static GeckoResult<String> sendCrashReport(@NonNull Context, @NonNull File, @NonNull File, @NonNull String);
|
||||
method @AnyThread @NonNull public static GeckoResult<String> sendCrashReport(@NonNull String, @NonNull File, @NonNull JSONObject);
|
||||
method @AnyThread @NonNull public static GeckoResult<String> sendCrashReport(@NonNull Context, @NonNull File, @NonNull Map<String,String>, @NonNull String);
|
||||
}
|
||||
|
||||
@UiThread public final class DynamicToolbarAnimator {
|
||||
|
|
|
@ -15,8 +15,6 @@ import android.os.Build;
|
|||
import android.os.Bundle;
|
||||
import android.os.Process;
|
||||
import android.util.Log;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONException;
|
||||
|
||||
import org.mozilla.geckoview.BuildConfig;
|
||||
import org.mozilla.geckoview.GeckoRuntime;
|
||||
|
@ -419,18 +417,20 @@ public class CrashHandler implements Thread.UncaughtExceptionHandler {
|
|||
final String url = getServerUrl(extras);
|
||||
extras.putString("ServerURL", url);
|
||||
|
||||
JSONObject json = new JSONObject();
|
||||
for (String key : extras.keySet()) {
|
||||
json.put(key, extras.get(key));
|
||||
}
|
||||
|
||||
final BufferedWriter extraWriter = new BufferedWriter(new FileWriter(extraFile));
|
||||
try {
|
||||
extraWriter.write(json.toString());
|
||||
for (String key : extras.keySet()) {
|
||||
// Each extra line is in the format, key=value, with newlines escaped.
|
||||
extraWriter.write(key);
|
||||
extraWriter.write('=');
|
||||
extraWriter.write(String.valueOf(extras.get(key)).replace("\n", "\\n"));
|
||||
extraWriter.write('\n');
|
||||
}
|
||||
} finally {
|
||||
extraWriter.close();
|
||||
}
|
||||
} catch (final IOException | JSONException e) {
|
||||
|
||||
} catch (final IOException e) {
|
||||
Log.e(LOGTAG, "Error writing extra file", e);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -9,13 +9,12 @@ import android.os.Bundle;
|
|||
import android.support.annotation.AnyThread;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
|
@ -27,10 +26,10 @@ import java.net.URLDecoder;
|
|||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
/**
|
||||
|
@ -41,7 +40,6 @@ public class CrashReporter {
|
|||
private static final String LOGTAG = "GeckoCrashReporter";
|
||||
private static final String MINI_DUMP_PATH_KEY = "upload_file_minidump";
|
||||
private static final String PAGE_URL_KEY = "URL";
|
||||
private static final String MINIDUMP_SHA256_HASH_KEY = "MinidumpSha256Hash";
|
||||
private static final String NOTES_KEY = "Notes";
|
||||
private static final String SERVER_URL_KEY = "ServerURL";
|
||||
private static final String STACK_TRACES_KEY = "StackTraces";
|
||||
|
@ -49,6 +47,7 @@ public class CrashReporter {
|
|||
private static final String PRODUCT_ID_KEY = "ProductID";
|
||||
private static final String PRODUCT_ID = "{eeb82917-e434-4870-8148-5c03d4caa81b}";
|
||||
private static final List<String> IGNORE_KEYS = Arrays.asList(
|
||||
NOTES_KEY,
|
||||
PAGE_URL_KEY,
|
||||
SERVER_URL_KEY,
|
||||
STACK_TRACES_KEY
|
||||
|
@ -131,27 +130,23 @@ public class CrashReporter {
|
|||
@NonNull final File extrasFile,
|
||||
@NonNull final String appName)
|
||||
throws IOException, URISyntaxException {
|
||||
final JSONObject annotations = getCrashAnnotations(context, minidumpFile, extrasFile, appName);
|
||||
// Compute the minidump hash and generate the stack traces
|
||||
computeMinidumpHash(extrasFile, minidumpFile);
|
||||
|
||||
final String url = annotations.optString(SERVER_URL_KEY, null);
|
||||
if (url == null) {
|
||||
return GeckoResult.fromException(new Exception("No server url present"));
|
||||
}
|
||||
// Extract the annotations from the .extra file
|
||||
HashMap<String, String> extrasMap = readStringsFromFile(extrasFile.getPath());
|
||||
|
||||
for (String key : IGNORE_KEYS) {
|
||||
annotations.remove(key);
|
||||
}
|
||||
|
||||
return sendCrashReport(url, minidumpFile, annotations);
|
||||
return sendCrashReport(context, minidumpFile, extrasMap, appName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a crash report to the Mozilla <a href="https://wiki.mozilla.org/Socorro">Socorro</a>
|
||||
* crash report server.
|
||||
*
|
||||
* @param serverURL The URL used to submit the crash report.
|
||||
* @param context The current {@link Context}
|
||||
* @param minidumpFile A {@link File} referring to the minidump.
|
||||
* @param extras A {@link JSONObject} holding the parsed JSON from the extra file.
|
||||
* @param extras A {@link HashMap} with the parsed key-value pairs from the extras file.
|
||||
* @param appName A human-readable app name.
|
||||
* @throws IOException This can be thrown if there was a networking error while sending the report.
|
||||
* @throws URISyntaxException This can be thrown if the crash server URI from the extra data was invalid.
|
||||
* @return A GeckoResult containing the crash ID as a String.
|
||||
|
@ -160,14 +155,22 @@ public class CrashReporter {
|
|||
*/
|
||||
@AnyThread
|
||||
public static @NonNull GeckoResult<String> sendCrashReport(
|
||||
@NonNull final String serverURL, @NonNull final File minidumpFile,
|
||||
@NonNull final JSONObject extras)
|
||||
throws IOException, URISyntaxException {
|
||||
@NonNull final Context context, @NonNull final File minidumpFile,
|
||||
@NonNull final Map<String, String> extras,
|
||||
@NonNull final String appName) throws IOException, URISyntaxException {
|
||||
Log.d(LOGTAG, "Sending crash report: " + minidumpFile.getPath());
|
||||
|
||||
String spec = extras.get(SERVER_URL_KEY);
|
||||
if (spec == null) {
|
||||
return GeckoResult.fromException(new Exception("No server url present"));
|
||||
}
|
||||
|
||||
extras.put(PRODUCT_NAME_KEY, appName);
|
||||
extras.put(PRODUCT_ID_KEY, PRODUCT_ID);
|
||||
|
||||
HttpURLConnection conn = null;
|
||||
try {
|
||||
final URL url = new URL(URLDecoder.decode(serverURL, "UTF-8"));
|
||||
final URL url = new URL(URLDecoder.decode(spec, "UTF-8"));
|
||||
final URI uri = new URI(url.getProtocol(), url.getUserInfo(),
|
||||
url.getHost(), url.getPort(),
|
||||
url.getPath(), url.getQuery(), url.getRef());
|
||||
|
@ -179,7 +182,38 @@ public class CrashReporter {
|
|||
conn.setRequestProperty("Content-Encoding", "gzip");
|
||||
|
||||
OutputStream os = new GZIPOutputStream(conn.getOutputStream());
|
||||
sendAnnotations(os, boundary, extras);
|
||||
for (String key : extras.keySet()) {
|
||||
if (IGNORE_KEYS.contains(key)) {
|
||||
Log.d(LOGTAG, "Ignoring: " + key);
|
||||
continue;
|
||||
}
|
||||
|
||||
sendPart(os, boundary, key, extras.get(key));
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(extras.containsKey(NOTES_KEY) ? extras.get(NOTES_KEY) + "\n" : "");
|
||||
sb.append(Build.MANUFACTURER).append(' ')
|
||||
.append(Build.MODEL).append('\n')
|
||||
.append(Build.FINGERPRINT);
|
||||
sendPart(os, boundary, NOTES_KEY, sb.toString());
|
||||
|
||||
sendPart(os, boundary, "Android_Manufacturer", Build.MANUFACTURER);
|
||||
sendPart(os, boundary, "Android_Model", Build.MODEL);
|
||||
sendPart(os, boundary, "Android_Board", Build.BOARD);
|
||||
sendPart(os, boundary, "Android_Brand", Build.BRAND);
|
||||
sendPart(os, boundary, "Android_Device", Build.DEVICE);
|
||||
sendPart(os, boundary, "Android_Display", Build.DISPLAY);
|
||||
sendPart(os, boundary, "Android_Fingerprint", Build.FINGERPRINT);
|
||||
sendPart(os, boundary, "Android_CPU_ABI", Build.CPU_ABI);
|
||||
sendPart(os, boundary, "Android_PackageName", context.getPackageName());
|
||||
try {
|
||||
sendPart(os, boundary, "Android_CPU_ABI2", Build.CPU_ABI2);
|
||||
sendPart(os, boundary, "Android_Hardware", Build.HARDWARE);
|
||||
} catch (Exception ex) {
|
||||
Log.e(LOGTAG, "Exception while sending SDK version 8 keys", ex);
|
||||
}
|
||||
sendPart(os, boundary, "Android_Version", Build.VERSION.SDK_INT + " (" + Build.VERSION.CODENAME + ")");
|
||||
sendFile(os, boundary, MINI_DUMP_PATH_KEY, minidumpFile);
|
||||
os.write(("\r\n--" + boundary + "--\r\n").getBytes());
|
||||
os.flush();
|
||||
|
@ -223,33 +257,66 @@ public class CrashReporter {
|
|||
return GeckoResult.fromException(new Exception("Failed to submit crash report"));
|
||||
}
|
||||
|
||||
private static String computeMinidumpHash(@NonNull final File minidump) throws IOException {
|
||||
MessageDigest md = null;
|
||||
FileInputStream stream = new FileInputStream(minidump);
|
||||
private static void computeMinidumpHash(final File extraFile, final File minidump) {
|
||||
try {
|
||||
md = MessageDigest.getInstance("SHA-256");
|
||||
FileInputStream stream = new FileInputStream(minidump);
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
|
||||
byte[] buffer = new byte[4096];
|
||||
int readBytes;
|
||||
try {
|
||||
byte[] buffer = new byte[4096];
|
||||
int readBytes;
|
||||
|
||||
while ((readBytes = stream.read(buffer)) != -1) {
|
||||
md.update(buffer, 0, readBytes);
|
||||
while ((readBytes = stream.read(buffer)) != -1) {
|
||||
md.update(buffer, 0, readBytes);
|
||||
}
|
||||
} finally {
|
||||
stream.close();
|
||||
}
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IOException(e);
|
||||
|
||||
byte[] digest = md.digest();
|
||||
StringBuilder hash = new StringBuilder(84);
|
||||
|
||||
hash.append("MinidumpSha256Hash=");
|
||||
|
||||
for (int i = 0; i < digest.length; i++) {
|
||||
hash.append(Integer.toHexString((digest[i] & 0xf0) >> 4));
|
||||
hash.append(Integer.toHexString(digest[i] & 0x0f));
|
||||
}
|
||||
|
||||
hash.append('\n');
|
||||
|
||||
FileWriter writer = new FileWriter(extraFile, /* append */ true);
|
||||
|
||||
try {
|
||||
writer.write(hash.toString());
|
||||
} finally {
|
||||
writer.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(LOGTAG, "exception while computing the minidump hash: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static HashMap<String, String> readStringsFromFile(final String filePath)
|
||||
throws IOException {
|
||||
FileReader fileReader = null;
|
||||
BufferedReader bufReader = null;
|
||||
try {
|
||||
fileReader = new FileReader(filePath);
|
||||
bufReader = new BufferedReader(fileReader);
|
||||
return readStringsFromReader(bufReader);
|
||||
} finally {
|
||||
stream.close();
|
||||
try {
|
||||
if (fileReader != null) {
|
||||
fileReader.close();
|
||||
}
|
||||
|
||||
if (bufReader != null) {
|
||||
bufReader.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
|
||||
byte[] digest = md.digest();
|
||||
StringBuilder hash = new StringBuilder(64);
|
||||
|
||||
for (int i = 0; i < digest.length; i++) {
|
||||
hash.append(Integer.toHexString((digest[i] & 0xf0) >> 4));
|
||||
hash.append(Integer.toHexString(digest[i] & 0x0f));
|
||||
}
|
||||
|
||||
return hash.toString();
|
||||
}
|
||||
|
||||
private static HashMap<String, String> readStringsFromReader(final BufferedReader reader)
|
||||
|
@ -267,62 +334,6 @@ public class CrashReporter {
|
|||
return map;
|
||||
}
|
||||
|
||||
private static JSONObject readExtraFile(final String filePath)
|
||||
throws IOException, JSONException {
|
||||
byte[] buffer = new byte[4096];
|
||||
FileInputStream inputStream = new FileInputStream(filePath);
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
int bytesRead = 0;
|
||||
|
||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
outputStream.write(buffer, 0, bytesRead);
|
||||
}
|
||||
|
||||
String contents = new String(outputStream.toByteArray(), "UTF-8");
|
||||
return new JSONObject(contents);
|
||||
}
|
||||
|
||||
private static JSONObject getCrashAnnotations(@NonNull final Context context,
|
||||
@NonNull final File minidump,
|
||||
@NonNull final File extra,
|
||||
@NonNull final String appName)
|
||||
throws IOException {
|
||||
try {
|
||||
final JSONObject annotations = readExtraFile(extra.getPath());
|
||||
|
||||
// Compute the minidump hash and generate the stack traces
|
||||
try {
|
||||
final String hash = computeMinidumpHash(minidump);
|
||||
annotations.put(MINIDUMP_SHA256_HASH_KEY, hash);
|
||||
} catch (Exception e) {
|
||||
Log.e(LOGTAG, "exception while computing the minidump hash: ", e);
|
||||
}
|
||||
|
||||
annotations.put(PRODUCT_NAME_KEY, appName);
|
||||
annotations.put(PRODUCT_ID_KEY, PRODUCT_ID);
|
||||
annotations.put("Android_Manufacturer", Build.MANUFACTURER);
|
||||
annotations.put("Android_Model", Build.MODEL);
|
||||
annotations.put("Android_Board", Build.BOARD);
|
||||
annotations.put("Android_Brand", Build.BRAND);
|
||||
annotations.put("Android_Device", Build.DEVICE);
|
||||
annotations.put("Android_Display", Build.DISPLAY);
|
||||
annotations.put("Android_Fingerprint", Build.FINGERPRINT);
|
||||
annotations.put("Android_CPU_ABI", Build.CPU_ABI);
|
||||
annotations.put("Android_PackageName", context.getPackageName());
|
||||
try {
|
||||
annotations.put("Android_CPU_ABI2", Build.CPU_ABI2);
|
||||
annotations.put("Android_Hardware", Build.HARDWARE);
|
||||
} catch (Exception ex) {
|
||||
Log.e(LOGTAG, "Exception while sending SDK version 8 keys", ex);
|
||||
}
|
||||
annotations.put("Android_Version", Build.VERSION.SDK_INT + " (" + Build.VERSION.CODENAME + ")");
|
||||
|
||||
return annotations;
|
||||
} catch (JSONException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String generateBoundary() {
|
||||
// Generate some random numbers to fill out the boundary
|
||||
int r0 = (int)(Integer.MAX_VALUE * Math.random());
|
||||
|
@ -330,16 +341,17 @@ public class CrashReporter {
|
|||
return String.format("---------------------------%08X%08X", r0, r1);
|
||||
}
|
||||
|
||||
private static void sendAnnotations(final OutputStream os, final String boundary,
|
||||
final JSONObject extras) throws IOException {
|
||||
os.write(("--" + boundary + "\r\n" +
|
||||
"Content-Disposition: form-data; name=\"extra\"; " +
|
||||
"filename=\"extra.json\"\r\n" +
|
||||
"Content-Type: application/json\r\n" +
|
||||
"\r\n"
|
||||
).getBytes());
|
||||
os.write(extras.toString().getBytes("UTF-8"));
|
||||
os.write('\n');
|
||||
private static void sendPart(final OutputStream os, final String boundary, final String name,
|
||||
final String data) {
|
||||
try {
|
||||
os.write(("--" + boundary + "\r\n" +
|
||||
"Content-Disposition: form-data; name=\"" + name + "\"\r\n" +
|
||||
"\r\n" +
|
||||
data + "\r\n"
|
||||
).getBytes());
|
||||
} catch (Exception ex) {
|
||||
Log.e(LOGTAG, "Exception when sending \"" + name + "\"", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void sendFile(final OutputStream os, final String boundary, final String name,
|
||||
|
|
|
@ -37,10 +37,6 @@ exclude: true
|
|||
([bug 1581161]({{bugzilla}}1581161))
|
||||
- Added `Autofill` commit support.
|
||||
([bug 1577005]({{bugzilla}}1577005))
|
||||
- Changed [`CrashReporter#sendCrashReport(Context, File, JSONObject)`][72.11] to
|
||||
accept a JSON object instead of a Map. Said object also includes the
|
||||
application name that was previously passed as the fourth argument to the
|
||||
method, which was thus removed.
|
||||
|
||||
[72.1]: {{javadoc_uri}}/GeckoSession.NavigationDelegate.LoadRequest#hasUserGesture-
|
||||
[72.2]: {{javadoc_uri}}/Autofill.html
|
||||
|
@ -52,7 +48,6 @@ exclude: true
|
|||
[72.8]: {{javadoc_uri}}/GeckoSession.SelectionActionDelegate.Selection.html
|
||||
[72.9]: {{javadoc_uri}}/BasicSelectionActionDelegate.html#getSelection-
|
||||
[72.10]: {{javadoc_uri}}/BasicSelectionActionDelegate.html#clearSelection-
|
||||
[72.11]: {{javadoc_uri}}/CrashReporter#sendCrashReport-android.content.Context-java.io.File-org.json.JSONObject-
|
||||
|
||||
## v71
|
||||
- Added a content blocking flag for blocked social cookies to [`ContentBlocking`][70.17].
|
||||
|
@ -450,4 +445,4 @@ exclude: true
|
|||
[65.24]: {{javadoc_uri}}/CrashReporter.html#sendCrashReport-android.content.Context-android.os.Bundle-java.lang.String-
|
||||
[65.25]: {{javadoc_uri}}/GeckoResult.html
|
||||
|
||||
[api-version]: 579e821b32aed4e3f51345e2cc1963a809883e9b
|
||||
[api-version]: 8d6a09b6a33550dffb6303dc01c5e6ff2d3cc499
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include "Authenticode.h"
|
||||
#include "BaseProfiler.h"
|
||||
#include "CrashAnnotations.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsWindowsDllInterceptor.h"
|
||||
#include "mozilla/CmdLineAndEnvUtils.h"
|
||||
|
@ -46,7 +47,7 @@ glue::detail::DllServicesBase* gDllServices;
|
|||
using namespace mozilla;
|
||||
|
||||
using CrashReporter::Annotation;
|
||||
using CrashReporter::AnnotationWriter;
|
||||
using CrashReporter::AnnotationToString;
|
||||
|
||||
#define DLL_BLOCKLIST_ENTRY(name, ...) {name, __VA_ARGS__},
|
||||
#define DLL_BLOCKLIST_STRING_TYPE const char*
|
||||
|
@ -216,26 +217,6 @@ class ReentrancySentinel {
|
|||
|
||||
std::map<DWORD, const char*>* ReentrancySentinel::sThreadMap;
|
||||
|
||||
class WritableBuffer {
|
||||
public:
|
||||
WritableBuffer() : mBuffer{0}, mLen(0) {}
|
||||
|
||||
void Write(const char* aData, size_t aLen) {
|
||||
size_t writable_len = std::min(aLen, Available());
|
||||
memcpy(mBuffer + mLen, aData, writable_len);
|
||||
mLen += writable_len;
|
||||
}
|
||||
|
||||
size_t const Length() { return mLen; }
|
||||
const char* Data() { return mBuffer; }
|
||||
|
||||
private:
|
||||
size_t const Available() { return sizeof(mBuffer) - mLen; }
|
||||
|
||||
char mBuffer[1024];
|
||||
size_t mLen;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a linked list of DLLs that have been blocked. It doesn't use
|
||||
* mozilla::LinkedList because this is an append-only list and doesn't need
|
||||
|
@ -245,9 +226,9 @@ class DllBlockSet {
|
|||
public:
|
||||
static void Add(const char* name, unsigned long long version);
|
||||
|
||||
// Write the list of blocked DLLs to a WritableBuffer object. This method is
|
||||
// run after a crash occurs and must therefore not use the heap, etc.
|
||||
static void Write(WritableBuffer& buffer);
|
||||
// Write the list of blocked DLLs to a file HANDLE. This method is run after
|
||||
// a crash occurs and must therefore not use the heap, etc.
|
||||
static void Write(HANDLE file);
|
||||
|
||||
private:
|
||||
DllBlockSet(const char* name, unsigned long long version)
|
||||
|
@ -275,7 +256,7 @@ void DllBlockSet::Add(const char* name, unsigned long long version) {
|
|||
gFirst = n;
|
||||
}
|
||||
|
||||
void DllBlockSet::Write(WritableBuffer& buffer) {
|
||||
void DllBlockSet::Write(HANDLE file) {
|
||||
// It would be nicer to use AutoCriticalSection here. However, its destructor
|
||||
// might not run if an exception occurs, in which case we would never leave
|
||||
// the critical section. (MSVC warns about this possibility.) So we
|
||||
|
@ -285,11 +266,12 @@ void DllBlockSet::Write(WritableBuffer& buffer) {
|
|||
// Because this method is called after a crash occurs, and uses heap memory,
|
||||
// protect this entire block with a structured exception handler.
|
||||
MOZ_SEH_TRY {
|
||||
DWORD nBytes;
|
||||
for (DllBlockSet* b = gFirst; b; b = b->mNext) {
|
||||
// write name[,v.v.v.v];
|
||||
buffer.Write(b->mName, strlen(b->mName));
|
||||
WriteFile(file, b->mName, strlen(b->mName), &nBytes, nullptr);
|
||||
if (b->mVersion != DllBlockInfo::ALL_VERSIONS) {
|
||||
buffer.Write(",", 1);
|
||||
WriteFile(file, ",", 1, &nBytes, nullptr);
|
||||
uint16_t parts[4];
|
||||
parts[0] = b->mVersion >> 48;
|
||||
parts[1] = (b->mVersion >> 32) & 0xFFFF;
|
||||
|
@ -297,14 +279,14 @@ void DllBlockSet::Write(WritableBuffer& buffer) {
|
|||
parts[3] = b->mVersion & 0xFFFF;
|
||||
for (int p = 0; p < 4; ++p) {
|
||||
char buf[32];
|
||||
_ltoa_s(parts[p], buf, sizeof(buf), 10);
|
||||
buffer.Write(buf, strlen(buf));
|
||||
ltoa(parts[p], buf, 10);
|
||||
WriteFile(file, buf, strlen(buf), &nBytes, nullptr);
|
||||
if (p != 3) {
|
||||
buffer.Write(".", 1);
|
||||
WriteFile(file, ".", 1, &nBytes, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
buffer.Write(";", 1);
|
||||
WriteFile(file, ";", 1, &nBytes, nullptr);
|
||||
}
|
||||
}
|
||||
MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {}
|
||||
|
@ -704,22 +686,31 @@ MFBT_API void DllBlocklist_Initialize(uint32_t aInitFlags) {
|
|||
MFBT_API void DllBlocklist_Shutdown() {}
|
||||
#endif // DEBUG
|
||||
|
||||
static void InternalWriteNotes(AnnotationWriter& aWriter) {
|
||||
WritableBuffer buffer;
|
||||
DllBlockSet::Write(buffer);
|
||||
static void WriteAnnotation(HANDLE aFile, Annotation aAnnotation,
|
||||
const char* aValue, DWORD* aNumBytes) {
|
||||
const char* str = AnnotationToString(aAnnotation);
|
||||
WriteFile(aFile, str, strlen(str), aNumBytes, nullptr);
|
||||
WriteFile(aFile, "=", 1, aNumBytes, nullptr);
|
||||
WriteFile(aFile, aValue, strlen(aValue), aNumBytes, nullptr);
|
||||
}
|
||||
|
||||
aWriter.Write(Annotation::BlockedDllList, buffer.Data(), buffer.Length());
|
||||
static void InternalWriteNotes(HANDLE file) {
|
||||
DWORD nBytes;
|
||||
|
||||
WriteAnnotation(file, Annotation::BlockedDllList, "", &nBytes);
|
||||
DllBlockSet::Write(file);
|
||||
WriteFile(file, "\n", 1, &nBytes, nullptr);
|
||||
|
||||
if (sBlocklistInitFailed) {
|
||||
aWriter.Write(Annotation::BlocklistInitFailed, "1");
|
||||
WriteAnnotation(file, Annotation::BlocklistInitFailed, "1\n", &nBytes);
|
||||
}
|
||||
|
||||
if (sUser32BeforeBlocklist) {
|
||||
aWriter.Write(Annotation::User32BeforeBlocklist, "1");
|
||||
WriteAnnotation(file, Annotation::User32BeforeBlocklist, "1\n", &nBytes);
|
||||
}
|
||||
}
|
||||
|
||||
using WriterFn = void (*)(AnnotationWriter&);
|
||||
using WriterFn = void (*)(HANDLE);
|
||||
static WriterFn gWriterFn = &InternalWriteNotes;
|
||||
|
||||
static void GetNativeNtBlockSetWriter() {
|
||||
|
@ -730,9 +721,9 @@ static void GetNativeNtBlockSetWriter() {
|
|||
}
|
||||
}
|
||||
|
||||
MFBT_API void DllBlocklist_WriteNotes(AnnotationWriter& aWriter) {
|
||||
MFBT_API void DllBlocklist_WriteNotes(HANDLE file) {
|
||||
MOZ_ASSERT(gWriterFn);
|
||||
gWriterFn(aWriter);
|
||||
gWriterFn(file);
|
||||
}
|
||||
|
||||
MFBT_API bool DllBlocklist_CheckStatus() {
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
(defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM64))
|
||||
|
||||
# include <windows.h>
|
||||
# include "CrashAnnotations.h"
|
||||
# include "mozilla/Attributes.h"
|
||||
# include "mozilla/Types.h"
|
||||
|
||||
|
@ -24,7 +23,7 @@ enum DllBlocklistInitFlags {
|
|||
|
||||
MFBT_API void DllBlocklist_Initialize(
|
||||
uint32_t aInitFlags = eDllBlocklistInitFlagDefault);
|
||||
MFBT_API void DllBlocklist_WriteNotes(CrashReporter::AnnotationWriter& aWriter);
|
||||
MFBT_API void DllBlocklist_WriteNotes(HANDLE file);
|
||||
MFBT_API bool DllBlocklist_CheckStatus();
|
||||
|
||||
// This export intends to clean up after DllBlocklist_Initialize().
|
||||
|
|
|
@ -29,7 +29,6 @@ const { TestUtils } = ChromeUtils.import(
|
|||
const { ContentTask } = ChromeUtils.import(
|
||||
"resource://testing-common/ContentTask.jsm"
|
||||
);
|
||||
const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
|
||||
|
@ -1642,6 +1641,13 @@ var BrowserTestUtils = {
|
|||
browsingContext
|
||||
) {
|
||||
let extra = {};
|
||||
let KeyValueParser = {};
|
||||
if (AppConstants.MOZ_CRASHREPORTER) {
|
||||
ChromeUtils.import(
|
||||
"resource://gre/modules/KeyValueParser.jsm",
|
||||
KeyValueParser
|
||||
);
|
||||
}
|
||||
|
||||
if (!browser.isRemoteBrowser) {
|
||||
throw new Error("<xul:browser> needs to be remote in order to crash");
|
||||
|
@ -1712,23 +1718,19 @@ var BrowserTestUtils = {
|
|||
if (dumpID) {
|
||||
removalPromise = Services.crashmanager
|
||||
.ensureCrashIsPresent(dumpID)
|
||||
.then(async () => {
|
||||
.then(() => {
|
||||
let minidumpDirectory = getMinidumpDirectory();
|
||||
let extrafile = minidumpDirectory.clone();
|
||||
extrafile.append(dumpID + ".extra");
|
||||
if (extrafile.exists()) {
|
||||
dump(`\nNo .extra file for dumpID: ${dumpID}\n`);
|
||||
if (AppConstants.MOZ_CRASHREPORTER) {
|
||||
let extradata = await OS.File.read(extrafile.path, {
|
||||
encoding: "utf-8",
|
||||
});
|
||||
extra = JSON.parse(extradata);
|
||||
extra = KeyValueParser.parseKeyValuePairsFromFile(extrafile);
|
||||
} else {
|
||||
dump(
|
||||
"\nCrashReporter not enabled - will not return any extra data\n"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
dump(`\nNo .extra file for dumpID: ${dumpID}\n`);
|
||||
}
|
||||
|
||||
if (shouldClearMinidumps) {
|
||||
|
|
|
@ -10,7 +10,6 @@ var { XPCOMUtils } = ChromeUtils.import(
|
|||
"resource://gre/modules/XPCOMUtils.jsm"
|
||||
);
|
||||
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
ExtensionData: "resource://gre/modules/Extension.jsm",
|
||||
|
@ -27,6 +26,53 @@ class SpecialPowersError extends Error {
|
|||
}
|
||||
}
|
||||
|
||||
function parseKeyValuePairs(text) {
|
||||
var lines = text.split("\n");
|
||||
var data = {};
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
if (lines[i] == "") {
|
||||
continue;
|
||||
}
|
||||
|
||||
// can't just .split() because the value might contain = characters
|
||||
let eq = lines[i].indexOf("=");
|
||||
if (eq != -1) {
|
||||
let [key, value] = [
|
||||
lines[i].substring(0, eq),
|
||||
lines[i].substring(eq + 1),
|
||||
];
|
||||
if (key && value) {
|
||||
data[key] = value.replace(/\\n/g, "\n").replace(/\\\\/g, "\\");
|
||||
}
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
function parseKeyValuePairsFromFile(file) {
|
||||
var fstream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(
|
||||
Ci.nsIFileInputStream
|
||||
);
|
||||
fstream.init(file, -1, 0, 0);
|
||||
var is = Cc["@mozilla.org/intl/converter-input-stream;1"].createInstance(
|
||||
Ci.nsIConverterInputStream
|
||||
);
|
||||
is.init(
|
||||
fstream,
|
||||
"UTF-8",
|
||||
1024,
|
||||
Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER
|
||||
);
|
||||
var str = {};
|
||||
var contents = "";
|
||||
while (is.readString(4096, str) != 0) {
|
||||
contents += str.value;
|
||||
}
|
||||
is.close();
|
||||
fstream.close();
|
||||
return parseKeyValuePairs(contents);
|
||||
}
|
||||
|
||||
function getTestPlugin(pluginName) {
|
||||
var ph = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
|
||||
var tags = ph.getPluginTags();
|
||||
|
@ -234,9 +280,8 @@ class SpecialPowersParent extends JSWindowActorParent {
|
|||
addDumpIDToMessage("pluginDumpID");
|
||||
addDumpIDToMessage("browserDumpID");
|
||||
|
||||
let self = this;
|
||||
let pluginID = aSubject.getPropertyAsAString("pluginDumpID");
|
||||
let extra = self._getExtraData(pluginID);
|
||||
let extra = this._getExtraData(pluginID);
|
||||
if (extra && "additional_minidumps" in extra) {
|
||||
let dumpNames = extra.additional_minidumps.split(",");
|
||||
for (let name of dumpNames) {
|
||||
|
@ -254,7 +299,6 @@ class SpecialPowersParent extends JSWindowActorParent {
|
|||
|
||||
addDumpIDToMessage("dumpID");
|
||||
}
|
||||
|
||||
this.sendAsyncMessage("SPProcessCrashService", message);
|
||||
break;
|
||||
}
|
||||
|
@ -277,22 +321,13 @@ class SpecialPowersParent extends JSWindowActorParent {
|
|||
return this._pendingCrashDumpDir;
|
||||
}
|
||||
|
||||
async _getExtraData(dumpId) {
|
||||
_getExtraData(dumpId) {
|
||||
let extraFile = this._getCrashDumpDir().clone();
|
||||
extraFile.append(dumpId + ".extra");
|
||||
if (!extraFile.exists()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var fstream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(
|
||||
Ci.nsIFileInputStream
|
||||
);
|
||||
fstream.init(extraFile, -1, 0, 0);
|
||||
let available = fstream.available();
|
||||
let json = NetUtil.readInputStreamToString(fstream, available);
|
||||
fstream.close();
|
||||
|
||||
return JSON.parse(json);
|
||||
return parseKeyValuePairsFromFile(extraFile);
|
||||
}
|
||||
|
||||
_deleteCrashDumpFiles(aFilenames) {
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
|
||||
const myScope = this;
|
||||
|
||||
const { parseKeyValuePairsFromLines } = ChromeUtils.import(
|
||||
"resource://gre/modules/KeyValueParser.jsm"
|
||||
);
|
||||
ChromeUtils.import("resource://gre/modules/Log.jsm", this);
|
||||
ChromeUtils.import("resource://gre/modules/osfile.jsm", this);
|
||||
const { PromiseUtils } = ChromeUtils.import(
|
||||
|
@ -50,7 +53,10 @@ function getAndRemoveField(obj, field) {
|
|||
let value = null;
|
||||
|
||||
if (field in obj) {
|
||||
value = obj[field];
|
||||
// We split extra files on LF characters but Windows-generated ones might
|
||||
// contain trailing CR characters so trim them here.
|
||||
value = obj[field].trim();
|
||||
|
||||
delete obj[field];
|
||||
}
|
||||
|
||||
|
@ -210,8 +216,6 @@ this.CrashManager.prototype = Object.freeze({
|
|||
EVENT_FILE_SUCCESS: "ok",
|
||||
// The event appears to be malformed.
|
||||
EVENT_FILE_ERROR_MALFORMED: "malformed",
|
||||
// The event is obsolete.
|
||||
EVENT_FILE_ERROR_OBSOLETE: "obsolete",
|
||||
// The type of event is unknown.
|
||||
EVENT_FILE_ERROR_UNKNOWN_EVENT: "unknown-event",
|
||||
|
||||
|
@ -349,7 +353,6 @@ this.CrashManager.prototype = Object.freeze({
|
|||
// Fall through.
|
||||
|
||||
case this.EVENT_FILE_ERROR_MALFORMED:
|
||||
case this.EVENT_FILE_ERROR_OBSOLETE:
|
||||
deletePaths.push(entry.path);
|
||||
break;
|
||||
|
||||
|
@ -679,7 +682,7 @@ this.CrashManager.prototype = Object.freeze({
|
|||
"TelemetryEnvironment"
|
||||
);
|
||||
let sessionId = getAndRemoveField(reportMeta, "TelemetrySessionId");
|
||||
let stackTraces = getAndRemoveField(reportMeta, "StackTraces");
|
||||
let stackTraces = parseAndRemoveField(reportMeta, "StackTraces");
|
||||
let minidumpSha256Hash = getAndRemoveField(
|
||||
reportMeta,
|
||||
"MinidumpSha256Hash"
|
||||
|
@ -719,12 +722,16 @@ this.CrashManager.prototype = Object.freeze({
|
|||
|
||||
switch (type) {
|
||||
case "crash.main.1":
|
||||
if (lines.length > 1) {
|
||||
this._log.warn(
|
||||
"Multiple lines unexpected in payload for " + entry.path
|
||||
);
|
||||
return this.EVENT_FILE_ERROR_MALFORMED;
|
||||
}
|
||||
// fall-through
|
||||
case "crash.main.2":
|
||||
return this.EVENT_FILE_ERROR_OBSOLETE;
|
||||
|
||||
case "crash.main.3":
|
||||
let crashID = lines[0];
|
||||
let metadata = JSON.parse(lines[1]);
|
||||
let metadata = parseKeyValuePairsFromLines(lines.slice(1));
|
||||
store.addCrash(
|
||||
this.PROCESS_TYPE_MAIN,
|
||||
this.CRASH_TYPE_CRASH,
|
||||
|
|
|
@ -101,10 +101,10 @@ this.TestingCrashManager.prototype = {
|
|||
})();
|
||||
},
|
||||
|
||||
createEventsFile(filename, type, date, id, content, index = 0) {
|
||||
createEventsFile(filename, type, date, content, index = 0) {
|
||||
let path = OS.Path.join(this._eventsDirs[index], filename);
|
||||
let dateInSecs = Math.floor(date.getTime() / 1000);
|
||||
let data = type + "\n" + dateInSecs + "\n" + id + "\n" + content;
|
||||
|
||||
let data = type + "\n" + Math.floor(date.getTime() / 1000) + "\n" + content;
|
||||
let encoder = new TextEncoder();
|
||||
let array = encoder.encode(data);
|
||||
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
|
||||
ChromeUtils.import("resource://gre/modules/AppConstants.jsm", this);
|
||||
ChromeUtils.import("resource://gre/modules/AsyncShutdown.jsm", this);
|
||||
const { parseKeyValuePairs } = ChromeUtils.import(
|
||||
"resource://gre/modules/KeyValueParser.jsm"
|
||||
);
|
||||
ChromeUtils.import("resource://gre/modules/osfile.jsm", this);
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm", this);
|
||||
|
||||
|
@ -118,8 +121,19 @@ function processExtraFile(extraPath) {
|
|||
try {
|
||||
let decoder = new TextDecoder();
|
||||
let extraData = await OS.File.read(extraPath);
|
||||
let keyValuePairs = parseKeyValuePairs(decoder.decode(extraData));
|
||||
|
||||
return JSON.parse(decoder.decode(extraData));
|
||||
// When reading from an .extra file literal '\\n' sequences are
|
||||
// automatically unescaped to two backslashes plus a newline, so we need
|
||||
// to re-escape them into '\\n' again so that the fields holding JSON
|
||||
// strings are valid.
|
||||
["TelemetryEnvironment", "StackTraces"].forEach(field => {
|
||||
if (field in keyValuePairs) {
|
||||
keyValuePairs[field] = keyValuePairs[field].replace(/\n/g, "n");
|
||||
}
|
||||
});
|
||||
|
||||
return keyValuePairs;
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
return {};
|
||||
|
|
|
@ -65,17 +65,6 @@ hangs in child processes can be easily recorded by the main process, we do not
|
|||
foresee the need for writing event files for child processes, design
|
||||
considerations below notwithstanding.
|
||||
|
||||
crash.main.3
|
||||
^^^^^^^^^^^^
|
||||
|
||||
This event is produced when the main process crashes.
|
||||
|
||||
The payload of this event is delimited by UNIX newlines (*\n*) and contains the
|
||||
following fields:
|
||||
|
||||
* The crash ID string, very likely a UUID
|
||||
* One line holding the crash metadata serialized as a JSON string
|
||||
|
||||
crash.main.2
|
||||
^^^^^^^^^^^^
|
||||
|
||||
|
@ -87,8 +76,6 @@ following fields:
|
|||
* The crash ID string, very likely a UUID
|
||||
* 0 or more lines of metadata, each containing one key=value pair of text
|
||||
|
||||
This event is obsolete.
|
||||
|
||||
crash.main.1
|
||||
^^^^^^^^^^^^
|
||||
|
||||
|
@ -98,8 +85,6 @@ The payload of this event is the string crash ID, very likely a UUID.
|
|||
There should be ``UUID.dmp`` and ``UUID.extra`` files on disk, saved by
|
||||
Breakpad.
|
||||
|
||||
This event is obsolete.
|
||||
|
||||
crash.submission.1
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
|
|
@ -1 +1,30 @@
|
|||
{"ContentSandboxLevel":"2","TelemetryEnvironment":"{\"EscapedField\":\"EscapedData\\n\\nfoo\"}","EMCheckCompatibility":"true","ProductName":"Firefox","ContentSandboxCapabilities":"119","TelemetryClientId":"","Vendor":"Mozilla","InstallTime":"1000000000","Theme":"classic/1.0","ReleaseChannel":"default","ServerURL":"https://crash-reports.mozilla.com","SafeMode":"0","ContentSandboxCapable":"1","useragent_locale":"en-US","Version":"55.0a1","BuildID":"20170512114708","ProductID":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","TelemetryServerURL":"","DOMIPCEnabled":"1","Add-ons":"","CrashTime":"1494582646","UptimeTS":"14.9179586","ThreadIdNameMapping":"","ContentSandboxEnabled":"1","ProcessType":"content","StartupTime":"1000000000","URL":"about:home"}
|
||||
ContentSandboxLevel=2
|
||||
TelemetryEnvironment={"EscapedField":"EscapedData\\n\\nfoo"}
|
||||
EMCheckCompatibility=true
|
||||
ProductName=Firefox
|
||||
ContentSandboxCapabilities=119
|
||||
TelemetryClientId=
|
||||
Vendor=Mozilla
|
||||
InstallTime=1000000000
|
||||
Theme=classic/1.0
|
||||
ReleaseChannel=default
|
||||
ServerURL=https://crash-reports.mozilla.com
|
||||
SafeMode=0
|
||||
ContentSandboxCapable=1
|
||||
useragent_locale=en-US
|
||||
Version=55.0a1
|
||||
BuildID=20170512114708
|
||||
ProductID={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
|
||||
TelemetryServerURL=
|
||||
DOMIPCEnabled=1
|
||||
Add-ons=
|
||||
CrashTime=1494582646
|
||||
UptimeTS=14.9179586
|
||||
ThreadIdNameMapping=
|
||||
ContentSandboxLevel=2
|
||||
ContentSandboxEnabled=1
|
||||
ProcessType=content
|
||||
DOMIPCEnabled=1
|
||||
StartupTime=1000000000
|
||||
URL=about:home
|
||||
ContentSandboxCapabilities=119
|
||||
|
|
|
@ -122,9 +122,9 @@ add_task(async function test_store_expires() {
|
|||
// Ensure discovery of unprocessed events files works.
|
||||
add_task(async function test_unprocessed_events_files() {
|
||||
let m = await getManager();
|
||||
await m.createEventsFile("1", "test.1", new Date(), "foo", "{}", 0);
|
||||
await m.createEventsFile("2", "test.1", new Date(), "bar", "{}", 0);
|
||||
await m.createEventsFile("1", "test.1", new Date(), "baz", "{}", 1);
|
||||
await m.createEventsFile("1", "test.1", new Date(), "foo", 0);
|
||||
await m.createEventsFile("2", "test.1", new Date(), "bar", 0);
|
||||
await m.createEventsFile("1", "test.1", new Date(), "baz", 1);
|
||||
|
||||
let paths = await m._getUnprocessedEventsFiles();
|
||||
Assert.equal(paths.length, 3);
|
||||
|
@ -159,7 +159,7 @@ add_task(async function test_malformed_files_deleted() {
|
|||
add_task(async function test_aggregate_ignore_unknown_events() {
|
||||
let m = await getManager();
|
||||
|
||||
await m.createEventsFile("1", "crash.main.3", DUMMY_DATE, "id1", "{}");
|
||||
await m.createEventsFile("1", "crash.main.2", DUMMY_DATE, "id1");
|
||||
await m.createEventsFile("2", "foobar.1", new Date(), "dummy");
|
||||
|
||||
let count = await m.aggregateEventsFiles();
|
||||
|
@ -176,7 +176,7 @@ add_task(async function test_prune_old() {
|
|||
let m = await getManager();
|
||||
let oldDate = new Date(Date.now() - 86400000);
|
||||
let newDate = new Date(Date.now() - 10000);
|
||||
await m.createEventsFile("1", "crash.main.3", oldDate, "id1", "{}");
|
||||
await m.createEventsFile("1", "crash.main.2", oldDate, "id1");
|
||||
await m.addCrash(m.PROCESS_TYPE_PLUGIN, m.CRASH_TYPE_CRASH, "id2", newDate);
|
||||
|
||||
await m.aggregateEventsFiles();
|
||||
|
@ -201,12 +201,12 @@ add_task(async function test_prune_old() {
|
|||
|
||||
add_task(async function test_schedule_maintenance() {
|
||||
let m = await getManager();
|
||||
await m.createEventsFile("1", "crash.main.3", DUMMY_DATE, "id1", "{}");
|
||||
await m.createEventsFile("1", "crash.main.2", DUMMY_DATE, "id1");
|
||||
|
||||
let oldDate = new Date(
|
||||
Date.now() - m.PURGE_OLDER_THAN_DAYS * 2 * 24 * 60 * 60 * 1000
|
||||
);
|
||||
await m.createEventsFile("2", "crash.main.3", oldDate, "id2", "{}");
|
||||
await m.createEventsFile("2", "crash.main.2", oldDate, "id2");
|
||||
|
||||
await m.scheduleMaintenance(25);
|
||||
let crashes = await m.getCrashes();
|
||||
|
@ -220,7 +220,7 @@ const productName = "Firefox";
|
|||
const productId = "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}";
|
||||
const sha256Hash =
|
||||
"f8410c3ac4496cfa9191a1240f0e365101aef40c7bf34fc5bcb8ec511832ed79";
|
||||
const stackTraces = { status: "OK" };
|
||||
const stackTraces = '{"status":"OK"}';
|
||||
|
||||
add_task(async function test_main_crash_event_file() {
|
||||
let ac = new TelemetryArchiveTesting.Checker();
|
||||
|
@ -233,23 +233,30 @@ add_task(async function test_main_crash_event_file() {
|
|||
theEnvironment.testValue = 'MyValue"';
|
||||
|
||||
let m = await getManager();
|
||||
const metadata = JSON.stringify({
|
||||
ProductName: productName,
|
||||
ProductID: productId,
|
||||
TelemetryEnvironment: JSON.stringify(theEnvironment),
|
||||
TelemetrySessionId: sessionId,
|
||||
MinidumpSha256Hash: sha256Hash,
|
||||
StackTraces: stackTraces,
|
||||
ThisShouldNot: "end-up-in-the-ping",
|
||||
});
|
||||
const fileContent =
|
||||
crashId +
|
||||
"\n" +
|
||||
"ProductName=" +
|
||||
productName +
|
||||
"\n" +
|
||||
"ProductID=" +
|
||||
productId +
|
||||
"\n" +
|
||||
"TelemetryEnvironment=" +
|
||||
JSON.stringify(theEnvironment) +
|
||||
"\n" +
|
||||
"TelemetrySessionId=" +
|
||||
sessionId +
|
||||
"\n" +
|
||||
"MinidumpSha256Hash=" +
|
||||
sha256Hash +
|
||||
"\n" +
|
||||
"StackTraces=" +
|
||||
stackTraces +
|
||||
"\n" +
|
||||
"ThisShouldNot=end-up-in-the-ping\n";
|
||||
|
||||
await m.createEventsFile(
|
||||
crashId,
|
||||
"crash.main.3",
|
||||
DUMMY_DATE,
|
||||
crashId,
|
||||
metadata
|
||||
);
|
||||
await m.createEventsFile(crashId, "crash.main.2", DUMMY_DATE, fileContent);
|
||||
let count = await m.aggregateEventsFiles();
|
||||
Assert.equal(count, 1);
|
||||
|
||||
|
@ -293,19 +300,18 @@ add_task(async function test_main_crash_event_file() {
|
|||
add_task(async function test_main_crash_event_file_noenv() {
|
||||
let ac = new TelemetryArchiveTesting.Checker();
|
||||
await ac.promiseInit();
|
||||
const metadata = JSON.stringify({
|
||||
ProductName: productName,
|
||||
ProductID: productId,
|
||||
});
|
||||
const fileContent =
|
||||
crashId +
|
||||
"\n" +
|
||||
"ProductName=" +
|
||||
productName +
|
||||
"\n" +
|
||||
"ProductID=" +
|
||||
productId +
|
||||
"\n";
|
||||
|
||||
let m = await getManager();
|
||||
await m.createEventsFile(
|
||||
crashId,
|
||||
"crash.main.3",
|
||||
DUMMY_DATE,
|
||||
crashId,
|
||||
metadata
|
||||
);
|
||||
await m.createEventsFile(crashId, "crash.main.2", DUMMY_DATE, fileContent);
|
||||
let count = await m.aggregateEventsFiles();
|
||||
Assert.equal(count, 1);
|
||||
|
||||
|
@ -333,13 +339,12 @@ add_task(async function test_main_crash_event_file_noenv() {
|
|||
|
||||
add_task(async function test_crash_submission_event_file() {
|
||||
let m = await getManager();
|
||||
await m.createEventsFile("1", "crash.main.3", DUMMY_DATE, "crash1", "{}");
|
||||
await m.createEventsFile("1", "crash.main.2", DUMMY_DATE, "crash1");
|
||||
await m.createEventsFile(
|
||||
"1-submission",
|
||||
"crash.submission.1",
|
||||
DUMMY_DATE_2,
|
||||
"crash1",
|
||||
"false\n"
|
||||
"crash1\nfalse\n"
|
||||
);
|
||||
|
||||
// The line below has been intentionally commented out to make sure that
|
||||
|
@ -349,8 +354,7 @@ add_task(async function test_crash_submission_event_file() {
|
|||
"2-submission",
|
||||
"crash.submission.1",
|
||||
DUMMY_DATE_2,
|
||||
"crash2",
|
||||
"true\nbp-2"
|
||||
"crash2\ntrue\nbp-2"
|
||||
);
|
||||
let count = await m.aggregateEventsFiles();
|
||||
Assert.equal(count, 3);
|
||||
|
@ -398,13 +402,7 @@ add_task(async function test_high_water_mark() {
|
|||
let store = await m._getStore();
|
||||
|
||||
for (let i = 0; i < store.HIGH_WATER_DAILY_THRESHOLD + 1; i++) {
|
||||
await m.createEventsFile(
|
||||
"m" + i,
|
||||
"crash.main.3",
|
||||
DUMMY_DATE,
|
||||
"m" + i,
|
||||
"{}"
|
||||
);
|
||||
await m.createEventsFile("m" + i, "crash.main.2", DUMMY_DATE, "m" + i);
|
||||
}
|
||||
|
||||
let count = await m.aggregateEventsFiles();
|
||||
|
|
|
@ -89,7 +89,7 @@ async function test_addCrashBase(crashId, allThreads) {
|
|||
Assert.ok(crash.metadata.StackTraces, "The StackTraces field is present.\n");
|
||||
|
||||
try {
|
||||
let stackTraces = crash.metadata.StackTraces;
|
||||
let stackTraces = JSON.parse(crash.metadata.StackTraces);
|
||||
Assert.equal(stackTraces.status, "OK");
|
||||
Assert.ok(stackTraces.crash_info, "The crash_info field is populated.");
|
||||
Assert.ok(
|
||||
|
@ -125,7 +125,7 @@ async function test_addCrashBase(crashId, allThreads) {
|
|||
} catch (e) {
|
||||
Assert.ok(
|
||||
false,
|
||||
"TelemetryEnvironment contents were not properly escaped\n"
|
||||
"TelemetryEnvironment contents were not properly re-escaped\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
#ifndef CrashAnnotations_h
|
||||
#define CrashAnnotations_h
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace CrashReporter {
|
||||
|
@ -72,15 +71,6 @@ bool IsAnnotationWhitelistedForPing(Annotation aAnnotation);
|
|||
*/
|
||||
bool IsAnnotationBlacklistedForContent(Annotation aAnnotation);
|
||||
|
||||
/**
|
||||
* Abstract annotation writer, this is needed only for code that writes out
|
||||
* annotations in the exception handler.
|
||||
*/
|
||||
class AnnotationWriter {
|
||||
public:
|
||||
virtual void Write(Annotation aAnnotation, const char* aValue, size_t aLen = 0) = 0;
|
||||
};
|
||||
|
||||
} // namespace CrashReporter
|
||||
|
||||
#endif // CrashAnnotations_h
|
||||
|
|
|
@ -12,6 +12,10 @@ const { XPCOMUtils } = ChromeUtils.import(
|
|||
const { AppConstants } = ChromeUtils.import(
|
||||
"resource://gre/modules/AppConstants.jsm"
|
||||
);
|
||||
const {
|
||||
parseKeyValuePairs,
|
||||
parseKeyValuePairsFromFileAsync,
|
||||
} = ChromeUtils.import("resource://gre/modules/KeyValueParser.jsm");
|
||||
XPCOMUtils.defineLazyGlobalGetters(this, [
|
||||
"File",
|
||||
"FormData",
|
||||
|
@ -27,7 +31,6 @@ const FAILED = "failed";
|
|||
const SUBMITTING = "submitting";
|
||||
|
||||
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
||||
const SUBMISSION_REGEX = /^bp-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
||||
|
||||
// TODO: this is still synchronous; need an async INI parser to make it async
|
||||
function parseINIStrings(path) {
|
||||
|
@ -126,23 +129,34 @@ function getPendingMinidump(id) {
|
|||
}
|
||||
|
||||
async function synthesizeExtraFile(extra) {
|
||||
let url = "https://crash-reports.mozilla.com/submit?id=";
|
||||
Services.appinfo.ID +
|
||||
let data =
|
||||
"ServerURL=https://crash-reports.mozilla.com/submit?id=" +
|
||||
Services.appinfo.ID +
|
||||
"&version=" +
|
||||
Services.appinfo.version +
|
||||
"&buildid=" +
|
||||
Services.appinfo.appBuildID;
|
||||
let data = {
|
||||
ServerURL: url,
|
||||
Vendor: Services.appinfo.vendor,
|
||||
ProductName: Services.appinfo.name,
|
||||
ProductID: Services.appinfo.ID,
|
||||
Version: Services.appinfo.version,
|
||||
BuildID: Services.appinfo.appBuildID,
|
||||
ReleaseChannel: AppConstants.MOZ_UPDATE_CHANNEL,
|
||||
};
|
||||
Services.appinfo.appBuildID +
|
||||
"\n" +
|
||||
"Vendor=" +
|
||||
Services.appinfo.vendor +
|
||||
"\n" +
|
||||
"ProductName=" +
|
||||
Services.appinfo.name +
|
||||
"\n" +
|
||||
"ProductID=" +
|
||||
Services.appinfo.ID +
|
||||
"\n" +
|
||||
"Version=" +
|
||||
Services.appinfo.version +
|
||||
"\n" +
|
||||
"BuildID=" +
|
||||
Services.appinfo.appBuildID +
|
||||
"\n" +
|
||||
"ReleaseChannel=" +
|
||||
AppConstants.MOZ_UPDATE_CHANNEL +
|
||||
"\n";
|
||||
|
||||
await OS.File.writeAtomic(extra, JSON.stringify(data), { encoding: "utf-8" });
|
||||
await OS.File.writeAtomic(extra, data, { encoding: "utf-8" });
|
||||
}
|
||||
|
||||
async function writeSubmittedReportAsync(crashID, viewURL) {
|
||||
|
@ -213,24 +227,6 @@ Submitter.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
parseResponse: function Submitter_parseResponse(response) {
|
||||
let parsedResponse = {};
|
||||
|
||||
for (let line of response.split("\n")) {
|
||||
let data = line.split("=");
|
||||
|
||||
if (
|
||||
(data.length == 2 &&
|
||||
(data[0] == "CrashID" && SUBMISSION_REGEX.test(data[1]))) ||
|
||||
data[0] == "ViewURL"
|
||||
) {
|
||||
parsedResponse[data[0]] = data[1];
|
||||
}
|
||||
}
|
||||
|
||||
return parsedResponse;
|
||||
},
|
||||
|
||||
submitForm: function Submitter_submitForm() {
|
||||
if (!("ServerURL" in this.extraKeyVals)) {
|
||||
return false;
|
||||
|
@ -252,19 +248,15 @@ Submitter.prototype = {
|
|||
let formData = new FormData();
|
||||
|
||||
// add the data
|
||||
delete this.extraKeyVals.ServerURL;
|
||||
delete this.extraKeyVals.StackTraces;
|
||||
|
||||
let payload = Object.assign({}, this.extraKeyVals);
|
||||
for (let [name, value] of Object.entries(this.extraKeyVals)) {
|
||||
if (name != "ServerURL" && name != "StackTraces") {
|
||||
formData.append(name, value);
|
||||
}
|
||||
}
|
||||
if (this.noThrottle) {
|
||||
// tell the server not to throttle this, since it was manually submitted
|
||||
payload.Throttleable = "0";
|
||||
formData.append("Throttleable", "0");
|
||||
}
|
||||
let json = new Blob([JSON.stringify(payload)], {
|
||||
type: "application/json",
|
||||
});
|
||||
formData.append("extra", json);
|
||||
|
||||
// add the minidumps
|
||||
let promises = [
|
||||
File.createFromFileName(this.dump).then(file => {
|
||||
|
@ -298,7 +290,7 @@ Submitter.prototype = {
|
|||
xhr.addEventListener("readystatechange", evt => {
|
||||
if (xhr.readyState == 4) {
|
||||
let ret =
|
||||
xhr.status === 200 ? this.parseResponse(xhr.responseText) : {};
|
||||
xhr.status === 200 ? parseKeyValuePairs(xhr.responseText) : {};
|
||||
let submitted = !!ret.CrashID;
|
||||
let p = Promise.resolve();
|
||||
|
||||
|
@ -399,9 +391,7 @@ Submitter.prototype = {
|
|||
this.extra = extra;
|
||||
this.memory = memoryExists ? memory : null;
|
||||
|
||||
let decoder = new TextDecoder();
|
||||
let extraData = await OS.File.read(extra);
|
||||
let extraKeyVals = JSON.parse(decoder.decode(extraData));
|
||||
let extraKeyVals = await parseKeyValuePairsFromFileAsync(extra);
|
||||
|
||||
for (let key in extraKeyVals) {
|
||||
if (!(key in this.extraKeyVals)) {
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/* 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/. */
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
|
||||
|
||||
var EXPORTED_SYMBOLS = [
|
||||
"parseKeyValuePairsFromLines",
|
||||
"parseKeyValuePairs",
|
||||
"parseKeyValuePairsFromFile",
|
||||
"parseKeyValuePairsFromFileAsync",
|
||||
];
|
||||
|
||||
var parseKeyValuePairsFromLines = function(lines) {
|
||||
let data = {};
|
||||
for (let line of lines) {
|
||||
if (line == "") {
|
||||
continue;
|
||||
}
|
||||
|
||||
// can't just .split() because the value might contain = characters
|
||||
let eq = line.indexOf("=");
|
||||
if (eq != -1) {
|
||||
let [key, value] = [line.substring(0, eq), line.substring(eq + 1)];
|
||||
if (key && value) {
|
||||
data[key] = value.replace(/\\n/g, "\n").replace(/\\\\/g, "\\");
|
||||
}
|
||||
}
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
function parseKeyValuePairs(text) {
|
||||
let lines = text.split("\n");
|
||||
return parseKeyValuePairsFromLines(lines);
|
||||
}
|
||||
|
||||
// some test setup still uses this sync version
|
||||
function parseKeyValuePairsFromFile(file) {
|
||||
let fstream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(
|
||||
Ci.nsIFileInputStream
|
||||
);
|
||||
fstream.init(file, -1, 0, 0);
|
||||
let is = Cc["@mozilla.org/intl/converter-input-stream;1"].createInstance(
|
||||
Ci.nsIConverterInputStream
|
||||
);
|
||||
is.init(
|
||||
fstream,
|
||||
"UTF-8",
|
||||
1024,
|
||||
Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER
|
||||
);
|
||||
let str = {};
|
||||
let contents = "";
|
||||
while (is.readString(4096, str) != 0) {
|
||||
contents += str.value;
|
||||
}
|
||||
is.close();
|
||||
fstream.close();
|
||||
return parseKeyValuePairs(contents);
|
||||
}
|
||||
|
||||
async function parseKeyValuePairsFromFileAsync(file) {
|
||||
let contents = await OS.File.read(file, { encoding: "utf-8" });
|
||||
return parseKeyValuePairs(contents);
|
||||
}
|
|
@ -28,7 +28,7 @@
|
|||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Disable exception handler warnings.
|
||||
#pragma warning(disable : 4530)
|
||||
#pragma warning( disable : 4530 )
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
|
@ -38,19 +38,19 @@
|
|||
#if _MSC_VER < 1400 // MSVC 2005/8
|
||||
// Older MSVC doesn't have fscanf_s, but they are compatible as long as
|
||||
// we don't use the string conversions (%s/%c/%S/%C).
|
||||
# define fscanf_s fscanf
|
||||
#define fscanf_s fscanf
|
||||
#endif
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
static const char kCheckpointSignature[] = "GBP1\n";
|
||||
|
||||
CrashReportSender::CrashReportSender(const wstring& checkpoint_file)
|
||||
CrashReportSender::CrashReportSender(const wstring &checkpoint_file)
|
||||
: checkpoint_file_(checkpoint_file),
|
||||
max_reports_per_day_(-1),
|
||||
last_sent_date_(-1),
|
||||
reports_sent_(0) {
|
||||
FILE* fd;
|
||||
FILE *fd;
|
||||
if (OpenCheckpointFile(L"r", &fd) == 0) {
|
||||
ReadCheckpoint(fd);
|
||||
fclose(fd);
|
||||
|
@ -58,17 +58,19 @@ CrashReportSender::CrashReportSender(const wstring& checkpoint_file)
|
|||
}
|
||||
|
||||
ReportResult CrashReportSender::SendCrashReport(
|
||||
const wstring& url, const string& parameters,
|
||||
const map<wstring, wstring>& files, wstring* report_code) {
|
||||
const wstring &url, const map<wstring, wstring> ¶meters,
|
||||
const map<wstring, wstring> &files, wstring *report_code) {
|
||||
int today = GetCurrentDate();
|
||||
if (today == last_sent_date_ && max_reports_per_day_ != -1 &&
|
||||
if (today == last_sent_date_ &&
|
||||
max_reports_per_day_ != -1 &&
|
||||
reports_sent_ >= max_reports_per_day_) {
|
||||
return RESULT_THROTTLED;
|
||||
}
|
||||
|
||||
int http_response = 0;
|
||||
bool result = HTTPUpload::SendRequest(url, parameters, files, NULL,
|
||||
report_code, &http_response);
|
||||
bool result = HTTPUpload::SendRequest(
|
||||
url, parameters, files, NULL, report_code,
|
||||
&http_response);
|
||||
|
||||
if (result) {
|
||||
ReportSent(today);
|
||||
|
@ -80,9 +82,10 @@ ReportResult CrashReportSender::SendCrashReport(
|
|||
}
|
||||
}
|
||||
|
||||
void CrashReportSender::ReadCheckpoint(FILE* fd) {
|
||||
void CrashReportSender::ReadCheckpoint(FILE *fd) {
|
||||
char buf[128];
|
||||
if (!fgets(buf, sizeof(buf), fd) || strcmp(buf, kCheckpointSignature) != 0) {
|
||||
if (!fgets(buf, sizeof(buf), fd) ||
|
||||
strcmp(buf, kCheckpointSignature) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -105,7 +108,7 @@ void CrashReportSender::ReportSent(int today) {
|
|||
++reports_sent_;
|
||||
|
||||
// Update the checkpoint file
|
||||
FILE* fd;
|
||||
FILE *fd;
|
||||
if (OpenCheckpointFile(L"w", &fd) == 0) {
|
||||
fputs(kCheckpointSignature, fd);
|
||||
fprintf(fd, "%d\n", last_sent_date_);
|
||||
|
@ -118,10 +121,10 @@ int CrashReportSender::GetCurrentDate() const {
|
|||
SYSTEMTIME system_time;
|
||||
GetSystemTime(&system_time);
|
||||
return (system_time.wYear * 10000) + (system_time.wMonth * 100) +
|
||||
system_time.wDay;
|
||||
system_time.wDay;
|
||||
}
|
||||
|
||||
int CrashReportSender::OpenCheckpointFile(const wchar_t* mode, FILE** fd) {
|
||||
int CrashReportSender::OpenCheckpointFile(const wchar_t *mode, FILE **fd) {
|
||||
if (checkpoint_file_.empty()) {
|
||||
return ENOENT;
|
||||
}
|
||||
|
|
|
@ -38,18 +38,17 @@
|
|||
// To use this library in your project, you will need to link against
|
||||
// wininet.lib.
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning( push )
|
||||
// Disable exception handler warnings.
|
||||
#pragma warning(disable : 4530)
|
||||
#pragma warning( disable : 4530 )
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using std::map;
|
||||
using std::string;
|
||||
using std::wstring;
|
||||
using std::map;
|
||||
|
||||
typedef enum {
|
||||
RESULT_FAILED = 0, // Failed to communicate with the server; try later.
|
||||
|
@ -66,32 +65,36 @@ class CrashReportSender {
|
|||
// If checkpoint_file is non-empty, breakpad will persist crash report
|
||||
// state to this file. A checkpoint file is required for
|
||||
// set_max_reports_per_day() to function properly.
|
||||
explicit CrashReportSender(const wstring& checkpoint_file);
|
||||
explicit CrashReportSender(const wstring &checkpoint_file);
|
||||
~CrashReportSender() {}
|
||||
|
||||
// Sets the maximum number of crash reports that will be sent in a 24-hour
|
||||
// period. This uses the state persisted to the checkpoint file.
|
||||
// The default value of -1 means that there is no limit on reports sent.
|
||||
void set_max_reports_per_day(int reports) { max_reports_per_day_ = reports; }
|
||||
void set_max_reports_per_day(int reports) {
|
||||
max_reports_per_day_ = reports;
|
||||
}
|
||||
|
||||
int max_reports_per_day() const { return max_reports_per_day_; }
|
||||
|
||||
// Sends the specified files, along with the map of
|
||||
// name value pairs, as a multipart POST request to the given URL.
|
||||
// Parameters are specified as a JSON-encoded string in |parameters|.
|
||||
// Parameter names must contain only printable ASCII characters,
|
||||
// and may not contain a quote (") character.
|
||||
// Only HTTP(S) URLs are currently supported. The return value indicates
|
||||
// the result of the operation (see above for possible results).
|
||||
// If report_code is non-NULL and the report is sent successfully (that is,
|
||||
// the return value is RESULT_SUCCEEDED), a code uniquely identifying the
|
||||
// report will be returned in report_code.
|
||||
// (Otherwise, report_code will be unchanged.)
|
||||
ReportResult SendCrashReport(const wstring& url, const string& parameters,
|
||||
const map<wstring, wstring>& files,
|
||||
wstring* report_code);
|
||||
ReportResult SendCrashReport(const wstring &url,
|
||||
const map<wstring, wstring> ¶meters,
|
||||
const map<wstring, wstring> &files,
|
||||
wstring *report_code);
|
||||
|
||||
private:
|
||||
// Reads persistent state from a checkpoint file.
|
||||
void ReadCheckpoint(FILE* fd);
|
||||
void ReadCheckpoint(FILE *fd);
|
||||
|
||||
// Called when a new report has been sent, to update the checkpoint state.
|
||||
void ReportSent(int today);
|
||||
|
@ -101,7 +104,7 @@ class CrashReportSender {
|
|||
|
||||
// Opens the checkpoint file with the specified mode.
|
||||
// Returns zero on success, or an error code on failure.
|
||||
int OpenCheckpointFile(const wchar_t* mode, FILE** fd);
|
||||
int OpenCheckpointFile(const wchar_t *mode, FILE **fd);
|
||||
|
||||
wstring checkpoint_file_;
|
||||
int max_reports_per_day_;
|
||||
|
@ -111,12 +114,12 @@ class CrashReportSender {
|
|||
int reports_sent_;
|
||||
|
||||
// Disallow copy constructor and operator=
|
||||
explicit CrashReportSender(const CrashReportSender&);
|
||||
void operator=(const CrashReportSender&);
|
||||
explicit CrashReportSender(const CrashReportSender &);
|
||||
void operator=(const CrashReportSender &);
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#pragma warning(pop)
|
||||
#pragma warning( pop )
|
||||
|
||||
#endif // CLIENT_WINDOWS_SENDER_CRASH_REPORT_SENDER_H__
|
||||
|
|
|
@ -1,548 +0,0 @@
|
|||
changeset: 562948:7a8aa1cef9d3
|
||||
parent: 562940:36dc32ab3d6e
|
||||
user: Gabriele Svelto <gsvelto@mozilla.com>
|
||||
date: Fri Jul 05 21:46:17 2019 +0200
|
||||
summary: Bug 1420363 - Platform-specific fixes; r=froydnj
|
||||
|
||||
diff --git a/src/common/mac/HTTPMultipartUpload.h b/src/common/mac/HTTPMultipartUpload.h
|
||||
--- a/src/common/mac/HTTPMultipartUpload.h
|
||||
+++ b/src/common/mac/HTTPMultipartUpload.h
|
||||
@@ -32,28 +32,28 @@
|
||||
// Each file is sent with a name field in addition to the filename and data
|
||||
// The data will be sent synchronously.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface HTTPMultipartUpload : NSObject {
|
||||
@protected
|
||||
NSURL *url_; // The destination URL (STRONG)
|
||||
- NSDictionary *parameters_; // The key/value pairs for sending data (STRONG)
|
||||
+ NSMutableString *parameters_; // The JSON payload for sending data (STRONG)
|
||||
NSMutableDictionary *files_; // Dictionary of name/file-path (STRONG)
|
||||
NSString *boundary_; // The boundary string (STRONG)
|
||||
NSHTTPURLResponse *response_; // The response from the send (STRONG)
|
||||
}
|
||||
|
||||
- (id)initWithURL:(NSURL *)url;
|
||||
|
||||
- (NSURL *)URL;
|
||||
|
||||
-- (void)setParameters:(NSDictionary *)parameters;
|
||||
-- (NSDictionary *)parameters;
|
||||
+- (void)setParameters:(NSMutableString *)parameters;
|
||||
+- (NSMutableString *)parameters;
|
||||
|
||||
- (void)addFileAtPath:(NSString *)path name:(NSString *)name;
|
||||
- (void)addFileContents:(NSData *)data name:(NSString *)name;
|
||||
- (NSDictionary *)files;
|
||||
|
||||
// Set the data and return the response
|
||||
- (NSData *)send:(NSError **)error;
|
||||
- (NSHTTPURLResponse *)response;
|
||||
diff --git a/src/common/mac/HTTPMultipartUpload.m b/src/common/mac/HTTPMultipartUpload.m
|
||||
--- a/src/common/mac/HTTPMultipartUpload.m
|
||||
+++ b/src/common/mac/HTTPMultipartUpload.m
|
||||
@@ -88,17 +88,17 @@ static NSData *SendSynchronousNSURLReque
|
||||
returningResponse:out_response
|
||||
error:out_error];
|
||||
#endif
|
||||
}
|
||||
@interface HTTPMultipartUpload(PrivateMethods)
|
||||
- (NSString *)multipartBoundary;
|
||||
// Each of the following methods will append the starting multipart boundary,
|
||||
// but not the ending one.
|
||||
-- (NSData *)formDataForKey:(NSString *)key value:(NSString *)value;
|
||||
+- (NSData *)formDataForJSON:(NSString *)json;
|
||||
- (NSData *)formDataForFileContents:(NSData *)contents name:(NSString *)name;
|
||||
- (NSData *)formDataForFile:(NSString *)file name:(NSString *)name;
|
||||
@end
|
||||
|
||||
@implementation HTTPMultipartUpload
|
||||
//=============================================================================
|
||||
#pragma mark -
|
||||
#pragma mark || Private ||
|
||||
@@ -105,23 +105,26 @@ static NSData *SendSynchronousNSURLReque
|
||||
//=============================================================================
|
||||
- (NSString *)multipartBoundary {
|
||||
// The boundary has 27 '-' characters followed by 16 hex digits
|
||||
return [NSString stringWithFormat:@"---------------------------%08X%08X",
|
||||
rand(), rand()];
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
-- (NSData *)formDataForKey:(NSString *)key value:(NSString *)value {
|
||||
- NSString *escaped = PercentEncodeNSString(key);
|
||||
- NSString *fmt =
|
||||
- @"--%@\r\nContent-Disposition: form-data; name=\"%@\"\r\n\r\n%@\r\n";
|
||||
- NSString *form = [NSString stringWithFormat:fmt, boundary_, escaped, value];
|
||||
+- (NSData *)formDataForJSON:(NSString *)json {
|
||||
+ NSMutableData *data = [NSMutableData data];
|
||||
+ NSString *fmt = @"--%@\r\nContent-Disposition: form-data; name=\"extra\"; "
|
||||
+ "filename=\"extra.json\"\r\nContent-Type: application/json\r\n\r\n";
|
||||
+ NSString *form = [NSString stringWithFormat:fmt, boundary_];
|
||||
|
||||
- return [form dataUsingEncoding:NSUTF8StringEncoding];
|
||||
+ [data appendData:[form dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
+ [data appendData:[json dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
+
|
||||
+ return data;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
- (NSData *)formDataForFileContents:(NSData *)contents name:(NSString *)name {
|
||||
NSMutableData *data = [NSMutableData data];
|
||||
NSString *escaped = PercentEncodeNSString(name);
|
||||
NSString *fmt = @"--%@\r\nContent-Disposition: form-data; name=\"%@\"; "
|
||||
"filename=\"minidump.dmp\"\r\nContent-Type: application/octet-stream\r\n\r\n";
|
||||
@@ -166,25 +169,25 @@ static NSData *SendSynchronousNSURLReque
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
- (NSURL *)URL {
|
||||
return url_;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
-- (void)setParameters:(NSDictionary *)parameters {
|
||||
+- (void)setParameters:(NSMutableString *)parameters {
|
||||
if (parameters != parameters_) {
|
||||
[parameters_ release];
|
||||
- parameters_ = [parameters copy];
|
||||
+ parameters_ = [parameters mutableCopy];
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
-- (NSDictionary *)parameters {
|
||||
+- (NSMutableString *)parameters {
|
||||
return parameters_;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
- (void)addFileAtPath:(NSString *)path name:(NSString *)name {
|
||||
[files_ setObject:path forKey:name];
|
||||
}
|
||||
|
||||
@@ -205,26 +208,18 @@ static NSData *SendSynchronousNSURLReque
|
||||
initWithURL:url_ cachePolicy:NSURLRequestUseProtocolCachePolicy
|
||||
timeoutInterval:10.0 ];
|
||||
|
||||
NSMutableData *postBody = [NSMutableData data];
|
||||
|
||||
[req setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@",
|
||||
boundary_] forHTTPHeaderField:@"Content-type"];
|
||||
|
||||
- // Add any parameters to the message
|
||||
- NSArray *parameterKeys = [parameters_ allKeys];
|
||||
- NSString *key;
|
||||
-
|
||||
- NSInteger count = [parameterKeys count];
|
||||
- for (NSInteger i = 0; i < count; ++i) {
|
||||
- key = [parameterKeys objectAtIndex:i];
|
||||
- [postBody appendData:[self formDataForKey:key
|
||||
- value:[parameters_ objectForKey:key]]];
|
||||
- }
|
||||
+ // Add JSON parameters to the message
|
||||
+ [postBody appendData:[self formDataForJSON:parameters_]];
|
||||
|
||||
// Add any files to the message
|
||||
NSArray *fileNames = [files_ allKeys];
|
||||
for (NSString *name in fileNames) {
|
||||
id fileOrData = [files_ objectForKey:name];
|
||||
NSData *fileData;
|
||||
|
||||
// The object can be either the path to a file (NSString) or the contents
|
||||
|
||||
changeset: 562940:36dc32ab3d6e
|
||||
parent: 562937:9987d601c5eb
|
||||
user: Gabriele Svelto <gsvelto@mozilla.com>
|
||||
date: Wed Jul 03 14:20:58 2019 +0200
|
||||
summary: Bug 1420363 - Windows-specific fixes; r=froydnj
|
||||
|
||||
diff --git a/src/common/windows/http_upload.cc b/src/common/windows/http_upload.cc
|
||||
--- a/src/common/windows/http_upload.cc
|
||||
+++ b/src/common/windows/http_upload.cc
|
||||
@@ -58,30 +58,25 @@ class HTTPUpload::AutoInternetHandle {
|
||||
HINTERNET get() { return handle_; }
|
||||
|
||||
private:
|
||||
HINTERNET handle_;
|
||||
};
|
||||
|
||||
// static
|
||||
bool HTTPUpload::SendRequest(const wstring &url,
|
||||
- const map<wstring, wstring> ¶meters,
|
||||
+ const string ¶meters,
|
||||
const map<wstring, wstring> &files,
|
||||
int *timeout,
|
||||
wstring *response_body,
|
||||
int *response_code) {
|
||||
if (response_code) {
|
||||
*response_code = 0;
|
||||
}
|
||||
|
||||
- // TODO(bryner): support non-ASCII parameter names
|
||||
- if (!CheckParameters(parameters)) {
|
||||
- return false;
|
||||
- }
|
||||
-
|
||||
// Break up the URL and make sure we can handle it
|
||||
wchar_t scheme[16], host[256], path[256];
|
||||
URL_COMPONENTS components;
|
||||
memset(&components, 0, sizeof(components));
|
||||
components.dwStructSize = sizeof(components);
|
||||
components.lpszScheme = scheme;
|
||||
components.dwSchemeLength = sizeof(scheme) / sizeof(scheme[0]);
|
||||
components.lpszHostName = host;
|
||||
@@ -260,35 +255,40 @@ wstring HTTPUpload::GenerateMultipartBou
|
||||
// static
|
||||
wstring HTTPUpload::GenerateRequestHeader(const wstring &boundary) {
|
||||
wstring header = L"Content-Type: multipart/form-data; boundary=";
|
||||
header += boundary;
|
||||
return header;
|
||||
}
|
||||
|
||||
// static
|
||||
-bool HTTPUpload::GenerateRequestBody(const map<wstring, wstring> ¶meters,
|
||||
+bool HTTPUpload::GenerateRequestBody(const string ¶meters,
|
||||
const map<wstring, wstring> &files,
|
||||
const wstring &boundary,
|
||||
string *request_body) {
|
||||
string boundary_str = WideToUTF8(boundary);
|
||||
if (boundary_str.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
request_body->clear();
|
||||
|
||||
- // Append each of the parameter pairs as a form-data part
|
||||
- for (map<wstring, wstring>::const_iterator pos = parameters.begin();
|
||||
- pos != parameters.end(); ++pos) {
|
||||
- request_body->append("--" + boundary_str + "\r\n");
|
||||
- request_body->append("Content-Disposition: form-data; name=\"" +
|
||||
- WideToUTF8(pos->first) + "\"\r\n\r\n" +
|
||||
- WideToUTF8(pos->second) + "\r\n");
|
||||
+ // Append the extra data as a single JSON form entry
|
||||
+ request_body->append("--" + boundary_str + "\r\n");
|
||||
+ request_body->append(
|
||||
+ "Content-Disposition: form-data; "
|
||||
+ "name=\"extra\"; "
|
||||
+ "filename=\"extra.json\"\r\n");
|
||||
+ request_body->append("Content-Type: application/json\r\n");
|
||||
+ request_body->append("\r\n");
|
||||
+
|
||||
+ if (!parameters.empty()) {
|
||||
+ request_body->append(parameters);
|
||||
}
|
||||
+ request_body->append("\r\n");
|
||||
|
||||
for (map<wstring, wstring>::const_iterator pos = files.begin();
|
||||
pos != files.end(); ++pos) {
|
||||
vector<char> contents;
|
||||
if (!GetFileContents(pos->second, &contents)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -394,27 +394,9 @@ string HTTPUpload::WideToMBCP(const wstr
|
||||
WideCharToMultiByte(cp, 0, wide.c_str(), -1, buf, charcount,
|
||||
NULL, NULL);
|
||||
|
||||
string result(buf);
|
||||
delete[] buf;
|
||||
return result;
|
||||
}
|
||||
|
||||
-// static
|
||||
-bool HTTPUpload::CheckParameters(const map<wstring, wstring> ¶meters) {
|
||||
- for (map<wstring, wstring>::const_iterator pos = parameters.begin();
|
||||
- pos != parameters.end(); ++pos) {
|
||||
- const wstring &str = pos->first;
|
||||
- if (str.size() == 0) {
|
||||
- return false; // disallow empty parameter names
|
||||
- }
|
||||
- for (unsigned int i = 0; i < str.size(); ++i) {
|
||||
- wchar_t c = str[i];
|
||||
- if (c < 32 || c == '"' || c > 127) {
|
||||
- return false;
|
||||
- }
|
||||
- }
|
||||
- }
|
||||
- return true;
|
||||
-}
|
||||
-
|
||||
} // namespace google_breakpad
|
||||
diff --git a/src/common/windows/http_upload.h b/src/common/windows/http_upload.h
|
||||
--- a/src/common/windows/http_upload.h
|
||||
+++ b/src/common/windows/http_upload.h
|
||||
@@ -24,17 +24,17 @@
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// HTTPUpload provides a "nice" API to send a multipart HTTP(S) POST
|
||||
// request using wininet. It currently supports requests that contain
|
||||
-// a set of string parameters (key/value pairs), and a file to upload.
|
||||
+// parameters encoded in a JSON string, and a file to upload.
|
||||
|
||||
#ifndef COMMON_WINDOWS_HTTP_UPLOAD_H_
|
||||
#define COMMON_WINDOWS_HTTP_UPLOAD_H_
|
||||
|
||||
#pragma warning(push)
|
||||
// Disable exception handler warnings.
|
||||
#pragma warning(disable : 4530)
|
||||
|
||||
@@ -42,36 +42,35 @@
|
||||
#include <wininet.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
+using std::map;
|
||||
using std::string;
|
||||
+using std::vector;
|
||||
using std::wstring;
|
||||
-using std::map;
|
||||
-using std::vector;
|
||||
|
||||
class HTTPUpload {
|
||||
public:
|
||||
// Sends the given sets of parameters and files as a multipart POST
|
||||
// request to the given URL.
|
||||
// Each key in |files| is the name of the file part of the request
|
||||
// (i.e. it corresponds to the name= attribute on an <input type="file">.
|
||||
- // Parameter names must contain only printable ASCII characters,
|
||||
- // and may not contain a quote (") character.
|
||||
+ // Parameters are specified as a JSON-encoded string in |parameters|.
|
||||
// Only HTTP(S) URLs are currently supported. Returns true on success.
|
||||
// If the request is successful and response_body is non-NULL,
|
||||
// the response body will be returned in response_body.
|
||||
// If response_code is non-NULL, it will be set to the HTTP response code
|
||||
// received (or 0 if the request failed before getting an HTTP response).
|
||||
static bool SendRequest(const wstring &url,
|
||||
- const map<wstring, wstring> ¶meters,
|
||||
+ const string ¶meters,
|
||||
const map<wstring, wstring> &files,
|
||||
int *timeout,
|
||||
wstring *response_body,
|
||||
int *response_code);
|
||||
|
||||
private:
|
||||
class AutoInternetHandle;
|
||||
|
||||
@@ -82,20 +81,20 @@ class HTTPUpload {
|
||||
static bool ReadResponse(HINTERNET request, wstring* response);
|
||||
|
||||
// Generates a new multipart boundary for a POST request
|
||||
static wstring GenerateMultipartBoundary();
|
||||
|
||||
// Generates a HTTP request header for a multipart form submit.
|
||||
static wstring GenerateRequestHeader(const wstring &boundary);
|
||||
|
||||
- // Given a set of parameters, a set of upload files, and a file part name,
|
||||
+ // Given a parameter string, a set of upload files, and a file part name,
|
||||
// generates a multipart request body string with these parameters
|
||||
// and minidump contents. Returns true on success.
|
||||
- static bool GenerateRequestBody(const map<wstring, wstring> ¶meters,
|
||||
+ static bool GenerateRequestBody(const string ¶meters,
|
||||
const map<wstring, wstring> &files,
|
||||
const wstring &boundary,
|
||||
string *request_body);
|
||||
|
||||
// Fills the supplied vector with the contents of filename.
|
||||
static bool GetFileContents(const wstring &filename, vector<char> *contents);
|
||||
|
||||
// Converts a UTF8 string to UTF16.
|
||||
@@ -104,21 +103,16 @@ class HTTPUpload {
|
||||
// Converts a UTF16 string to UTF8.
|
||||
static string WideToUTF8(const wstring &wide) {
|
||||
return WideToMBCP(wide, CP_UTF8);
|
||||
}
|
||||
|
||||
// Converts a UTF16 string to specified code page.
|
||||
static string WideToMBCP(const wstring &wide, unsigned int cp);
|
||||
|
||||
- // Checks that the given list of parameters has only printable
|
||||
- // ASCII characters in the parameter name, and does not contain
|
||||
- // any quote (") characters. Returns true if so.
|
||||
- static bool CheckParameters(const map<wstring, wstring> ¶meters);
|
||||
-
|
||||
// No instances of this class should be created.
|
||||
// Disallow all constructors, destructors, and operator=.
|
||||
HTTPUpload();
|
||||
explicit HTTPUpload(const HTTPUpload &);
|
||||
void operator=(const HTTPUpload &);
|
||||
~HTTPUpload();
|
||||
};
|
||||
|
||||
|
||||
changeset: 562937:9987d601c5eb
|
||||
parent: 562924:57f50ca45b4a
|
||||
user: Gabriele Svelto <gsvelto@mozilla.com>
|
||||
date: Thu Aug 01 15:19:52 2019 +0200
|
||||
summary: Bug 1420363 - Linux-specific bits; r=froydnj
|
||||
|
||||
diff --git a/src/common/linux/http_upload.cc b/src/common/linux/http_upload.cc
|
||||
--- a/src/common/linux/http_upload.cc
|
||||
+++ b/src/common/linux/http_upload.cc
|
||||
@@ -50,30 +50,27 @@ static size_t WriteCallback(void *ptr, s
|
||||
} // namespace
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
static const char kUserAgent[] = "Breakpad/1.0 (Linux)";
|
||||
|
||||
// static
|
||||
bool HTTPUpload::SendRequest(const string &url,
|
||||
- const map<string, string> ¶meters,
|
||||
+ const string ¶meters,
|
||||
const map<string, string> &files,
|
||||
const string &proxy,
|
||||
const string &proxy_user_pwd,
|
||||
const string &ca_certificate_file,
|
||||
string *response_body,
|
||||
long *response_code,
|
||||
string *error_description) {
|
||||
if (response_code != NULL)
|
||||
*response_code = 0;
|
||||
|
||||
- if (!CheckParameters(parameters))
|
||||
- return false;
|
||||
-
|
||||
// We may have been linked statically; if curl_easy_init is in the
|
||||
// current binary, no need to search for a dynamic version.
|
||||
void* curl_lib = dlopen(NULL, RTLD_NOW);
|
||||
if (!CheckCurlLib(curl_lib)) {
|
||||
fprintf(stderr,
|
||||
"Failed to open curl lib from binary, use libcurl.so instead\n");
|
||||
dlerror(); // Clear dlerror before attempting to open libraries.
|
||||
dlclose(curl_lib);
|
||||
@@ -128,24 +125,24 @@ bool HTTPUpload::SendRequest(const strin
|
||||
if (!ca_certificate_file.empty())
|
||||
(*curl_easy_setopt)(curl, CURLOPT_CAINFO, ca_certificate_file.c_str());
|
||||
|
||||
struct curl_httppost *formpost = NULL;
|
||||
struct curl_httppost *lastptr = NULL;
|
||||
// Add form data.
|
||||
CURLFORMcode (*curl_formadd)(struct curl_httppost **, struct curl_httppost **, ...);
|
||||
*(void**) (&curl_formadd) = dlsym(curl_lib, "curl_formadd");
|
||||
- map<string, string>::const_iterator iter = parameters.begin();
|
||||
- for (; iter != parameters.end(); ++iter)
|
||||
- (*curl_formadd)(&formpost, &lastptr,
|
||||
- CURLFORM_COPYNAME, iter->first.c_str(),
|
||||
- CURLFORM_COPYCONTENTS, iter->second.c_str(),
|
||||
- CURLFORM_END);
|
||||
+ (*curl_formadd)(&formpost, &lastptr, CURLFORM_COPYNAME, "extra",
|
||||
+ CURLFORM_BUFFER, "extra.json", CURLFORM_BUFFERPTR,
|
||||
+ parameters.c_str(), CURLFORM_BUFFERLENGTH,
|
||||
+ parameters.length(), CURLFORM_CONTENTTYPE, "application/json",
|
||||
+ CURLFORM_END);
|
||||
|
||||
// Add form files.
|
||||
+ map<string, string>::const_iterator iter = files.begin();
|
||||
for (iter = files.begin(); iter != files.end(); ++iter) {
|
||||
(*curl_formadd)(&formpost, &lastptr,
|
||||
CURLFORM_COPYNAME, iter->first.c_str(),
|
||||
CURLFORM_FILE, iter->second.c_str(),
|
||||
CURLFORM_END);
|
||||
}
|
||||
|
||||
(*curl_easy_setopt)(curl, CURLOPT_HTTPPOST, formpost);
|
||||
@@ -205,26 +202,9 @@ bool HTTPUpload::SendRequest(const strin
|
||||
|
||||
// static
|
||||
bool HTTPUpload::CheckCurlLib(void* curl_lib) {
|
||||
return curl_lib &&
|
||||
dlsym(curl_lib, "curl_easy_init") &&
|
||||
dlsym(curl_lib, "curl_easy_setopt");
|
||||
}
|
||||
|
||||
-// static
|
||||
-bool HTTPUpload::CheckParameters(const map<string, string> ¶meters) {
|
||||
- for (map<string, string>::const_iterator pos = parameters.begin();
|
||||
- pos != parameters.end(); ++pos) {
|
||||
- const string &str = pos->first;
|
||||
- if (str.size() == 0)
|
||||
- return false; // disallow empty parameter names
|
||||
- for (unsigned int i = 0; i < str.size(); ++i) {
|
||||
- int c = str[i];
|
||||
- if (c < 32 || c == '"' || c > 127) {
|
||||
- return false;
|
||||
- }
|
||||
- }
|
||||
- }
|
||||
- return true;
|
||||
-}
|
||||
-
|
||||
} // namespace google_breakpad
|
||||
diff --git a/src/common/linux/http_upload.h b/src/common/linux/http_upload.h
|
||||
--- a/src/common/linux/http_upload.h
|
||||
+++ b/src/common/linux/http_upload.h
|
||||
@@ -24,17 +24,17 @@
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// HTTPUpload provides a "nice" API to send a multipart HTTP(S) POST
|
||||
// request using libcurl. It currently supports requests that contain
|
||||
-// a set of string parameters (key/value pairs), and a file to upload.
|
||||
+// parameters encoded in a JSON string, and a file to upload.
|
||||
|
||||
#ifndef COMMON_LINUX_HTTP_UPLOAD_H__
|
||||
#define COMMON_LINUX_HTTP_UPLOAD_H__
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "common/using_std_string.h"
|
||||
@@ -44,41 +44,35 @@ namespace google_breakpad {
|
||||
using std::map;
|
||||
|
||||
class HTTPUpload {
|
||||
public:
|
||||
// Sends the given sets of parameters and files as a multipart POST
|
||||
// request to the given URL.
|
||||
// Each key in |files| is the name of the file part of the request
|
||||
// (i.e. it corresponds to the name= attribute on an <input type="file">.
|
||||
- // Parameter names must contain only printable ASCII characters,
|
||||
- // and may not contain a quote (") character.
|
||||
+ // Parameters are specified as a JSON-encoded string in |parameters|.
|
||||
// Only HTTP(S) URLs are currently supported. Returns true on success.
|
||||
// If the request is successful and response_body is non-NULL,
|
||||
// the response body will be returned in response_body.
|
||||
// If response_code is non-NULL, it will be set to the HTTP response code
|
||||
// received (or 0 if the request failed before getting an HTTP response).
|
||||
// If the send fails, a description of the error will be
|
||||
// returned in error_description.
|
||||
static bool SendRequest(const string &url,
|
||||
- const map<string, string> ¶meters,
|
||||
+ const string ¶meters,
|
||||
const map<string, string> &files,
|
||||
const string &proxy,
|
||||
const string &proxy_user_pwd,
|
||||
const string &ca_certificate_file,
|
||||
string *response_body,
|
||||
long *response_code,
|
||||
string *error_description);
|
||||
|
||||
private:
|
||||
- // Checks that the given list of parameters has only printable
|
||||
- // ASCII characters in the parameter name, and does not contain
|
||||
- // any quote (") characters. Returns true if so.
|
||||
- static bool CheckParameters(const map<string, string> ¶meters);
|
||||
-
|
||||
// Checks the curl_lib parameter points to a valid curl lib.
|
||||
static bool CheckCurlLib(void* curl_lib);
|
||||
|
||||
// No instances of this class should be created.
|
||||
// Disallow all constructors, destructors, and operator=.
|
||||
HTTPUpload();
|
||||
explicit HTTPUpload(const HTTPUpload &);
|
||||
void operator=(const HTTPUpload &);
|
||||
|
|
@ -24,7 +24,6 @@
|
|||
# include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#include "json/json.h"
|
||||
#include "nss.h"
|
||||
#include "sechash.h"
|
||||
|
||||
|
@ -42,7 +41,6 @@ using std::vector;
|
|||
namespace CrashReporter {
|
||||
|
||||
StringTable gStrings;
|
||||
Json::Value gData;
|
||||
string gSettingsPath;
|
||||
string gEventsPath;
|
||||
string gPingPath;
|
||||
|
@ -129,18 +127,6 @@ bool ReadStringsFromFile(const string& path, StringTable& strings,
|
|||
return success;
|
||||
}
|
||||
|
||||
static bool ReadExtraFile(const string& aExtraDataPath, Json::Value& aExtra) {
|
||||
bool success = false;
|
||||
ifstream* f = UIOpenRead(aExtraDataPath, ios::in);
|
||||
if (f->is_open()) {
|
||||
Json::CharReaderBuilder builder;
|
||||
success = parseFromStream(builder, *f, &aExtra, nullptr);
|
||||
}
|
||||
|
||||
delete f;
|
||||
return success;
|
||||
}
|
||||
|
||||
static string Basename(const string& file) {
|
||||
string::size_type slashIndex = file.rfind(UI_DIR_SEPARATOR);
|
||||
if (slashIndex != string::npos)
|
||||
|
@ -158,50 +144,9 @@ static string GetDumpLocalID() {
|
|||
return localId.substr(0, dot);
|
||||
}
|
||||
|
||||
static bool ReadEventFile(const string& aPath, string& aEventVersion,
|
||||
string& aTime, string& aUuid, Json::Value& aData) {
|
||||
bool res = false;
|
||||
ifstream* f = UIOpenRead(aPath, ios::binary);
|
||||
|
||||
if (f->is_open()) {
|
||||
std::getline(*f, aEventVersion, '\n');
|
||||
res = f->good();
|
||||
std::getline(*f, aTime, '\n');
|
||||
res &= f->good();
|
||||
std::getline(*f, aUuid, '\n');
|
||||
res &= f->good();
|
||||
|
||||
if (res) {
|
||||
Json::CharReaderBuilder builder;
|
||||
res = parseFromStream(builder, *f, &aData, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
delete f;
|
||||
return res;
|
||||
}
|
||||
|
||||
static void OverwriteEventFile(const string& aPath, const string& aEventVersion,
|
||||
const string& aTime, const string& aUuid,
|
||||
const Json::Value& aData) {
|
||||
ofstream* f = UIOpenWrite(aPath, ios::binary | ios::trunc);
|
||||
if (f->is_open()) {
|
||||
f->write(aEventVersion.c_str(), aEventVersion.length()) << '\n';
|
||||
f->write(aTime.c_str(), aTime.length()) << '\n';
|
||||
f->write(aUuid.c_str(), aUuid.length()) << '\n';
|
||||
|
||||
Json::StreamWriterBuilder builder;
|
||||
builder["indentation"] = "";
|
||||
std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
|
||||
writer->write(aData, f);
|
||||
*f << "\n";
|
||||
}
|
||||
|
||||
delete f;
|
||||
}
|
||||
|
||||
static void UpdateEventFile(const Json::Value& aExtraData, const string& aHash,
|
||||
const string& aPingUuid) {
|
||||
// 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.
|
||||
return;
|
||||
|
@ -209,28 +154,14 @@ static void UpdateEventFile(const Json::Value& aExtraData, const string& aHash,
|
|||
|
||||
string localId = GetDumpLocalID();
|
||||
string path = gEventsPath + UI_DIR_SEPARATOR + localId;
|
||||
string eventVersion;
|
||||
string crashTime;
|
||||
string crashUuid;
|
||||
Json::Value eventData;
|
||||
ofstream* f = UIOpenWrite(path, ios::app);
|
||||
|
||||
if (!ReadEventFile(path, eventVersion, crashTime, crashUuid, eventData)) {
|
||||
return;
|
||||
if (f->is_open()) {
|
||||
*f << aKey << "=" << aValue << std::endl;
|
||||
f->close();
|
||||
}
|
||||
|
||||
if (!aHash.empty()) {
|
||||
eventData["MinidumpSha256Hash"] = aHash;
|
||||
}
|
||||
|
||||
if (!aPingUuid.empty()) {
|
||||
eventData["CrashPingUUID"] = aPingUuid;
|
||||
}
|
||||
|
||||
if (aExtraData.isMember("StackTraces")) {
|
||||
eventData["StackTraces"] = aExtraData["StackTraces"];
|
||||
}
|
||||
|
||||
OverwriteEventFile(path, eventVersion, crashTime, crashUuid, eventData);
|
||||
delete f;
|
||||
}
|
||||
|
||||
static void WriteSubmissionEvent(SubmissionResult result,
|
||||
|
@ -537,13 +468,14 @@ static string ComputeDumpHash() {
|
|||
|
||||
using namespace CrashReporter;
|
||||
|
||||
Json::Value kEmptyJsonString("");
|
||||
|
||||
void RewriteStrings(Json::Value& aExtraData) {
|
||||
void RewriteStrings(StringTable& queryParameters) {
|
||||
// rewrite some UI strings with the values from the query parameters
|
||||
string product = aExtraData.get("ProductName", kEmptyJsonString).asString();
|
||||
Json::Value mozilla("Mozilla");
|
||||
string vendor = aExtraData.get("Vendor", mozilla).asString();
|
||||
string product = queryParameters["ProductName"];
|
||||
string vendor = queryParameters["Vendor"];
|
||||
if (vendor.empty()) {
|
||||
// Assume Mozilla if no vendor is specified
|
||||
vendor = "Mozilla";
|
||||
}
|
||||
|
||||
char buf[4096];
|
||||
UI_SNPRINTF(buf, sizeof(buf), gStrings[ST_CRASHREPORTERVENDORTITLE].c_str(),
|
||||
|
@ -590,13 +522,8 @@ void RewriteStrings(Json::Value& aExtraData) {
|
|||
gStrings[ST_ERROR_ENDOFLIFE] = buf;
|
||||
}
|
||||
|
||||
bool CheckEndOfLifed(const Json::Value& aVersion) {
|
||||
if (!aVersion.isString()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string reportPath =
|
||||
gSettingsPath + UI_DIR_SEPARATOR + "EndOfLife" + aVersion.asString();
|
||||
bool CheckEndOfLifed(string version) {
|
||||
string reportPath = gSettingsPath + UI_DIR_SEPARATOR + "EndOfLife" + version;
|
||||
return UIFileExists(reportPath);
|
||||
}
|
||||
|
||||
|
@ -673,22 +600,22 @@ int main(int argc, char** argv) {
|
|||
gMemoryFile.erase();
|
||||
}
|
||||
|
||||
Json::Value extraData;
|
||||
if (!ReadExtraFile(gExtraFile, extraData)) {
|
||||
StringTable queryParameters;
|
||||
if (!ReadStringsFromFile(gExtraFile, queryParameters, true)) {
|
||||
UIError(gStrings[ST_ERROR_EXTRAFILEREAD]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!extraData.isMember("ProductName")) {
|
||||
if (queryParameters.find("ProductName") == queryParameters.end()) {
|
||||
UIError(gStrings[ST_ERROR_NOPRODUCTNAME]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// There is enough information in the extra file to rewrite strings
|
||||
// to be product specific
|
||||
RewriteStrings(extraData);
|
||||
RewriteStrings(queryParameters);
|
||||
|
||||
if (!extraData.isMember("ServerURL")) {
|
||||
if (queryParameters.find("ServerURL") == queryParameters.end()) {
|
||||
UIError(gStrings[ST_ERROR_NOSERVERURL]);
|
||||
return 0;
|
||||
}
|
||||
|
@ -697,10 +624,8 @@ int main(int argc, char** argv) {
|
|||
// asking the platform-specific code to guess.
|
||||
gSettingsPath = UIGetEnv("MOZ_CRASHREPORTER_DATA_DIRECTORY");
|
||||
if (gSettingsPath.empty()) {
|
||||
string product =
|
||||
extraData.get("ProductName", kEmptyJsonString).asString();
|
||||
string vendor = extraData.get("Vendor", kEmptyJsonString).asString();
|
||||
|
||||
string product = queryParameters["ProductName"];
|
||||
string vendor = queryParameters["Vendor"];
|
||||
if (!UIGetSettingsPath(vendor, product, gSettingsPath)) {
|
||||
gSettingsPath.clear();
|
||||
}
|
||||
|
@ -717,11 +642,23 @@ int main(int argc, char** argv) {
|
|||
gPingPath = UIGetEnv("MOZ_CRASHREPORTER_PING_DIRECTORY");
|
||||
|
||||
// Assemble and send the crash ping
|
||||
string hash = ComputeDumpHash();
|
||||
|
||||
string hash;
|
||||
string pingUuid;
|
||||
SendCrashPing(extraData, hash, pingUuid, gPingPath);
|
||||
UpdateEventFile(extraData, hash, pingUuid);
|
||||
|
||||
hash = ComputeDumpHash();
|
||||
if (!hash.empty()) {
|
||||
AppendToEventFile("MinidumpSha256Hash", hash);
|
||||
}
|
||||
|
||||
if (SendCrashPing(queryParameters, hash, pingUuid, gPingPath)) {
|
||||
AppendToEventFile("CrashPingUUID", pingUuid);
|
||||
}
|
||||
|
||||
// Update the crash event with stacks if they are present
|
||||
auto stackTracesItr = queryParameters.find("StackTraces");
|
||||
if (stackTracesItr != queryParameters.end()) {
|
||||
AppendToEventFile(stackTracesItr->first, stackTracesItr->second);
|
||||
}
|
||||
|
||||
if (!UIFileExists(gReporterDumpFile)) {
|
||||
UIError(gStrings[ST_ERROR_DUMPFILEEXISTS]);
|
||||
|
@ -734,12 +671,12 @@ int main(int argc, char** argv) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
string sendURL = extraData.get("ServerURL", kEmptyJsonString).asString();
|
||||
string sendURL = queryParameters["ServerURL"];
|
||||
// we don't need to actually send these
|
||||
extraData.removeMember("ServerURL");
|
||||
extraData.removeMember("StackTraces");
|
||||
queryParameters.erase("ServerURL");
|
||||
queryParameters.erase("StackTraces");
|
||||
|
||||
extraData["Throttleable"] = "1";
|
||||
queryParameters["Throttleable"] = "1";
|
||||
|
||||
// re-set XUL_APP_FILE for xulrunner wrapped apps
|
||||
const char* appfile = getenv("MOZ_CRASHREPORTER_RESTART_XUL_APP_FILE");
|
||||
|
@ -779,9 +716,8 @@ int main(int argc, char** argv) {
|
|||
}
|
||||
|
||||
// see if this version has been end-of-lifed
|
||||
|
||||
if (extraData.isMember("Version") &&
|
||||
CheckEndOfLifed(extraData["Version"])) {
|
||||
if (queryParameters.find("Version") != queryParameters.end() &&
|
||||
CheckEndOfLifed(queryParameters["Version"])) {
|
||||
UIError(gStrings[ST_ERROR_ENDOFLIFE]);
|
||||
DeleteDump();
|
||||
return 0;
|
||||
|
@ -793,9 +729,8 @@ int main(int argc, char** argv) {
|
|||
files["memory_report"] = gMemoryFile;
|
||||
}
|
||||
|
||||
if (!UIShowCrashUI(files, extraData, sendURL, restartArgs)) {
|
||||
if (!UIShowCrashUI(files, queryParameters, sendURL, restartArgs))
|
||||
DeleteDump();
|
||||
}
|
||||
}
|
||||
|
||||
UIShutdown();
|
||||
|
|
|
@ -37,8 +37,6 @@ std::string WideToUTF8(const std::wstring& wide, bool* success = 0);
|
|||
|
||||
#endif
|
||||
|
||||
#include "json/json.h"
|
||||
|
||||
#define UI_CRASH_REPORTER_FILENAME "crashreporter"
|
||||
#define UI_MINIDUMP_ANALYZER_FILENAME "minidump-analyzer"
|
||||
#define UI_PING_SENDER_FILENAME "pingsender"
|
||||
|
@ -105,7 +103,7 @@ void LogMessage(const std::string& message);
|
|||
void DeleteDump();
|
||||
|
||||
// Telemetry ping
|
||||
bool SendCrashPing(Json::Value& extra, const std::string& hash,
|
||||
bool SendCrashPing(StringTable& strings, const std::string& hash,
|
||||
std::string& pingUuid, const std::string& pingDir);
|
||||
|
||||
static const unsigned int kSaveCount = 10;
|
||||
|
@ -124,7 +122,7 @@ void UIShowDefaultUI();
|
|||
// Run the UI for when the app was launched with a dump file
|
||||
// Return true if the user sent (or tried to send) the crash report,
|
||||
// false if they chose not to, and it should be deleted.
|
||||
bool UIShowCrashUI(const StringTable& files, const Json::Value& queryParameters,
|
||||
bool UIShowCrashUI(const StringTable& files, const StringTable& queryParameters,
|
||||
const std::string& sendURL,
|
||||
const std::vector<std::string>& restartArgs);
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ GtkWidget* gRestartButton = 0;
|
|||
bool gInitialized = false;
|
||||
bool gDidTrySend = false;
|
||||
StringTable gFiles;
|
||||
Json::Value gQueryParameters;
|
||||
StringTable gQueryParameters;
|
||||
string gHttpProxy;
|
||||
string gAuth;
|
||||
string gCACertificateFile;
|
||||
|
@ -180,15 +180,11 @@ void LoadProxyinfo() {
|
|||
}
|
||||
|
||||
gpointer SendThread(gpointer args) {
|
||||
Json::StreamWriterBuilder builder;
|
||||
builder["indentation"] = "";
|
||||
string parameters(writeString(builder, gQueryParameters));
|
||||
|
||||
string response, error;
|
||||
long response_code;
|
||||
|
||||
bool success = google_breakpad::HTTPUpload::SendRequest(
|
||||
gSendURL, parameters, gFiles, gHttpProxy, gAuth, gCACertificateFile,
|
||||
gSendURL, gQueryParameters, gFiles, gHttpProxy, gAuth, gCACertificateFile,
|
||||
&response, &response_code, &error);
|
||||
if (success) {
|
||||
LogMessage("Crash report submitted successfully");
|
||||
|
@ -247,7 +243,7 @@ static void UpdateURL() {
|
|||
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gIncludeURLCheck))) {
|
||||
gQueryParameters["URL"] = gURLParameter;
|
||||
} else {
|
||||
gQueryParameters.removeMember("URL");
|
||||
gQueryParameters.erase("URL");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,8 +11,6 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "json/json.h"
|
||||
|
||||
const char kIniFile[] = "crashreporter.ini";
|
||||
|
||||
extern GtkWidget* gWindow;
|
||||
|
@ -29,7 +27,7 @@ extern GThread* gSendThreadID;
|
|||
extern bool gInitialized;
|
||||
extern bool gDidTrySend;
|
||||
extern StringTable gFiles;
|
||||
extern Json::Value gQueryParameters;
|
||||
extern StringTable gQueryParameters;
|
||||
extern std::string gHttpProxy;
|
||||
extern std::string gAuth;
|
||||
extern std::string gCACertificateFile;
|
||||
|
|
|
@ -175,20 +175,11 @@ static void ShowReportInfo(GtkTextView* viewReportTextView) {
|
|||
|
||||
gtk_text_buffer_delete(buffer, &start, &end);
|
||||
|
||||
for (Json::ValueConstIterator iter = gQueryParameters.begin();
|
||||
iter != gQueryParameters.end(); ++iter) {
|
||||
gtk_text_buffer_insert(buffer, &end, iter.name().c_str(),
|
||||
iter.name().length());
|
||||
for (StringTable::iterator iter = gQueryParameters.begin();
|
||||
iter != gQueryParameters.end(); iter++) {
|
||||
gtk_text_buffer_insert(buffer, &end, iter->first.c_str(), -1);
|
||||
gtk_text_buffer_insert(buffer, &end, ": ", -1);
|
||||
string value;
|
||||
if (iter->isString()) {
|
||||
value = iter->asString();
|
||||
} else {
|
||||
Json::StreamWriterBuilder builder;
|
||||
builder["indentation"] = "";
|
||||
value = writeString(builder, *iter);
|
||||
}
|
||||
gtk_text_buffer_insert(buffer, &end, value.c_str(), value.length());
|
||||
gtk_text_buffer_insert(buffer, &end, iter->second.c_str(), -1);
|
||||
gtk_text_buffer_insert(buffer, &end, "\n", -1);
|
||||
}
|
||||
|
||||
|
@ -252,11 +243,10 @@ static void CommentChanged(GtkTextBuffer* buffer, gpointer userData) {
|
|||
gtk_text_buffer_get_start_iter(buffer, &start);
|
||||
gtk_text_buffer_get_end_iter(buffer, &end);
|
||||
const char* comment = gtk_text_buffer_get_text(buffer, &start, &end, TRUE);
|
||||
if (comment[0] == '\0' || gCommentFieldHint) {
|
||||
gQueryParameters.removeMember("Comments");
|
||||
} else {
|
||||
if (comment[0] == '\0' || gCommentFieldHint)
|
||||
gQueryParameters.erase("Comments");
|
||||
else
|
||||
gQueryParameters["Comments"] = comment;
|
||||
}
|
||||
}
|
||||
|
||||
static void CommentInsert(GtkTextBuffer* buffer, GtkTextIter* location,
|
||||
|
@ -332,11 +322,10 @@ static void UpdateEmail() {
|
|||
email = "";
|
||||
gtk_widget_set_sensitive(gEmailEntry, FALSE);
|
||||
}
|
||||
if (email[0] == '\0' || gEmailFieldHint) {
|
||||
gQueryParameters.removeMember("Email");
|
||||
} else {
|
||||
if (email[0] == '\0' || gEmailFieldHint)
|
||||
gQueryParameters.erase("Email");
|
||||
else
|
||||
gQueryParameters["Email"] = email;
|
||||
}
|
||||
}
|
||||
|
||||
static void EmailMeClicked(GtkButton* sender, gpointer userData) {
|
||||
|
@ -403,15 +392,14 @@ void UIShutdown() {
|
|||
// Don't dlclose gnomeLib as libgnomevfs and libORBit-2 use atexit().
|
||||
}
|
||||
|
||||
bool UIShowCrashUI(const StringTable& files, const Json::Value& queryParameters,
|
||||
bool UIShowCrashUI(const StringTable& files, const StringTable& queryParameters,
|
||||
const string& sendURL, const vector<string>& restartArgs) {
|
||||
gFiles = files;
|
||||
gQueryParameters = queryParameters;
|
||||
gSendURL = sendURL;
|
||||
gRestartArgs = restartArgs;
|
||||
if (gQueryParameters.isMember("URL")) {
|
||||
gURLParameter = gQueryParameters["URL"].asString();
|
||||
}
|
||||
if (gQueryParameters.find("URL") != gQueryParameters.end())
|
||||
gURLParameter = gQueryParameters["URL"];
|
||||
|
||||
if (gAutoSubmit) {
|
||||
SendReport();
|
||||
|
@ -513,7 +501,7 @@ bool UIShowCrashUI(const StringTable& files, const Json::Value& queryParameters,
|
|||
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(gCommentText), GTK_WRAP_WORD_CHAR);
|
||||
gtk_widget_set_size_request(GTK_WIDGET(gCommentText), -1, 100);
|
||||
|
||||
if (gQueryParameters.isMember("URL")) {
|
||||
if (gQueryParameters.find("URL") != gQueryParameters.end()) {
|
||||
gIncludeURLCheck =
|
||||
gtk_check_button_new_with_label(gStrings[ST_CHECKURL].c_str());
|
||||
gtk_box_pack_start(GTK_BOX(innerVBox), gIncludeURLCheck, FALSE, FALSE, 0);
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#include <Cocoa/Cocoa.h>
|
||||
#include "HTTPMultipartUpload.h"
|
||||
#include "crashreporter.h"
|
||||
#include "json/json.h"
|
||||
|
||||
// Defined below
|
||||
@class TextViewWithPlaceHolder;
|
||||
|
@ -47,7 +46,7 @@
|
|||
}
|
||||
|
||||
- (void)showCrashUI:(const StringTable&)files
|
||||
queryParameters:(const Json::Value&)queryParameters
|
||||
queryParameters:(const StringTable&)queryParameters
|
||||
sendURL:(const std::string&)sendURL;
|
||||
- (void)showErrorUI:(const std::string&)message;
|
||||
- (void)showReportInfo;
|
||||
|
|
|
@ -23,7 +23,7 @@ using namespace CrashReporter;
|
|||
static NSAutoreleasePool* gMainPool;
|
||||
static CrashReporterUI* gUI = 0;
|
||||
static StringTable gFiles;
|
||||
static Json::Value gQueryParameters;
|
||||
static StringTable gQueryParameters;
|
||||
static string gURLParameter;
|
||||
static string gSendURL;
|
||||
static vector<string> gRestartArgs;
|
||||
|
@ -92,7 +92,7 @@ static bool RestartApplication() {
|
|||
}
|
||||
|
||||
- (void)showCrashUI:(const StringTable&)files
|
||||
queryParameters:(const Json::Value&)queryParameters
|
||||
queryParameters:(const StringTable&)queryParameters
|
||||
sendURL:(const string&)sendURL {
|
||||
gFiles = files;
|
||||
gQueryParameters = queryParameters;
|
||||
|
@ -127,9 +127,9 @@ static bool RestartApplication() {
|
|||
if (gRTLlayout) [mCommentText toggleBaseWritingDirection:self];
|
||||
[[mEmailText cell] setPlaceholderString:Str(ST_EMAILGRAYTEXT)];
|
||||
|
||||
if (gQueryParameters.isMember("URL")) {
|
||||
if (gQueryParameters.find("URL") != gQueryParameters.end()) {
|
||||
// save the URL value in case the checkbox gets unchecked
|
||||
gURLParameter = gQueryParameters["URL"].asString();
|
||||
gURLParameter = gQueryParameters["URL"];
|
||||
} else {
|
||||
// no URL specified, hide checkbox
|
||||
[mIncludeURLButton removeFromSuperview];
|
||||
|
@ -197,20 +197,13 @@ static bool RestartApplication() {
|
|||
forKey:NSFontAttributeName];
|
||||
|
||||
[mViewReportTextView setString:@""];
|
||||
for (Json::ValueConstIterator iter = gQueryParameters.begin(); iter != gQueryParameters.end();
|
||||
++iter) {
|
||||
NSAttributedString* key = [[NSAttributedString alloc] initWithString:NSSTR(iter.name() + ": ")
|
||||
for (StringTable::iterator iter = gQueryParameters.begin(); iter != gQueryParameters.end();
|
||||
iter++) {
|
||||
NSAttributedString* key = [[NSAttributedString alloc] initWithString:NSSTR(iter->first + ": ")
|
||||
attributes:boldAttr];
|
||||
string str;
|
||||
if (iter->isString()) {
|
||||
str = iter->asString();
|
||||
} else {
|
||||
Json::StreamWriterBuilder builder;
|
||||
builder["indentation"] = "";
|
||||
str = writeString(builder, *iter);
|
||||
}
|
||||
NSAttributedString* value = [[NSAttributedString alloc] initWithString:NSSTR(str + "\n")
|
||||
attributes:normalAttr];
|
||||
NSAttributedString* value =
|
||||
[[NSAttributedString alloc] initWithString:NSSTR(iter->second + "\n")
|
||||
attributes:normalAttr];
|
||||
[[mViewReportTextView textStorage] appendAttributedString:key];
|
||||
[[mViewReportTextView textStorage] appendAttributedString:value];
|
||||
[key release];
|
||||
|
@ -291,7 +284,7 @@ static bool RestartApplication() {
|
|||
if ([[[mCommentText textStorage] mutableString] length] > 0)
|
||||
gQueryParameters["Comments"] = [[[mCommentText textStorage] mutableString] UTF8String];
|
||||
else
|
||||
gQueryParameters.removeMember("Comments");
|
||||
gQueryParameters.erase("Comments");
|
||||
}
|
||||
|
||||
// Limit the comment field to 500 bytes in UTF-8
|
||||
|
@ -466,7 +459,7 @@ static bool RestartApplication() {
|
|||
if ([mIncludeURLButton state] == NSOnState && !gURLParameter.empty()) {
|
||||
gQueryParameters["URL"] = gURLParameter;
|
||||
} else {
|
||||
gQueryParameters.removeMember("URL");
|
||||
gQueryParameters.erase("URL");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -476,7 +469,7 @@ static bool RestartApplication() {
|
|||
gQueryParameters["Email"] = [email UTF8String];
|
||||
[mEmailText setEnabled:YES];
|
||||
} else {
|
||||
gQueryParameters.removeMember("Email");
|
||||
gQueryParameters.erase("Email");
|
||||
[mEmailText setEnabled:NO];
|
||||
}
|
||||
}
|
||||
|
@ -506,15 +499,28 @@ static bool RestartApplication() {
|
|||
mPost = [[HTTPMultipartUpload alloc] initWithURL:url];
|
||||
if (!mPost) return false;
|
||||
|
||||
NSMutableDictionary* parameters =
|
||||
[[NSMutableDictionary alloc] initWithCapacity:gQueryParameters.size()];
|
||||
if (!parameters) return false;
|
||||
|
||||
StringTable::const_iterator end = gQueryParameters.end();
|
||||
for (StringTable::const_iterator i = gQueryParameters.begin(); i != end; i++) {
|
||||
NSString* key = NSSTR(i->first);
|
||||
NSString* value = NSSTR(i->second);
|
||||
if (key && value) {
|
||||
[parameters setObject:value forKey:key];
|
||||
} else {
|
||||
ostringstream message;
|
||||
message << "Warning: skipping annotation '" << i->first
|
||||
<< "' due to malformed UTF-8 encoding";
|
||||
LogMessage(message.str());
|
||||
}
|
||||
}
|
||||
|
||||
for (StringTable::const_iterator i = gFiles.begin(); i != gFiles.end(); i++) {
|
||||
[mPost addFileAtPath:NSSTR(i->second) name:NSSTR(i->first)];
|
||||
}
|
||||
|
||||
Json::StreamWriterBuilder builder;
|
||||
builder["indentation"] = "";
|
||||
string output = writeString(builder, gQueryParameters).append("\r\n");
|
||||
NSMutableString* parameters = [[NSMutableString alloc] initWithUTF8String:output.c_str()];
|
||||
|
||||
[mPost setParameters:parameters];
|
||||
[parameters release];
|
||||
|
||||
|
@ -688,7 +694,7 @@ void UIShowDefaultUI() {
|
|||
[NSApp run];
|
||||
}
|
||||
|
||||
bool UIShowCrashUI(const StringTable& files, const Json::Value& queryParameters,
|
||||
bool UIShowCrashUI(const StringTable& files, const StringTable& queryParameters,
|
||||
const string& sendURL, const vector<string>& restartArgs) {
|
||||
gRestartArgs = restartArgs;
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ using namespace CrashReporter;
|
|||
|
||||
typedef struct {
|
||||
HWND hDlg;
|
||||
Json::Value queryParameters;
|
||||
map<wstring, wstring> queryParameters;
|
||||
map<wstring, wstring> files;
|
||||
wstring sendURL;
|
||||
|
||||
|
@ -82,9 +82,9 @@ static SendThreadData gSendData = {
|
|||
0,
|
||||
};
|
||||
static vector<string> gRestartArgs;
|
||||
static Json::Value gQueryParameters;
|
||||
static map<wstring, wstring> gQueryParameters;
|
||||
static wstring gCrashReporterKey(L"Software\\Mozilla\\Crash Reporter");
|
||||
static string gURLParameter;
|
||||
static wstring gURLParameter;
|
||||
static int gCheckboxPadding = 6;
|
||||
static bool gRTLlayout = false;
|
||||
|
||||
|
@ -351,12 +351,9 @@ static DWORD WINAPI SendThreadProc(LPVOID param) {
|
|||
finishedOk = false;
|
||||
LogMessage("No server URL, not sending report");
|
||||
} else {
|
||||
Json::StreamWriterBuilder builder;
|
||||
builder["indentation"] = "";
|
||||
string parameters(Json::writeString(builder, td->queryParameters));
|
||||
google_breakpad::CrashReportSender sender(L"");
|
||||
finishedOk = (sender.SendCrashReport(td->sendURL, parameters, td->files,
|
||||
&td->serverResponse) ==
|
||||
finishedOk = (sender.SendCrashReport(td->sendURL, td->queryParameters,
|
||||
td->files, &td->serverResponse) ==
|
||||
google_breakpad::RESULT_SUCCEEDED);
|
||||
if (finishedOk) {
|
||||
LogMessage("Crash report submitted successfully");
|
||||
|
@ -490,19 +487,11 @@ static void RestartApplication() {
|
|||
static void ShowReportInfo(HWND hwndDlg) {
|
||||
wstring description;
|
||||
|
||||
for (Json::ValueConstIterator iter = gQueryParameters.begin();
|
||||
iter != gQueryParameters.end(); ++iter) {
|
||||
description += UTF8ToWide(iter.name());
|
||||
for (map<wstring, wstring>::const_iterator i = gQueryParameters.begin();
|
||||
i != gQueryParameters.end(); i++) {
|
||||
description += i->first;
|
||||
description += L": ";
|
||||
string value;
|
||||
if (iter->isString()) {
|
||||
value = iter->asString();
|
||||
} else {
|
||||
Json::StreamWriterBuilder builder;
|
||||
builder["indentation"] = "";
|
||||
value = Json::writeString(builder, *iter);
|
||||
}
|
||||
description += UTF8ToWide(value);
|
||||
description += i->second;
|
||||
description += L"\n";
|
||||
}
|
||||
|
||||
|
@ -514,9 +503,9 @@ static void ShowReportInfo(HWND hwndDlg) {
|
|||
|
||||
static void UpdateURL(HWND hwndDlg) {
|
||||
if (IsDlgButtonChecked(hwndDlg, IDC_INCLUDEURLCHECK)) {
|
||||
gQueryParameters["URL"] = gURLParameter;
|
||||
gQueryParameters[L"URL"] = gURLParameter;
|
||||
} else {
|
||||
gQueryParameters.removeMember("URL");
|
||||
gQueryParameters.erase(L"URL");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -525,11 +514,11 @@ static void UpdateEmail(HWND hwndDlg) {
|
|||
wchar_t email[MAX_EMAIL_LENGTH];
|
||||
GetDlgItemTextW(hwndDlg, IDC_EMAILTEXT, email,
|
||||
sizeof(email) / sizeof(email[0]));
|
||||
gQueryParameters["Email"] = WideToUTF8(email);
|
||||
gQueryParameters[L"Email"] = email;
|
||||
if (IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK))
|
||||
EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILTEXT), true);
|
||||
} else {
|
||||
gQueryParameters.removeMember("Email");
|
||||
gQueryParameters.erase(L"Email");
|
||||
EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILTEXT), false);
|
||||
}
|
||||
}
|
||||
|
@ -539,9 +528,9 @@ static void UpdateComment(HWND hwndDlg) {
|
|||
GetDlgItemTextW(hwndDlg, IDC_COMMENTTEXT, comment,
|
||||
sizeof(comment) / sizeof(comment[0]));
|
||||
if (wcslen(comment) > 0)
|
||||
gQueryParameters["Comments"] = WideToUTF8(comment);
|
||||
gQueryParameters[L"Comments"] = comment;
|
||||
else
|
||||
gQueryParameters.removeMember("Comments");
|
||||
gQueryParameters.erase(L"Comments");
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -981,7 +970,7 @@ static BOOL CALLBACK CrashReporterDialogProc(HWND hwndDlg, UINT message,
|
|||
SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, EM_REQUESTRESIZE, 0, 0);
|
||||
|
||||
// if no URL was given, hide the URL checkbox
|
||||
if (!gQueryParameters.isMember("URL")) {
|
||||
if (gQueryParameters.find(L"URL") == gQueryParameters.end()) {
|
||||
RECT urlCheckRect, emailCheckRect;
|
||||
GetWindowRect(GetDlgItem(hwndDlg, IDC_INCLUDEURLCHECK), &urlCheckRect);
|
||||
GetWindowRect(GetDlgItem(hwndDlg, IDC_EMAILMECHECK), &emailCheckRect);
|
||||
|
@ -1211,7 +1200,7 @@ static bool CanUseMainCrashReportServer() {
|
|||
!IsWindowsVersionOrGreater(5, 2, 0));
|
||||
}
|
||||
|
||||
bool UIShowCrashUI(const StringTable& files, const Json::Value& queryParameters,
|
||||
bool UIShowCrashUI(const StringTable& files, const StringTable& queryParameters,
|
||||
const string& sendURL, const vector<string>& restartArgs) {
|
||||
gSendData.hDlg = nullptr;
|
||||
gSendData.sendURL = UTF8ToWide(sendURL);
|
||||
|
@ -1228,21 +1217,21 @@ bool UIShowCrashUI(const StringTable& files, const Json::Value& queryParameters,
|
|||
gSendData.files[UTF8ToWide(i->first)] = UTF8ToWide(i->second);
|
||||
}
|
||||
|
||||
gQueryParameters = queryParameters;
|
||||
for (StringTable::const_iterator i = queryParameters.begin();
|
||||
i != queryParameters.end(); i++) {
|
||||
gQueryParameters[UTF8ToWide(i->first)] = UTF8ToWide(i->second);
|
||||
}
|
||||
|
||||
if (gQueryParameters.isMember("Vendor")) {
|
||||
if (gQueryParameters.find(L"Vendor") != gQueryParameters.end()) {
|
||||
gCrashReporterKey = L"Software\\";
|
||||
string vendor = gQueryParameters["Vendor"].asString();
|
||||
if (!vendor.empty()) {
|
||||
gCrashReporterKey += UTF8ToWide(vendor) + L"\\";
|
||||
if (!gQueryParameters[L"Vendor"].empty()) {
|
||||
gCrashReporterKey += gQueryParameters[L"Vendor"] + L"\\";
|
||||
}
|
||||
string productName = gQueryParameters["ProductName"].asString();
|
||||
gCrashReporterKey += UTF8ToWide(productName) + L"\\Crash Reporter";
|
||||
gCrashReporterKey += gQueryParameters[L"ProductName"] + L"\\Crash Reporter";
|
||||
}
|
||||
|
||||
if (gQueryParameters.isMember("URL")) {
|
||||
gURLParameter = gQueryParameters["URL"].asString();
|
||||
}
|
||||
if (gQueryParameters.find(L"URL") != gQueryParameters.end())
|
||||
gURLParameter = gQueryParameters[L"URL"];
|
||||
|
||||
gRestartArgs = restartArgs;
|
||||
|
||||
|
|
|
@ -115,16 +115,15 @@ const int kTelemetryVersion = 4;
|
|||
|
||||
// Create the payload.metadata node of the crash ping using fields extracted
|
||||
// from the .extra file
|
||||
static Json::Value CreateMetadataNode(const Json::Value& aExtra) {
|
||||
static Json::Value CreateMetadataNode(StringTable& strings) {
|
||||
Json::Value node;
|
||||
|
||||
for (Json::ValueConstIterator iter = aExtra.begin(); iter != aExtra.end();
|
||||
++iter) {
|
||||
for (auto line : strings) {
|
||||
Annotation annotation;
|
||||
|
||||
if (AnnotationFromString(annotation, iter.memberName())) {
|
||||
if (AnnotationFromString(annotation, line.first.c_str())) {
|
||||
if (IsAnnotationWhitelistedForPing(annotation)) {
|
||||
node[iter.memberName()] = *iter;
|
||||
node[line.first] = line.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -133,8 +132,7 @@ static Json::Value CreateMetadataNode(const Json::Value& aExtra) {
|
|||
}
|
||||
|
||||
// Create the payload node of the crash ping
|
||||
static Json::Value CreatePayloadNode(const Json::Value& aExtra,
|
||||
const string& aHash,
|
||||
static Json::Value CreatePayloadNode(StringTable& strings, const string& aHash,
|
||||
const string& aSessionId) {
|
||||
Json::Value payload;
|
||||
|
||||
|
@ -146,12 +144,18 @@ static Json::Value CreatePayloadNode(const Json::Value& aExtra,
|
|||
payload["crashId"] = GetDumpLocalID();
|
||||
payload["minidumpSha256Hash"] = aHash;
|
||||
payload["processType"] = "main"; // This is always a main crash
|
||||
if (aExtra.isMember("StackTraces")) {
|
||||
payload["stackTraces"] = aExtra["StackTraces"];
|
||||
|
||||
// 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(aExtra);
|
||||
payload["metadata"] = CreateMetadataNode(strings);
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
@ -182,10 +186,12 @@ static Json::Value CreateApplicationNode(
|
|||
}
|
||||
|
||||
// Create the root node of the crash ping
|
||||
static Json::Value CreateRootNode(
|
||||
const Json::Value& aExtra, const string& aUuid, const string& aHash,
|
||||
const string& aClientId, const string& aSessionId, const string& aName,
|
||||
const string& aVersion, const string& aChannel, const string& aBuildId) {
|
||||
static Json::Value CreateRootNode(StringTable& strings, const string& aUuid,
|
||||
const string& aHash, 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;
|
||||
|
@ -201,7 +207,7 @@ static Json::Value CreateRootNode(
|
|||
string displayVersion;
|
||||
string platformVersion;
|
||||
|
||||
if (reader.parse(aExtra["TelemetryEnvironment"].asString(), environment,
|
||||
if (reader.parse(strings["TelemetryEnvironment"], environment,
|
||||
/* collectComments */ false)) {
|
||||
if (environment.isMember("build") && environment["build"].isObject()) {
|
||||
Json::Value build = environment["build"];
|
||||
|
@ -224,10 +230,10 @@ static Json::Value CreateRootNode(
|
|||
root["environment"] = environment;
|
||||
}
|
||||
|
||||
root["payload"] = CreatePayloadNode(aExtra, aHash, aSessionId);
|
||||
root["payload"] = CreatePayloadNode(strings, aHash, aSessionId);
|
||||
root["application"] = CreateApplicationNode(
|
||||
aExtra["Vendor"].asString(), aName, aVersion, displayVersion,
|
||||
platformVersion, aChannel, aBuildId, architecture, xpcomAbi);
|
||||
strings["Vendor"], aName, aVersion, displayVersion, platformVersion,
|
||||
aChannel, aBuildId, architecture, xpcomAbi);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
@ -258,7 +264,7 @@ static bool WritePing(const string& aPath, const string& aPing) {
|
|||
return success;
|
||||
}
|
||||
|
||||
// Assembles the crash ping using the JSON data extracted from the .extra file
|
||||
// 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.
|
||||
|
@ -267,31 +273,22 @@ static bool WritePing(const string& aPath, const string& aPing) {
|
|||
// won't block waiting for the ping to be delivered.
|
||||
//
|
||||
// Returns true if the ping was assembled and handed over to the pingsender
|
||||
// correctly, also populates the aPingUuid parameter with the ping UUID. Returns
|
||||
// false otherwise and leaves the aPingUuid parameter unmodified.
|
||||
bool SendCrashPing(Json::Value& aExtra, const string& aHash, string& aPingUuid,
|
||||
// correctly, false otherwise and populates the aUUID field with the ping UUID.
|
||||
bool SendCrashPing(StringTable& strings, const string& aHash, string& pingUuid,
|
||||
const string& pingDir) {
|
||||
string clientId = strings[kTelemetryClientId];
|
||||
string serverUrl = strings[kTelemetryUrl];
|
||||
string sessionId = strings[kTelemetrySessionId];
|
||||
|
||||
// Remove the telemetry-related data from the crash annotations
|
||||
Json::Value value;
|
||||
if (!aExtra.removeMember(kTelemetryClientId, &value)) {
|
||||
return false;
|
||||
}
|
||||
string clientId = value.asString();
|
||||
strings.erase(kTelemetryClientId);
|
||||
strings.erase(kTelemetryUrl);
|
||||
strings.erase(kTelemetrySessionId);
|
||||
|
||||
if (!aExtra.removeMember(kTelemetryUrl, &value)) {
|
||||
return false;
|
||||
}
|
||||
string serverUrl = value.asString();
|
||||
|
||||
if (!aExtra.removeMember(kTelemetrySessionId, &value)) {
|
||||
return false;
|
||||
}
|
||||
string sessionId = value.asString();
|
||||
|
||||
string buildId = aExtra["BuildID"].asString();
|
||||
string channel = aExtra["ReleaseChannel"].asString();
|
||||
string name = aExtra["ProductName"].asString();
|
||||
string version = aExtra["Version"].asString();
|
||||
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);
|
||||
|
@ -300,7 +297,7 @@ bool SendCrashPing(Json::Value& aExtra, const string& aHash, string& aPingUuid,
|
|||
return false;
|
||||
}
|
||||
|
||||
Json::Value root = CreateRootNode(aExtra, uuid, aHash, clientId, sessionId,
|
||||
Json::Value root = CreateRootNode(strings, uuid, aHash, clientId, sessionId,
|
||||
name, version, channel, buildId);
|
||||
|
||||
// Write out the result to the pending pings directory
|
||||
|
@ -315,7 +312,7 @@ bool SendCrashPing(Json::Value& aExtra, const string& aHash, string& aPingUuid,
|
|||
// Hand over the ping to the sender
|
||||
vector<string> args = {url, pingPath};
|
||||
if (UIRunProgram(GetProgramPath(UI_PING_SENDER_FILENAME), args)) {
|
||||
aPingUuid = uuid;
|
||||
pingUuid = uuid;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
|
|
@ -55,7 +55,7 @@ static const char kUserAgent[] = "Breakpad/1.0 (Linux)";
|
|||
|
||||
// static
|
||||
bool HTTPUpload::SendRequest(const string &url,
|
||||
const string ¶meters,
|
||||
const map<string, string> ¶meters,
|
||||
const map<string, string> &files,
|
||||
const string &proxy,
|
||||
const string &proxy_user_pwd,
|
||||
|
@ -66,6 +66,9 @@ bool HTTPUpload::SendRequest(const string &url,
|
|||
if (response_code != NULL)
|
||||
*response_code = 0;
|
||||
|
||||
if (!CheckParameters(parameters))
|
||||
return false;
|
||||
|
||||
// We may have been linked statically; if curl_easy_init is in the
|
||||
// current binary, no need to search for a dynamic version.
|
||||
void* curl_lib = dlopen(NULL, RTLD_NOW);
|
||||
|
@ -130,14 +133,14 @@ bool HTTPUpload::SendRequest(const string &url,
|
|||
// Add form data.
|
||||
CURLFORMcode (*curl_formadd)(struct curl_httppost **, struct curl_httppost **, ...);
|
||||
*(void**) (&curl_formadd) = dlsym(curl_lib, "curl_formadd");
|
||||
(*curl_formadd)(&formpost, &lastptr, CURLFORM_COPYNAME, "extra",
|
||||
CURLFORM_BUFFER, "extra.json", CURLFORM_BUFFERPTR,
|
||||
parameters.c_str(), CURLFORM_BUFFERLENGTH,
|
||||
parameters.length(), CURLFORM_CONTENTTYPE, "application/json",
|
||||
CURLFORM_END);
|
||||
map<string, string>::const_iterator iter = parameters.begin();
|
||||
for (; iter != parameters.end(); ++iter)
|
||||
(*curl_formadd)(&formpost, &lastptr,
|
||||
CURLFORM_COPYNAME, iter->first.c_str(),
|
||||
CURLFORM_COPYCONTENTS, iter->second.c_str(),
|
||||
CURLFORM_END);
|
||||
|
||||
// Add form files.
|
||||
map<string, string>::const_iterator iter = files.begin();
|
||||
for (iter = files.begin(); iter != files.end(); ++iter) {
|
||||
(*curl_formadd)(&formpost, &lastptr,
|
||||
CURLFORM_COPYNAME, iter->first.c_str(),
|
||||
|
@ -207,4 +210,21 @@ bool HTTPUpload::CheckCurlLib(void* curl_lib) {
|
|||
dlsym(curl_lib, "curl_easy_setopt");
|
||||
}
|
||||
|
||||
// static
|
||||
bool HTTPUpload::CheckParameters(const map<string, string> ¶meters) {
|
||||
for (map<string, string>::const_iterator pos = parameters.begin();
|
||||
pos != parameters.end(); ++pos) {
|
||||
const string &str = pos->first;
|
||||
if (str.size() == 0)
|
||||
return false; // disallow empty parameter names
|
||||
for (unsigned int i = 0; i < str.size(); ++i) {
|
||||
int c = str[i];
|
||||
if (c < 32 || c == '"' || c > 127) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
// HTTPUpload provides a "nice" API to send a multipart HTTP(S) POST
|
||||
// request using libcurl. It currently supports requests that contain
|
||||
// parameters encoded in a JSON string, and a file to upload.
|
||||
// a set of string parameters (key/value pairs), and a file to upload.
|
||||
|
||||
#ifndef COMMON_LINUX_HTTP_UPLOAD_H__
|
||||
#define COMMON_LINUX_HTTP_UPLOAD_H__
|
||||
|
@ -49,7 +49,8 @@ class HTTPUpload {
|
|||
// request to the given URL.
|
||||
// Each key in |files| is the name of the file part of the request
|
||||
// (i.e. it corresponds to the name= attribute on an <input type="file">.
|
||||
// Parameters are specified as a JSON-encoded string in |parameters|.
|
||||
// Parameter names must contain only printable ASCII characters,
|
||||
// and may not contain a quote (") character.
|
||||
// Only HTTP(S) URLs are currently supported. Returns true on success.
|
||||
// If the request is successful and response_body is non-NULL,
|
||||
// the response body will be returned in response_body.
|
||||
|
@ -58,7 +59,7 @@ class HTTPUpload {
|
|||
// If the send fails, a description of the error will be
|
||||
// returned in error_description.
|
||||
static bool SendRequest(const string &url,
|
||||
const string ¶meters,
|
||||
const map<string, string> ¶meters,
|
||||
const map<string, string> &files,
|
||||
const string &proxy,
|
||||
const string &proxy_user_pwd,
|
||||
|
@ -68,6 +69,11 @@ class HTTPUpload {
|
|||
string *error_description);
|
||||
|
||||
private:
|
||||
// Checks that the given list of parameters has only printable
|
||||
// ASCII characters in the parameter name, and does not contain
|
||||
// any quote (") characters. Returns true if so.
|
||||
static bool CheckParameters(const map<string, string> ¶meters);
|
||||
|
||||
// Checks the curl_lib parameter points to a valid curl lib.
|
||||
static bool CheckCurlLib(void* curl_lib);
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
@interface HTTPMultipartUpload : NSObject {
|
||||
@protected
|
||||
NSURL *url_; // The destination URL (STRONG)
|
||||
NSMutableString *parameters_; // The JSON payload for sending data (STRONG)
|
||||
NSDictionary *parameters_; // The key/value pairs for sending data (STRONG)
|
||||
NSMutableDictionary *files_; // Dictionary of name/file-path (STRONG)
|
||||
NSString *boundary_; // The boundary string (STRONG)
|
||||
NSHTTPURLResponse *response_; // The response from the send (STRONG)
|
||||
|
@ -47,8 +47,8 @@
|
|||
|
||||
- (NSURL *)URL;
|
||||
|
||||
- (void)setParameters:(NSMutableString *)parameters;
|
||||
- (NSMutableString *)parameters;
|
||||
- (void)setParameters:(NSDictionary *)parameters;
|
||||
- (NSDictionary *)parameters;
|
||||
|
||||
- (void)addFileAtPath:(NSString *)path name:(NSString *)name;
|
||||
- (void)addFileContents:(NSData *)data name:(NSString *)name;
|
||||
|
|
|
@ -93,7 +93,7 @@ static NSData *SendSynchronousNSURLRequest(NSURLRequest *req,
|
|||
- (NSString *)multipartBoundary;
|
||||
// Each of the following methods will append the starting multipart boundary,
|
||||
// but not the ending one.
|
||||
- (NSData *)formDataForJSON:(NSString *)json;
|
||||
- (NSData *)formDataForKey:(NSString *)key value:(NSString *)value;
|
||||
- (NSData *)formDataForFileContents:(NSData *)contents name:(NSString *)name;
|
||||
- (NSData *)formDataForFile:(NSString *)file name:(NSString *)name;
|
||||
@end
|
||||
|
@ -110,16 +110,13 @@ static NSData *SendSynchronousNSURLRequest(NSURLRequest *req,
|
|||
}
|
||||
|
||||
//=============================================================================
|
||||
- (NSData *)formDataForJSON:(NSString *)json {
|
||||
NSMutableData *data = [NSMutableData data];
|
||||
NSString *fmt = @"--%@\r\nContent-Disposition: form-data; name=\"extra\"; "
|
||||
"filename=\"extra.json\"\r\nContent-Type: application/json\r\n\r\n";
|
||||
NSString *form = [NSString stringWithFormat:fmt, boundary_];
|
||||
- (NSData *)formDataForKey:(NSString *)key value:(NSString *)value {
|
||||
NSString *escaped = PercentEncodeNSString(key);
|
||||
NSString *fmt =
|
||||
@"--%@\r\nContent-Disposition: form-data; name=\"%@\"\r\n\r\n%@\r\n";
|
||||
NSString *form = [NSString stringWithFormat:fmt, boundary_, escaped, value];
|
||||
|
||||
[data appendData:[form dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
[data appendData:[json dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
|
||||
return data;
|
||||
return [form dataUsingEncoding:NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
@ -174,15 +171,15 @@ static NSData *SendSynchronousNSURLRequest(NSURLRequest *req,
|
|||
}
|
||||
|
||||
//=============================================================================
|
||||
- (void)setParameters:(NSMutableString *)parameters {
|
||||
- (void)setParameters:(NSDictionary *)parameters {
|
||||
if (parameters != parameters_) {
|
||||
[parameters_ release];
|
||||
parameters_ = [parameters mutableCopy];
|
||||
parameters_ = [parameters copy];
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
- (NSMutableString *)parameters {
|
||||
- (NSDictionary *)parameters {
|
||||
return parameters_;
|
||||
}
|
||||
|
||||
|
@ -213,8 +210,16 @@ static NSData *SendSynchronousNSURLRequest(NSURLRequest *req,
|
|||
[req setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@",
|
||||
boundary_] forHTTPHeaderField:@"Content-type"];
|
||||
|
||||
// Add JSON parameters to the message
|
||||
[postBody appendData:[self formDataForJSON:parameters_]];
|
||||
// Add any parameters to the message
|
||||
NSArray *parameterKeys = [parameters_ allKeys];
|
||||
NSString *key;
|
||||
|
||||
NSInteger count = [parameterKeys count];
|
||||
for (NSInteger i = 0; i < count; ++i) {
|
||||
key = [parameterKeys objectAtIndex:i];
|
||||
[postBody appendData:[self formDataForKey:key
|
||||
value:[parameters_ objectForKey:key]]];
|
||||
}
|
||||
|
||||
// Add any files to the message
|
||||
NSArray *fileNames = [files_ allKeys];
|
||||
|
|
|
@ -63,7 +63,7 @@ class HTTPUpload::AutoInternetHandle {
|
|||
|
||||
// static
|
||||
bool HTTPUpload::SendRequest(const wstring &url,
|
||||
const string ¶meters,
|
||||
const map<wstring, wstring> ¶meters,
|
||||
const map<wstring, wstring> &files,
|
||||
int *timeout,
|
||||
wstring *response_body,
|
||||
|
@ -72,6 +72,11 @@ bool HTTPUpload::SendRequest(const wstring &url,
|
|||
*response_code = 0;
|
||||
}
|
||||
|
||||
// TODO(bryner): support non-ASCII parameter names
|
||||
if (!CheckParameters(parameters)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Break up the URL and make sure we can handle it
|
||||
wchar_t scheme[16], host[256], path[256];
|
||||
URL_COMPONENTS components;
|
||||
|
@ -260,7 +265,7 @@ wstring HTTPUpload::GenerateRequestHeader(const wstring &boundary) {
|
|||
}
|
||||
|
||||
// static
|
||||
bool HTTPUpload::GenerateRequestBody(const string ¶meters,
|
||||
bool HTTPUpload::GenerateRequestBody(const map<wstring, wstring> ¶meters,
|
||||
const map<wstring, wstring> &files,
|
||||
const wstring &boundary,
|
||||
string *request_body) {
|
||||
|
@ -271,19 +276,14 @@ bool HTTPUpload::GenerateRequestBody(const string ¶meters,
|
|||
|
||||
request_body->clear();
|
||||
|
||||
// Append the extra data as a single JSON form entry
|
||||
request_body->append("--" + boundary_str + "\r\n");
|
||||
request_body->append(
|
||||
"Content-Disposition: form-data; "
|
||||
"name=\"extra\"; "
|
||||
"filename=\"extra.json\"\r\n");
|
||||
request_body->append("Content-Type: application/json\r\n");
|
||||
request_body->append("\r\n");
|
||||
|
||||
if (!parameters.empty()) {
|
||||
request_body->append(parameters);
|
||||
// Append each of the parameter pairs as a form-data part
|
||||
for (map<wstring, wstring>::const_iterator pos = parameters.begin();
|
||||
pos != parameters.end(); ++pos) {
|
||||
request_body->append("--" + boundary_str + "\r\n");
|
||||
request_body->append("Content-Disposition: form-data; name=\"" +
|
||||
WideToUTF8(pos->first) + "\"\r\n\r\n" +
|
||||
WideToUTF8(pos->second) + "\r\n");
|
||||
}
|
||||
request_body->append("\r\n");
|
||||
|
||||
for (map<wstring, wstring>::const_iterator pos = files.begin();
|
||||
pos != files.end(); ++pos) {
|
||||
|
@ -399,4 +399,22 @@ string HTTPUpload::WideToMBCP(const wstring &wide, unsigned int cp) {
|
|||
return result;
|
||||
}
|
||||
|
||||
// static
|
||||
bool HTTPUpload::CheckParameters(const map<wstring, wstring> ¶meters) {
|
||||
for (map<wstring, wstring>::const_iterator pos = parameters.begin();
|
||||
pos != parameters.end(); ++pos) {
|
||||
const wstring &str = pos->first;
|
||||
if (str.size() == 0) {
|
||||
return false; // disallow empty parameter names
|
||||
}
|
||||
for (unsigned int i = 0; i < str.size(); ++i) {
|
||||
wchar_t c = str[i];
|
||||
if (c < 32 || c == '"' || c > 127) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
// HTTPUpload provides a "nice" API to send a multipart HTTP(S) POST
|
||||
// request using wininet. It currently supports requests that contain
|
||||
// parameters encoded in a JSON string, and a file to upload.
|
||||
// a set of string parameters (key/value pairs), and a file to upload.
|
||||
|
||||
#ifndef COMMON_WINDOWS_HTTP_UPLOAD_H_
|
||||
#define COMMON_WINDOWS_HTTP_UPLOAD_H_
|
||||
|
@ -47,10 +47,10 @@
|
|||
|
||||
namespace google_breakpad {
|
||||
|
||||
using std::map;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using std::wstring;
|
||||
using std::map;
|
||||
using std::vector;
|
||||
|
||||
class HTTPUpload {
|
||||
public:
|
||||
|
@ -58,14 +58,15 @@ class HTTPUpload {
|
|||
// request to the given URL.
|
||||
// Each key in |files| is the name of the file part of the request
|
||||
// (i.e. it corresponds to the name= attribute on an <input type="file">.
|
||||
// Parameters are specified as a JSON-encoded string in |parameters|.
|
||||
// Parameter names must contain only printable ASCII characters,
|
||||
// and may not contain a quote (") character.
|
||||
// Only HTTP(S) URLs are currently supported. Returns true on success.
|
||||
// If the request is successful and response_body is non-NULL,
|
||||
// the response body will be returned in response_body.
|
||||
// If response_code is non-NULL, it will be set to the HTTP response code
|
||||
// received (or 0 if the request failed before getting an HTTP response).
|
||||
static bool SendRequest(const wstring &url,
|
||||
const string ¶meters,
|
||||
const map<wstring, wstring> ¶meters,
|
||||
const map<wstring, wstring> &files,
|
||||
int *timeout,
|
||||
wstring *response_body,
|
||||
|
@ -86,10 +87,10 @@ class HTTPUpload {
|
|||
// Generates a HTTP request header for a multipart form submit.
|
||||
static wstring GenerateRequestHeader(const wstring &boundary);
|
||||
|
||||
// Given a parameter string, a set of upload files, and a file part name,
|
||||
// Given a set of parameters, a set of upload files, and a file part name,
|
||||
// generates a multipart request body string with these parameters
|
||||
// and minidump contents. Returns true on success.
|
||||
static bool GenerateRequestBody(const string ¶meters,
|
||||
static bool GenerateRequestBody(const map<wstring, wstring> ¶meters,
|
||||
const map<wstring, wstring> &files,
|
||||
const wstring &boundary,
|
||||
string *request_body);
|
||||
|
@ -108,6 +109,11 @@ class HTTPUpload {
|
|||
// Converts a UTF16 string to specified code page.
|
||||
static string WideToMBCP(const wstring &wide, unsigned int cp);
|
||||
|
||||
// Checks that the given list of parameters has only printable
|
||||
// ASCII characters in the parameter name, and does not contain
|
||||
// any quote (") characters. Returns true if so.
|
||||
static bool CheckParameters(const map<wstring, wstring> ¶meters);
|
||||
|
||||
// No instances of this class should be created.
|
||||
// Disallow all constructors, destructors, and operator=.
|
||||
HTTPUpload();
|
||||
|
|
|
@ -71,7 +71,6 @@ using google_breakpad::ProcessResult;
|
|||
using google_breakpad::ProcessState;
|
||||
using google_breakpad::StackFrame;
|
||||
|
||||
using mozilla::IFStream;
|
||||
using mozilla::OFStream;
|
||||
using mozilla::Unused;
|
||||
|
||||
|
@ -361,22 +360,6 @@ static bool ProcessMinidump(Json::Value& aStackTraces,
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool ReadExtraFile(const string& aExtraDataPath, Json::Value& aExtra) {
|
||||
IFStream f(
|
||||
#if defined(XP_WIN)
|
||||
UTF8ToWide(aExtraDataPath).c_str(),
|
||||
#else
|
||||
aExtraDataPath.c_str(),
|
||||
#endif // defined(XP_WIN)
|
||||
ios::in);
|
||||
if (!f.is_open()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Json::CharReaderBuilder builder;
|
||||
return parseFromStream(builder, f, &aExtra, nullptr);
|
||||
}
|
||||
|
||||
// Update the extra data file by adding the StackTraces and ModuleSignatureInfo
|
||||
// fields that contain the JSON outputs of this program.
|
||||
static bool UpdateExtraDataFile(const string& aDumpPath,
|
||||
|
@ -390,33 +373,29 @@ static bool UpdateExtraDataFile(const string& aDumpPath,
|
|||
}
|
||||
|
||||
extraDataPath.replace(dot, extraDataPath.length() - dot, kExtraDataExtension);
|
||||
bool res = false;
|
||||
|
||||
Json::Value extra;
|
||||
if (!ReadExtraFile(extraDataPath, extra)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We want to open the extra file in append mode.
|
||||
ios_base::openmode mode = ios::out | ios::app;
|
||||
OFStream f(
|
||||
#if defined(XP_WIN)
|
||||
UTF8ToWide(extraDataPath).c_str(),
|
||||
#else
|
||||
extraDataPath.c_str(),
|
||||
#endif // defined(XP_WIN)
|
||||
ios::out | ios::trunc);
|
||||
mode);
|
||||
|
||||
bool res = false;
|
||||
if (f.is_open()) {
|
||||
extra["StackTraces"] = aStackTraces;
|
||||
Json::FastWriter writer;
|
||||
|
||||
f << "StackTraces=" << writer.write(aStackTraces);
|
||||
res = !f.fail();
|
||||
|
||||
if (!!aCertSubjects) {
|
||||
extra["ModuleSignatureInfo"] = aCertSubjects;
|
||||
f << "ModuleSignatureInfo=" << writer.write(aCertSubjects);
|
||||
res &= !f.fail();
|
||||
}
|
||||
|
||||
Json::StreamWriterBuilder builder;
|
||||
builder["indentation"] = "";
|
||||
std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
|
||||
writer->write(extra, &f);
|
||||
f << "\n";
|
||||
res = !f.fail();
|
||||
f.close();
|
||||
}
|
||||
|
||||
|
|
|
@ -92,6 +92,7 @@ if CONFIG['MOZ_CRASHREPORTER']:
|
|||
EXTRA_JS_MODULES += [
|
||||
'CrashReports.jsm',
|
||||
'CrashSubmit.jsm',
|
||||
'KeyValueParser.jsm',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
|
|
@ -181,7 +181,7 @@ static const XP_CHAR extraFileExtension[] = XP_TEXT(".extra");
|
|||
static const XP_CHAR memoryReportExtension[] = XP_TEXT(".memory.json.gz");
|
||||
static xpstring* defaultMemoryReportPath = nullptr;
|
||||
|
||||
static const char kCrashMainID[] = "crash.main.3\n";
|
||||
static const char kCrashMainID[] = "crash.main.2\n";
|
||||
|
||||
static google_breakpad::ExceptionHandler* gExceptionHandler = nullptr;
|
||||
|
||||
|
@ -527,133 +527,128 @@ bool copy_file(const char* from, const char* to) {
|
|||
/**
|
||||
* The PlatformWriter class provides a tool to create and write to a file that
|
||||
* is safe to call from within an exception handler. To use it this way the
|
||||
* file path needs to be provided as a bare C string.
|
||||
* file path needs to be provided as a bare C string. If the writer is created
|
||||
* using an nsIFile instance it will *not* be safe to use from a crashed
|
||||
* context.
|
||||
*/
|
||||
#ifdef XP_WIN
|
||||
|
||||
class PlatformWriter {
|
||||
public:
|
||||
#ifdef XP_WIN
|
||||
typedef HANDLE NativeFileDesc;
|
||||
typedef wchar_t NativeChar;
|
||||
#elif defined(XP_UNIX)
|
||||
typedef int NativeFileDesc;
|
||||
typedef char NativeChar;
|
||||
#else
|
||||
# error "Need implementation of PlatformWriter for this platform"
|
||||
#endif
|
||||
PlatformWriter() : mHandle(INVALID_HANDLE_VALUE) {}
|
||||
|
||||
const NativeFileDesc kInvalidFileDesc =
|
||||
#ifdef XP_WIN
|
||||
INVALID_HANDLE_VALUE;
|
||||
#elif defined(XP_UNIX)
|
||||
-1;
|
||||
#endif
|
||||
explicit PlatformWriter(const wchar_t* path) : PlatformWriter() {
|
||||
Open(path);
|
||||
}
|
||||
|
||||
PlatformWriter() : mFD(kInvalidFileDesc) {}
|
||||
explicit PlatformWriter(const NativeChar* aPath) : PlatformWriter() {
|
||||
Open(aPath);
|
||||
explicit PlatformWriter(nsIFile* file) : PlatformWriter() {
|
||||
nsAutoString path;
|
||||
if (NS_SUCCEEDED(file->GetPath(path))) {
|
||||
Open(path.get());
|
||||
}
|
||||
}
|
||||
|
||||
~PlatformWriter() {
|
||||
if (Valid()) {
|
||||
#ifdef XP_WIN
|
||||
CloseHandle(mFD);
|
||||
#elif defined(XP_UNIX)
|
||||
sys_close(mFD);
|
||||
#endif
|
||||
CloseHandle(mHandle);
|
||||
}
|
||||
}
|
||||
|
||||
void Open(const NativeChar* aPath) {
|
||||
#ifdef XP_WIN
|
||||
mFD = CreateFile(aPath, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS,
|
||||
FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
#elif defined(XP_UNIX)
|
||||
mFD = sys_open(aPath, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
||||
#endif
|
||||
void Open(const wchar_t* path) {
|
||||
mHandle = CreateFile(path, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS,
|
||||
FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
}
|
||||
|
||||
void OpenHandle(NativeFileDesc aFD) { mFD = aFD; }
|
||||
bool Valid() { return mFD != kInvalidFileDesc; }
|
||||
void OpenHandle(HANDLE aHandle) { mHandle = aHandle; }
|
||||
|
||||
void WriteBuffer(const char* aBuffer, size_t aLen) {
|
||||
bool Valid() { return mHandle != INVALID_HANDLE_VALUE; }
|
||||
|
||||
void WriteBuffer(const char* buffer, size_t len) {
|
||||
if (!Valid()) {
|
||||
return;
|
||||
}
|
||||
#ifdef XP_WIN
|
||||
DWORD nBytes;
|
||||
WriteFile(mFD, aBuffer, aLen, &nBytes, nullptr);
|
||||
#elif defined(XP_UNIX)
|
||||
mozilla::Unused << sys_write(mFD, aBuffer, aLen);
|
||||
#endif
|
||||
WriteFile(mHandle, buffer, len, &nBytes, nullptr);
|
||||
}
|
||||
|
||||
void WriteString(const char* aStr) { WriteBuffer(aStr, my_strlen(aStr)); }
|
||||
|
||||
template <int N>
|
||||
void WriteLiteral(const char (&aStr)[N]) {
|
||||
WriteBuffer(aStr, N - 1);
|
||||
}
|
||||
|
||||
NativeFileDesc FileDesc() { return mFD; }
|
||||
HANDLE Handle() { return mHandle; }
|
||||
|
||||
private:
|
||||
NativeFileDesc mFD;
|
||||
HANDLE mHandle;
|
||||
};
|
||||
|
||||
class JSONAnnotationWriter : public AnnotationWriter {
|
||||
#elif defined(XP_UNIX)
|
||||
|
||||
class PlatformWriter {
|
||||
public:
|
||||
explicit JSONAnnotationWriter(PlatformWriter& aPlatformWriter)
|
||||
: mWriter(aPlatformWriter), mEmpty(true) {
|
||||
mWriter.WriteBuffer("{", 1);
|
||||
PlatformWriter() : mFD(-1) {}
|
||||
|
||||
explicit PlatformWriter(const char* path) : PlatformWriter() { Open(path); }
|
||||
|
||||
explicit PlatformWriter(nsIFile* file) : PlatformWriter() {
|
||||
nsAutoCString path;
|
||||
if (NS_SUCCEEDED(file->GetNativePath(path))) {
|
||||
Open(path.get());
|
||||
}
|
||||
}
|
||||
|
||||
~JSONAnnotationWriter() { mWriter.WriteBuffer("}", 1); }
|
||||
~PlatformWriter() {
|
||||
if (Valid()) {
|
||||
sys_close(mFD);
|
||||
}
|
||||
}
|
||||
|
||||
void Write(Annotation aAnnotation, const char* aValue,
|
||||
size_t aLen = 0) override {
|
||||
size_t len = aLen ? aLen : my_strlen(aValue);
|
||||
const char* annotationStr = AnnotationToString(aAnnotation);
|
||||
void Open(const char* path) {
|
||||
mFD = sys_open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
||||
}
|
||||
|
||||
WritePrefix();
|
||||
mWriter.WriteBuffer(annotationStr, my_strlen(annotationStr));
|
||||
WriteSeparator();
|
||||
WriteEscapedString(aValue, len);
|
||||
WriteSuffix();
|
||||
void OpenHandle(int aFd) { mFD = aFd; }
|
||||
|
||||
bool Valid() { return mFD != -1; }
|
||||
|
||||
void WriteBuffer(const char* buffer, size_t len) {
|
||||
if (!Valid()) {
|
||||
return;
|
||||
}
|
||||
Unused << sys_write(mFD, buffer, len);
|
||||
}
|
||||
|
||||
private:
|
||||
int mFD;
|
||||
};
|
||||
|
||||
#else
|
||||
# error "Need implementation of PlatformWrite for this platform"
|
||||
#endif
|
||||
|
||||
template <int N>
|
||||
static void WriteLiteral(PlatformWriter& pw, const char (&str)[N]) {
|
||||
pw.WriteBuffer(str, N - 1);
|
||||
}
|
||||
|
||||
static void WriteString(PlatformWriter& pw, const char* str) {
|
||||
pw.WriteBuffer(str, my_strlen(str));
|
||||
}
|
||||
|
||||
class AnnotationWriter {
|
||||
public:
|
||||
virtual void Write(Annotation aAnnotation, const char* aValue) = 0;
|
||||
};
|
||||
|
||||
class INIAnnotationWriter : public AnnotationWriter {
|
||||
public:
|
||||
explicit INIAnnotationWriter(PlatformWriter& aPlatformWriter)
|
||||
: mPlatformWriter(aPlatformWriter) {}
|
||||
|
||||
void Write(Annotation aAnnotation, const char* aValue) override {
|
||||
WriteString(mPlatformWriter, AnnotationToString(aAnnotation));
|
||||
WriteLiteral(mPlatformWriter, "=");
|
||||
WriteString(mPlatformWriter, aValue);
|
||||
WriteLiteral(mPlatformWriter, "\n");
|
||||
};
|
||||
|
||||
private:
|
||||
void WritePrefix() {
|
||||
if (mEmpty) {
|
||||
mWriter.WriteBuffer("\"", 1);
|
||||
mEmpty = false;
|
||||
} else {
|
||||
mWriter.WriteBuffer(",\"", 2);
|
||||
}
|
||||
}
|
||||
|
||||
void WriteSeparator() { mWriter.WriteBuffer("\":\"", 3); }
|
||||
void WriteSuffix() { mWriter.WriteBuffer("\"", 1); }
|
||||
void WriteEscapedString(const char* aStr, size_t aLen) {
|
||||
for (size_t i = 0; i < aLen; i++) {
|
||||
uint8_t c = aStr[i];
|
||||
if (c <= 0x1f || c == '\\' || c == '\"') {
|
||||
mWriter.WriteBuffer("\\u00", 4);
|
||||
WriteHexDigitAsAsciiChar((c & 0x00f0) >> 4);
|
||||
WriteHexDigitAsAsciiChar(c & 0x000f);
|
||||
} else {
|
||||
mWriter.WriteBuffer(aStr + i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WriteHexDigitAsAsciiChar(uint8_t u) {
|
||||
char buf[1];
|
||||
buf[0] = static_cast<unsigned>((u < 10) ? '0' + u : 'a' + (u - 10));
|
||||
mWriter.WriteBuffer(buf, 1);
|
||||
}
|
||||
|
||||
PlatformWriter mWriter;
|
||||
bool mEmpty;
|
||||
PlatformWriter& mPlatformWriter;
|
||||
};
|
||||
|
||||
class BinaryAnnotationWriter : public AnnotationWriter {
|
||||
|
@ -661,9 +656,8 @@ class BinaryAnnotationWriter : public AnnotationWriter {
|
|||
explicit BinaryAnnotationWriter(PlatformWriter& aPlatformWriter)
|
||||
: mPlatformWriter(aPlatformWriter) {}
|
||||
|
||||
void Write(Annotation aAnnotation, const char* aValue,
|
||||
size_t aLen = 0) override {
|
||||
uint64_t len = aLen ? aLen : my_strlen(aValue);
|
||||
void Write(Annotation aAnnotation, const char* aValue) override {
|
||||
uint64_t len = my_strlen(aValue);
|
||||
mPlatformWriter.WriteBuffer((const char*)&aAnnotation, sizeof(aAnnotation));
|
||||
mPlatformWriter.WriteBuffer((const char*)&len, sizeof(len));
|
||||
mPlatformWriter.WriteBuffer(aValue, len);
|
||||
|
@ -923,6 +917,32 @@ static bool LaunchCrashHandlerService(XP_CHAR* aProgramPath,
|
|||
|
||||
#endif
|
||||
|
||||
static void WriteEscapedMozCrashReason(PlatformWriter& aWriter) {
|
||||
if (gMozCrashReason == nullptr) {
|
||||
return; // No crash reason, bail out
|
||||
}
|
||||
|
||||
const char* reason = gMozCrashReason;
|
||||
size_t len = my_strlen(gMozCrashReason);
|
||||
|
||||
WriteString(aWriter, AnnotationToString(Annotation::MozCrashReason));
|
||||
WriteLiteral(aWriter, "=");
|
||||
|
||||
// The crash reason might not be escaped so escape it one character at a time
|
||||
// and write out the resulting string.
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (reason[i] == '\\') {
|
||||
WriteLiteral(aWriter, "\\\\");
|
||||
} else if (reason[i] == '\n') {
|
||||
WriteLiteral(aWriter, "\\n");
|
||||
} else {
|
||||
aWriter.WriteBuffer(reason + i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
WriteLiteral(aWriter, "\n");
|
||||
}
|
||||
|
||||
static void WriteMozCrashReason(AnnotationWriter& aWriter) {
|
||||
if (gMozCrashReason != nullptr) {
|
||||
aWriter.Write(Annotation::MozCrashReason, gMozCrashReason);
|
||||
|
@ -932,11 +952,11 @@ static void WriteMozCrashReason(AnnotationWriter& aWriter) {
|
|||
static void WriteAnnotationsForMainProcessCrash(PlatformWriter& pw,
|
||||
const phc::AddrInfo* addrInfo,
|
||||
time_t crashTime) {
|
||||
JSONAnnotationWriter writer(pw);
|
||||
INIAnnotationWriter writer(pw);
|
||||
for (auto key : MakeEnumeratedRange(Annotation::Count)) {
|
||||
const nsCString& value = crashReporterAPIData_Table[key];
|
||||
if (!value.IsEmpty()) {
|
||||
writer.Write(key, value.get(), value.Length());
|
||||
writer.Write(key, value.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -984,13 +1004,12 @@ static void WriteAnnotationsForMainProcessCrash(PlatformWriter& pw,
|
|||
}
|
||||
|
||||
# ifdef HAS_DLL_BLOCKLIST
|
||||
// HACK: The DLL blocklist code will manually write its annotations as JSON
|
||||
DllBlocklist_WriteNotes(writer);
|
||||
DllBlocklist_WriteNotes(pw.Handle());
|
||||
# endif
|
||||
#endif // XP_WIN
|
||||
|
||||
WriteMemoryStatus(writer);
|
||||
WriteMozCrashReason(writer);
|
||||
WriteEscapedMozCrashReason(pw);
|
||||
|
||||
char oomAllocationSizeBuffer[32] = "";
|
||||
if (gOOMAllocationSize) {
|
||||
|
@ -1060,11 +1079,11 @@ static void WriteCrashEventFile(time_t crashTime, const char* crashTimeString,
|
|||
#endif
|
||||
|
||||
eventFile.Open(crashEventPath);
|
||||
eventFile.WriteLiteral(kCrashMainID);
|
||||
eventFile.WriteString(crashTimeString);
|
||||
eventFile.WriteLiteral("\n");
|
||||
eventFile.WriteString(id_ascii);
|
||||
eventFile.WriteLiteral("\n");
|
||||
WriteLiteral(eventFile, kCrashMainID);
|
||||
WriteString(eventFile, crashTimeString);
|
||||
WriteLiteral(eventFile, "\n");
|
||||
WriteString(eventFile, id_ascii);
|
||||
WriteLiteral(eventFile, "\n");
|
||||
WriteAnnotationsForMainProcessCrash(eventFile, addrInfo, crashTime);
|
||||
}
|
||||
}
|
||||
|
@ -1135,7 +1154,7 @@ bool MinidumpCallback(
|
|||
// write crash time to file
|
||||
if (lastCrashTimeFilename[0] != 0) {
|
||||
PlatformWriter lastCrashFile(lastCrashTimeFilename);
|
||||
lastCrashFile.WriteString(crashTimeString);
|
||||
WriteString(lastCrashFile, crashTimeString);
|
||||
}
|
||||
|
||||
WriteCrashEventFile(crashTime, crashTimeString, addrInfo,
|
||||
|
@ -1285,7 +1304,8 @@ static void PrepareChildExceptionTimeAnnotations(
|
|||
apiData.OpenHandle(f);
|
||||
BinaryAnnotationWriter writer(apiData);
|
||||
|
||||
// ...and write out any annotations.
|
||||
// ...and write out any annotations. These must be escaped if necessary
|
||||
// (but don't call EscapeAnnotation here, because it touches the heap).
|
||||
WriteMemoryStatus(writer);
|
||||
|
||||
char oomAllocationSizeBuffer[32] = "";
|
||||
|
@ -1974,6 +1994,40 @@ nsresult UnsetExceptionHandler() {
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
static void ReplaceChar(nsCString& str, const nsACString& character,
|
||||
const nsACString& replacement) {
|
||||
nsCString::const_iterator iter, end;
|
||||
|
||||
str.BeginReading(iter);
|
||||
str.EndReading(end);
|
||||
|
||||
while (FindInReadable(character, iter, end)) {
|
||||
nsCString::const_iterator start;
|
||||
str.BeginReading(start);
|
||||
int32_t pos = end - start;
|
||||
str.Replace(pos - 1, 1, replacement);
|
||||
|
||||
str.BeginReading(iter);
|
||||
iter.advance(pos + replacement.Length() - 1);
|
||||
str.EndReading(end);
|
||||
}
|
||||
}
|
||||
|
||||
static nsresult EscapeAnnotation(const nsACString& data,
|
||||
nsCString& escapedData) {
|
||||
if (FindInReadable(NS_LITERAL_CSTRING("\0"), data))
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
|
||||
escapedData = data;
|
||||
|
||||
// escape backslashes
|
||||
ReplaceChar(escapedData, NS_LITERAL_CSTRING("\\"),
|
||||
NS_LITERAL_CSTRING("\\\\"));
|
||||
// escape newlines
|
||||
ReplaceChar(escapedData, NS_LITERAL_CSTRING("\n"), NS_LITERAL_CSTRING("\\n"));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
class DelayedNote {
|
||||
public:
|
||||
DelayedNote(Annotation aKey, const nsACString& aData)
|
||||
|
@ -2035,11 +2089,15 @@ nsresult AnnotateCrashReport(Annotation key, unsigned int data) {
|
|||
nsresult AnnotateCrashReport(Annotation key, const nsACString& data) {
|
||||
if (!GetEnabled()) return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
nsCString escapedData;
|
||||
nsresult rv = EscapeAnnotation(data, escapedData);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
if (!XRE_IsParentProcess()) {
|
||||
// The newer CrashReporterClient can be used from any thread.
|
||||
if (RefPtr<CrashReporterClient> client =
|
||||
CrashReporterClient::GetSingleton()) {
|
||||
client->AnnotateCrashReport(key, data);
|
||||
client->AnnotateCrashReport(key, escapedData);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -2051,7 +2109,8 @@ nsresult AnnotateCrashReport(Annotation key, const nsACString& data) {
|
|||
}
|
||||
|
||||
MutexAutoLock lock(*crashReporterAPILock);
|
||||
crashReporterAPIData_Table[key] = data;
|
||||
|
||||
crashReporterAPIData_Table[key] = escapedData;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -2118,10 +2177,20 @@ void SetMinidumpAnalysisAllThreads() {
|
|||
nsresult AppendAppNotesToCrashReport(const nsACString& data) {
|
||||
if (!GetEnabled()) return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (FindInReadable(NS_LITERAL_CSTRING("\0"), data))
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
|
||||
if (!XRE_IsParentProcess()) {
|
||||
// Since we don't go through AnnotateCrashReport in the parent process,
|
||||
// we must ensure that the data is escaped and valid before the parent
|
||||
// sees it.
|
||||
nsCString escapedData;
|
||||
nsresult rv = EscapeAnnotation(data, escapedData);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
if (RefPtr<CrashReporterClient> client =
|
||||
CrashReporterClient::GetSingleton()) {
|
||||
client->AppendAppNotes(data);
|
||||
client->AppendAppNotes(escapedData);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -2787,8 +2856,12 @@ static void ReadAndValidateExceptionTimeAnnotations(
|
|||
value.Append(c);
|
||||
} while (len > 0);
|
||||
|
||||
nsAutoCString escapedValue;
|
||||
nsresult rv = EscapeAnnotation(value, escapedValue);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
|
||||
// Looks good, save the (annotation, value) pair
|
||||
aAnnotations[static_cast<Annotation>(rawAnnotation)] = value;
|
||||
aAnnotations[static_cast<Annotation>(rawAnnotation)] = escapedValue;
|
||||
} while (res > 0);
|
||||
}
|
||||
|
||||
|
@ -2798,11 +2871,11 @@ static bool WriteExtraFile(PlatformWriter pw,
|
|||
return false;
|
||||
}
|
||||
|
||||
JSONAnnotationWriter writer(pw);
|
||||
INIAnnotationWriter writer(pw);
|
||||
for (auto key : MakeEnumeratedRange(Annotation::Count)) {
|
||||
const nsCString& value = aAnnotations[key];
|
||||
if (!value.IsEmpty()) {
|
||||
writer.Write(key, value.get(), value.Length());
|
||||
writer.Write(key, value.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2816,15 +2889,8 @@ bool WriteExtraFile(const nsAString& id, const AnnotationTable& annotations) {
|
|||
}
|
||||
|
||||
extra->Append(id + NS_LITERAL_STRING(".extra"));
|
||||
#ifdef XP_WIN
|
||||
nsAutoString path;
|
||||
NS_ENSURE_SUCCESS(extra->GetPath(path), false);
|
||||
#elif defined(XP_UNIX)
|
||||
nsAutoCString path;
|
||||
NS_ENSURE_SUCCESS(extra->GetNativePath(path), false);
|
||||
#endif
|
||||
|
||||
return WriteExtraFile(PlatformWriter(path.get()), annotations);
|
||||
return WriteExtraFile(PlatformWriter(extra), annotations);
|
||||
}
|
||||
|
||||
static void ReadExceptionTimeAnnotations(AnnotationTable& aAnnotations,
|
||||
|
|
|
@ -111,12 +111,7 @@ function parseMultipartForm(request)
|
|||
//TODO: handle non-ascii here?
|
||||
let name = b.substring(6, b.length - 1);
|
||||
//TODO: handle multiple-value properties?
|
||||
if (("Content-Type" in headers) &&
|
||||
(headers["Content-Type"] == "application/json")) {
|
||||
formData = Object.assign(formData, JSON.parse(part));
|
||||
} else {
|
||||
formData[name] = part;
|
||||
}
|
||||
formData[name] = part;
|
||||
}
|
||||
//TODO: handle filename= ?
|
||||
//TODO: handle multipart/mixed for multi-file uploads?
|
||||
|
@ -162,7 +157,7 @@ function handleRequest(request, response)
|
|||
.getService(Ci.nsIUUIDGenerator);
|
||||
let uuid = uuidGenerator.generateUUID().toString();
|
||||
// ditch the {}, add bp- prefix
|
||||
uuid = 'bp-' + uuid.substring(1,uuid.length-1);
|
||||
uuid = 'bp-' + uuid.substring(1,uuid.length-2);
|
||||
|
||||
let d = JSON.stringify(formData);
|
||||
//dump('saving crash report ' + uuid + ': ' + d + '\n');
|
||||
|
|
|
@ -140,7 +140,11 @@ function writeMinidumpFile(dir, uuid, date) {
|
|||
}
|
||||
|
||||
function writeExtraFile(dir, uuid, date, data) {
|
||||
writeCrashReportFile(dir, uuid, ".extra", date, JSON.stringify(data));
|
||||
let extradata = "";
|
||||
for (let x in data) {
|
||||
extradata += x + "=" + data[x] + "\n";
|
||||
}
|
||||
writeCrashReportFile(dir, uuid, ".extra", date, extradata);
|
||||
}
|
||||
|
||||
function writeMemoryReport(dir, uuid, date) {
|
||||
|
|
|
@ -9,12 +9,6 @@ function getEventDir() {
|
|||
return OS.Path.join(do_get_tempdir().path, "crash-events");
|
||||
}
|
||||
|
||||
function sendCommandAsync(command) {
|
||||
return new Promise(resolve => {
|
||||
sendCommand(command, resolve);
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Run an xpcshell subprocess and crash it.
|
||||
*
|
||||
|
@ -43,7 +37,7 @@ function sendCommandAsync(command) {
|
|||
* exit with an error.
|
||||
*
|
||||
*/
|
||||
async function do_crash(setup, callback, canReturnZero) {
|
||||
function do_crash(setup, callback, canReturnZero) {
|
||||
// get current process filename (xpcshell)
|
||||
let bin = Services.dirsvc.get("XREExeF", Ci.nsIFile);
|
||||
if (!bin.exists()) {
|
||||
|
@ -92,7 +86,7 @@ async function do_crash(setup, callback, canReturnZero) {
|
|||
Assert.notEqual(process.exitValue, 0);
|
||||
}
|
||||
|
||||
await handleMinidump(callback);
|
||||
handleMinidump(callback);
|
||||
}
|
||||
|
||||
function getMinidump() {
|
||||
|
@ -130,7 +124,7 @@ function runMinidumpAnalyzer(dumpFile, additionalArgs) {
|
|||
process.run(true /* blocking */, args, args.length);
|
||||
}
|
||||
|
||||
async function handleMinidump(callback) {
|
||||
function handleMinidump(callback) {
|
||||
// find minidump
|
||||
let minidump = getMinidump();
|
||||
|
||||
|
@ -144,33 +138,35 @@ async function handleMinidump(callback) {
|
|||
let memoryfile = minidump.clone();
|
||||
memoryfile.leafName = memoryfile.leafName.slice(0, -4) + ".memory.json.gz";
|
||||
|
||||
let cleanup = function() {
|
||||
[minidump, extrafile, memoryfile].forEach(file => {
|
||||
if (file.exists()) {
|
||||
file.remove(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Just in case, don't let these files linger.
|
||||
registerCleanupFunction(cleanup);
|
||||
registerCleanupFunction(function() {
|
||||
if (minidump.exists()) {
|
||||
minidump.remove(false);
|
||||
}
|
||||
if (extrafile.exists()) {
|
||||
extrafile.remove(false);
|
||||
}
|
||||
if (memoryfile.exists()) {
|
||||
memoryfile.remove(false);
|
||||
}
|
||||
});
|
||||
|
||||
Assert.ok(extrafile.exists());
|
||||
let data = await OS.File.read(extrafile.path);
|
||||
let decoder = new TextDecoder();
|
||||
let extra = JSON.parse(decoder.decode(data));
|
||||
let extra = parseKeyValuePairsFromFile(extrafile);
|
||||
|
||||
if (callback) {
|
||||
await callback(minidump, extra, extrafile);
|
||||
callback(minidump, extra, extrafile);
|
||||
}
|
||||
|
||||
cleanup();
|
||||
}
|
||||
|
||||
function spinEventLoop() {
|
||||
return new Promise(resolve => {
|
||||
executeSoon(resolve);
|
||||
});
|
||||
if (minidump.exists()) {
|
||||
minidump.remove(false);
|
||||
}
|
||||
if (extrafile.exists()) {
|
||||
extrafile.remove(false);
|
||||
}
|
||||
if (memoryfile.exists()) {
|
||||
memoryfile.remove(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -180,8 +176,9 @@ function spinEventLoop() {
|
|||
* to set data as needed _before_ the crash. The tail file triggers a generic
|
||||
* crash after setup.
|
||||
*/
|
||||
async function do_content_crash(setup, callback) {
|
||||
function do_content_crash(setup, callback) {
|
||||
do_load_child_test_harness();
|
||||
do_test_pending();
|
||||
|
||||
// Setting the minidump path won't work in the child, so we need to do
|
||||
// that here.
|
||||
|
@ -202,19 +199,28 @@ async function do_content_crash(setup, callback) {
|
|||
}
|
||||
}
|
||||
|
||||
let handleCrash = function() {
|
||||
let id = getMinidump().leafName.slice(0, -4);
|
||||
Services.crashmanager.ensureCrashIsPresent(id).then(() => {
|
||||
try {
|
||||
handleMinidump(callback);
|
||||
} catch (x) {
|
||||
do_report_unexpected_exception(x);
|
||||
}
|
||||
do_test_finished();
|
||||
});
|
||||
};
|
||||
|
||||
do_get_profile();
|
||||
await makeFakeAppDir();
|
||||
await sendCommandAsync('load("' + headfile.path.replace(/\\/g, "/") + '");');
|
||||
await sendCommandAsync(setup);
|
||||
await sendCommandAsync('load("' + tailfile.path.replace(/\\/g, "/") + '");');
|
||||
await spinEventLoop();
|
||||
let id = getMinidump().leafName.slice(0, -4);
|
||||
await Services.crashmanager.ensureCrashIsPresent(id);
|
||||
try {
|
||||
await handleMinidump(callback);
|
||||
} catch (x) {
|
||||
do_report_unexpected_exception(x);
|
||||
}
|
||||
makeFakeAppDir().then(() => {
|
||||
sendCommand('load("' + headfile.path.replace(/\\/g, "/") + '");', () =>
|
||||
sendCommand(setup, () =>
|
||||
sendCommand('load("' + tailfile.path.replace(/\\/g, "/") + '");', () =>
|
||||
executeSoon(handleCrash)
|
||||
)
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -223,8 +229,9 @@ async function do_content_crash(setup, callback) {
|
|||
* This variant accepts a trigger function which runs in the content process
|
||||
* and does something to _trigger_ the crash.
|
||||
*/
|
||||
async function do_triggered_content_crash(trigger, callback) {
|
||||
function do_triggered_content_crash(trigger, callback) {
|
||||
do_load_child_test_harness();
|
||||
do_test_pending();
|
||||
|
||||
// Setting the minidump path won't work in the child, so we need to do
|
||||
// that here.
|
||||
|
@ -243,21 +250,33 @@ async function do_triggered_content_crash(trigger, callback) {
|
|||
}
|
||||
}
|
||||
|
||||
let handleCrash = function() {
|
||||
let id = getMinidump().leafName.slice(0, -4);
|
||||
Services.crashmanager.ensureCrashIsPresent(id).then(() => {
|
||||
try {
|
||||
handleMinidump(callback);
|
||||
} catch (x) {
|
||||
do_report_unexpected_exception(x);
|
||||
}
|
||||
do_test_finished();
|
||||
});
|
||||
};
|
||||
|
||||
do_get_profile();
|
||||
await makeFakeAppDir();
|
||||
await sendCommandAsync('load("' + headfile.path.replace(/\\/g, "/") + '");');
|
||||
await sendCommandAsync(trigger);
|
||||
await spinEventLoop();
|
||||
let id = getMinidump().leafName.slice(0, -4);
|
||||
await Services.crashmanager.ensureCrashIsPresent(id);
|
||||
try {
|
||||
await handleMinidump(callback);
|
||||
} catch (x) {
|
||||
do_report_unexpected_exception(x);
|
||||
}
|
||||
makeFakeAppDir().then(() => {
|
||||
sendCommand('load("' + headfile.path.replace(/\\/g, "/") + '");', () =>
|
||||
sendCommand(trigger, () => executeSoon(handleCrash))
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Import binary APIs via js-ctypes.
|
||||
var { CrashTestUtils } = ChromeUtils.import(
|
||||
"resource://test/CrashTestUtils.jsm"
|
||||
);
|
||||
var {
|
||||
parseKeyValuePairs,
|
||||
parseKeyValuePairsFromFile,
|
||||
parseKeyValuePairsFromFileAsync,
|
||||
parseKeyValuePairsFromLines,
|
||||
} = ChromeUtils.import("resource://gre/modules/KeyValueParser.jsm");
|
||||
|
|
|
@ -190,20 +190,18 @@ function assertStack(stack, expected) {
|
|||
// "context", "scan", et al. May be null if you don't need to check the stack.
|
||||
// minidumpAnalyzerArgs: An array of additional arguments to pass to
|
||||
// minidump-analyzer.exe.
|
||||
async function do_x64CFITest(how, expectedStack, minidumpAnalyzerArgs) {
|
||||
function do_x64CFITest(how, expectedStack, minidumpAnalyzerArgs) {
|
||||
// Setup is run in the subprocess so we cannot use any closures.
|
||||
let setupFn = "crashType = CrashTestUtils." + how + ";";
|
||||
|
||||
let callbackFn = async function(minidumpFile, extra, extraFile) {
|
||||
let callbackFn = function(minidumpFile, extra, extraFile) {
|
||||
runMinidumpAnalyzer(minidumpFile, minidumpAnalyzerArgs);
|
||||
|
||||
// Refresh updated extra data
|
||||
let data = await OS.File.read(extraFile.path);
|
||||
let decoder = new TextDecoder();
|
||||
extra = JSON.parse(decoder.decode(data));
|
||||
extra = parseKeyValuePairsFromFile(extraFile);
|
||||
|
||||
initTestCrasherSymbols();
|
||||
let stackTraces = extra.StackTraces;
|
||||
let stackTraces = JSON.parse(extra.StackTraces);
|
||||
let crashingThreadIndex = stackTraces.crash_info.crashing_thread;
|
||||
gModules = stackTraces.modules;
|
||||
let crashingFrames = stackTraces.threads[crashingThreadIndex].frames;
|
||||
|
|
|
@ -105,8 +105,8 @@ function after_osfile_crash_exn(mdump, extra) {
|
|||
Assert.equal(state.latestSent[1], "read");
|
||||
}
|
||||
|
||||
add_task(async function run_test() {
|
||||
await do_crash(setup_crash, after_crash);
|
||||
await do_crash(setup_osfile_crash_noerror, after_osfile_crash_noerror);
|
||||
await do_crash(setup_osfile_crash_exn, after_osfile_crash_exn);
|
||||
});
|
||||
function run_test() {
|
||||
do_crash(setup_crash, after_crash);
|
||||
do_crash(setup_osfile_crash_noerror, after_osfile_crash_noerror);
|
||||
do_crash(setup_osfile_crash_exn, after_osfile_crash_exn);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
add_task(async function run_test() {
|
||||
function run_test() {
|
||||
// Try crashing with an abort().
|
||||
await do_crash(
|
||||
do_crash(
|
||||
function() {
|
||||
crashType = CrashTestUtils.CRASH_ABORT;
|
||||
crashReporter.annotateCrashReport("TestKey", "TestValue");
|
||||
|
@ -14,4 +14,4 @@ add_task(async function run_test() {
|
|||
// process will exit with a zero exit status
|
||||
true
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
add_task(async function run_test() {
|
||||
function run_test() {
|
||||
if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
|
||||
dump(
|
||||
"INFO | test_crash_after_js_large_allocation_failure.js | Can't test crashreporter in a non-libxul build.\n"
|
||||
|
@ -6,7 +6,7 @@ add_task(async function run_test() {
|
|||
return;
|
||||
}
|
||||
|
||||
await do_crash(
|
||||
do_crash(
|
||||
function() {
|
||||
crashType = CrashTestUtils.CRASH_MOZ_CRASH;
|
||||
crashReporter.annotateCrashReport("TestKey", "Yes");
|
||||
|
@ -20,4 +20,4 @@ add_task(async function run_test() {
|
|||
},
|
||||
true
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
add_task(async function run_test() {
|
||||
function run_test() {
|
||||
if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
|
||||
dump(
|
||||
"INFO | test_crash_after_js_oom_reporting.js | Can't test crashreporter in a non-libxul build.\n"
|
||||
|
@ -6,7 +6,7 @@ add_task(async function run_test() {
|
|||
return;
|
||||
}
|
||||
|
||||
await do_crash(
|
||||
do_crash(
|
||||
function() {
|
||||
crashType = CrashTestUtils.CRASH_MOZ_CRASH;
|
||||
crashReporter.annotateCrashReport("TestKey", "Yes");
|
||||
|
@ -24,4 +24,4 @@ add_task(async function run_test() {
|
|||
},
|
||||
true
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
add_task(async function run_test() {
|
||||
function run_test() {
|
||||
if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
|
||||
dump(
|
||||
"INFO | test_crash_after_js_oom_recovered.js | Can't test crashreporter in a non-libxul build.\n"
|
||||
|
@ -6,7 +6,7 @@ add_task(async function run_test() {
|
|||
return;
|
||||
}
|
||||
|
||||
await do_crash(
|
||||
do_crash(
|
||||
function() {
|
||||
crashType = CrashTestUtils.CRASH_MOZ_CRASH;
|
||||
crashReporter.annotateCrashReport("TestKey", "Yes");
|
||||
|
@ -19,4 +19,4 @@ add_task(async function run_test() {
|
|||
},
|
||||
true
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
add_task(async function run_test() {
|
||||
function run_test() {
|
||||
if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
|
||||
dump(
|
||||
"INFO | test_crash_after_js_oom_reported.js | Can't test crashreporter in a non-libxul build.\n"
|
||||
|
@ -6,7 +6,7 @@ add_task(async function run_test() {
|
|||
return;
|
||||
}
|
||||
|
||||
await do_crash(
|
||||
do_crash(
|
||||
function() {
|
||||
crashType = CrashTestUtils.CRASH_MOZ_CRASH;
|
||||
crashReporter.annotateCrashReport("TestKey", "Yes");
|
||||
|
@ -33,4 +33,4 @@ add_task(async function run_test() {
|
|||
},
|
||||
true
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
add_task(async function run_test() {
|
||||
function run_test() {
|
||||
if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
|
||||
dump(
|
||||
"INFO | test_crash_after_js_oom_reported_2.js | Can't test crashreporter in a non-libxul build.\n"
|
||||
|
@ -6,7 +6,7 @@ add_task(async function run_test() {
|
|||
return;
|
||||
}
|
||||
|
||||
await do_crash(
|
||||
do_crash(
|
||||
function() {
|
||||
crashType = CrashTestUtils.CRASH_MOZ_CRASH;
|
||||
crashReporter.annotateCrashReport("TestKey", "Yes");
|
||||
|
@ -25,4 +25,4 @@ add_task(async function run_test() {
|
|||
},
|
||||
true
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
add_task(async function run_test() {
|
||||
function run_test() {
|
||||
// Try crashing with a runtime abort
|
||||
await do_crash(
|
||||
do_crash(
|
||||
function() {
|
||||
crashType = CrashTestUtils.CRASH_MOZ_CRASH;
|
||||
crashReporter.annotateCrashReport("TestKey", "TestValue");
|
||||
|
@ -14,4 +14,4 @@ add_task(async function run_test() {
|
|||
// process will exit with a zero exit status
|
||||
true
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
add_task(async function run_test() {
|
||||
function run_test() {
|
||||
if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
|
||||
dump(
|
||||
"INFO | test_crash_oom.js | Can't test crashreporter in a non-libxul build.\n"
|
||||
|
@ -6,7 +6,7 @@ add_task(async function run_test() {
|
|||
return;
|
||||
}
|
||||
|
||||
await do_crash(
|
||||
do_crash(
|
||||
function() {
|
||||
crashType = CrashTestUtils.CRASH_OOM;
|
||||
crashReporter.annotateCrashReport("TestKey", "Yes");
|
||||
|
@ -18,4 +18,4 @@ add_task(async function run_test() {
|
|||
},
|
||||
true
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ function check(extra, size) {
|
|||
Assert.ok(/^(\d+,)*\d+$/.test(extra.PHCFreeStack));
|
||||
}
|
||||
|
||||
add_task(async function run_test() {
|
||||
function run_test() {
|
||||
if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
|
||||
dump(
|
||||
"INFO | test_crash_phc.js | Can't test crashreporter in a non-libxul build.\n"
|
||||
|
@ -20,7 +20,7 @@ add_task(async function run_test() {
|
|||
return;
|
||||
}
|
||||
|
||||
await do_crash(
|
||||
do_crash(
|
||||
function() {
|
||||
crashType = CrashTestUtils.CRASH_PHC_USE_AFTER_FREE;
|
||||
},
|
||||
|
@ -31,7 +31,7 @@ add_task(async function run_test() {
|
|||
true
|
||||
);
|
||||
|
||||
await do_crash(
|
||||
do_crash(
|
||||
function() {
|
||||
crashType = CrashTestUtils.CRASH_PHC_DOUBLE_FREE;
|
||||
},
|
||||
|
@ -41,4 +41,4 @@ add_task(async function run_test() {
|
|||
},
|
||||
true
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
add_task(async function run_test() {
|
||||
function run_test() {
|
||||
if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
|
||||
dump(
|
||||
"INFO | test_crash_purevirtual.js | Can't test crashreporter in a non-libxul build.\n"
|
||||
|
@ -15,7 +15,7 @@ add_task(async function run_test() {
|
|||
}
|
||||
|
||||
// Try crashing with a pure virtual call
|
||||
await do_crash(
|
||||
do_crash(
|
||||
function() {
|
||||
crashType = CrashTestUtils.CRASH_PURE_VIRTUAL_CALL;
|
||||
crashReporter.annotateCrashReport("TestKey", "TestValue");
|
||||
|
@ -26,4 +26,4 @@ add_task(async function run_test() {
|
|||
// process will exit with a zero exit status
|
||||
true
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
add_task(async function run_test() {
|
||||
function run_test() {
|
||||
// Try crashing with a Rust panic
|
||||
await do_crash(
|
||||
do_crash(
|
||||
function() {
|
||||
Cc["@mozilla.org/xpcom/debug;1"]
|
||||
.getService(Ci.nsIDebug2)
|
||||
|
@ -12,4 +12,4 @@ add_task(async function run_test() {
|
|||
// process will exit with a zero exit status
|
||||
true
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
add_task(async function run_test() {
|
||||
function run_test() {
|
||||
// Try crashing with a Rust panic
|
||||
await do_crash(
|
||||
do_crash(
|
||||
function() {
|
||||
Cc["@mozilla.org/xpcom/debug;1"]
|
||||
.getService(Ci.nsIDebug2)
|
||||
|
@ -12,4 +12,4 @@ add_task(async function run_test() {
|
|||
// process will exit with a zero exit status
|
||||
true
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -35,6 +35,6 @@ function after_crash(mdump, extra) {
|
|||
Assert.equal(extra.ShutdownProgress, "profile-before-change");
|
||||
}
|
||||
|
||||
add_task(async function run_test() {
|
||||
await do_crash(setup_crash, after_crash);
|
||||
});
|
||||
function run_test() {
|
||||
do_crash(setup_crash, after_crash);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
add_task(async function run_test() {
|
||||
function run_test() {
|
||||
if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
|
||||
dump(
|
||||
"INFO | test_crash_thread_annotation.js | Can't test crashreporter in a non-libxul build.\n"
|
||||
|
@ -6,7 +6,7 @@ add_task(async function run_test() {
|
|||
return;
|
||||
}
|
||||
|
||||
await do_crash(
|
||||
do_crash(
|
||||
function() {
|
||||
crashType = CrashTestUtils.CRASH_INVALID_POINTER_DEREF;
|
||||
},
|
||||
|
@ -15,4 +15,4 @@ add_task(async function run_test() {
|
|||
},
|
||||
true
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
add_task(async function run_test() {
|
||||
function run_test() {
|
||||
// Try crashing with an uncaught exception.
|
||||
await do_crash(
|
||||
do_crash(
|
||||
function() {
|
||||
crashType = CrashTestUtils.CRASH_UNCAUGHT_EXCEPTION;
|
||||
crashReporter.annotateCrashReport("TestKey", "TestValue");
|
||||
|
@ -14,4 +14,4 @@ add_task(async function run_test() {
|
|||
// process will exit with a zero exit status
|
||||
true
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
add_task(async function run_test() {
|
||||
await do_x64CFITest("CRASH_X64CFI_ALLOC_LARGE", [
|
||||
function run_test() {
|
||||
do_x64CFITest("CRASH_X64CFI_ALLOC_LARGE", [
|
||||
{ symbol: "CRASH_X64CFI_ALLOC_LARGE", trust: "context" },
|
||||
{ symbol: "CRASH_X64CFI_LAUNCHER", trust: "cfi" },
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
add_task(async function run_test() {
|
||||
await do_x64CFITest("CRASH_X64CFI_ALLOC_SMALL", [
|
||||
function run_test() {
|
||||
do_x64CFITest("CRASH_X64CFI_ALLOC_SMALL", [
|
||||
{ symbol: "CRASH_X64CFI_ALLOC_SMALL", trust: "context" },
|
||||
{ symbol: "CRASH_X64CFI_LAUNCHER", trust: "cfi" },
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
add_task(async function run_test() {
|
||||
await do_x64CFITest("CRASH_X64CFI_EPILOG", [
|
||||
function run_test() {
|
||||
do_x64CFITest("CRASH_X64CFI_EPILOG", [
|
||||
{ symbol: "CRASH_X64CFI_EPILOG", trust: "context" },
|
||||
{ symbol: "CRASH_X64CFI_LAUNCHER", trust: "cfi" },
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
add_task(async function run_test() {
|
||||
function run_test() {
|
||||
// Test that minidump-analyzer gracefully handles chained
|
||||
// unwind code entries that form a circular reference
|
||||
// (infinite loop).
|
||||
|
@ -11,7 +11,7 @@ add_task(async function run_test() {
|
|||
// but should not be calculated via CFI. If we see CFI here that would be an
|
||||
// indication that either our alternative EXE was not used, or we failed to
|
||||
// abandon unwind info parsing.
|
||||
await do_x64CFITest(
|
||||
do_x64CFITest(
|
||||
"CRASH_X64CFI_ALLOC_SMALL",
|
||||
[
|
||||
{ symbol: "CRASH_X64CFI_ALLOC_SMALL", trust: "context" },
|
||||
|
@ -19,4 +19,4 @@ add_task(async function run_test() {
|
|||
],
|
||||
["--force-use-module", exe.path]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
add_task(async function run_test() {
|
||||
function run_test() {
|
||||
// Test that minidump-analyzer gracefully handles chained
|
||||
// IMAGE_RUNTIME_FUNCTION_ENTRY items that form a circular reference
|
||||
// (infinite loop).
|
||||
|
@ -11,7 +11,7 @@ add_task(async function run_test() {
|
|||
// but should not be calculated via CFI. If we see CFI here that would be an
|
||||
// indication that either our alternative EXE was not used, or we failed to
|
||||
// abandon unwind info parsing.
|
||||
await do_x64CFITest(
|
||||
do_x64CFITest(
|
||||
"CRASH_X64CFI_ALLOC_SMALL",
|
||||
[
|
||||
{ symbol: "CRASH_X64CFI_ALLOC_SMALL", trust: "context" },
|
||||
|
@ -19,4 +19,4 @@ add_task(async function run_test() {
|
|||
],
|
||||
["--force-use-module", exe.path]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
add_task(async function run_test() {
|
||||
function run_test() {
|
||||
// Test that minidump-analyzer gracefully handles an invalid pointer to the
|
||||
// exception unwind information.
|
||||
let exe = do_get_file("test_crash_win64cfi_invalid_exception_rva.exe");
|
||||
|
@ -10,7 +10,7 @@ add_task(async function run_test() {
|
|||
// but should not be calculated via CFI. If we see CFI here that would be an
|
||||
// indication that either our alternative EXE was not used, or we failed to
|
||||
// abandon unwind info parsing.
|
||||
await do_x64CFITest(
|
||||
do_x64CFITest(
|
||||
"CRASH_X64CFI_ALLOC_SMALL",
|
||||
[
|
||||
{ symbol: "CRASH_X64CFI_ALLOC_SMALL", trust: "context" },
|
||||
|
@ -18,4 +18,4 @@ add_task(async function run_test() {
|
|||
],
|
||||
["--force-use-module", exe.path]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
add_task(async function run_test() {
|
||||
function run_test() {
|
||||
// Test that minidump-analyzer gracefully handles corrupt PE files.
|
||||
let exe = do_get_file("test_crash_win64cfi_not_a_pe.exe");
|
||||
ok(exe);
|
||||
|
@ -9,7 +9,7 @@ add_task(async function run_test() {
|
|||
// but should not be calculated via CFI. If we see CFI here that would be an
|
||||
// indication that either our alternative EXE was not used, or we failed to
|
||||
// abandon unwind info parsing.
|
||||
await do_x64CFITest(
|
||||
do_x64CFITest(
|
||||
"CRASH_X64CFI_ALLOC_SMALL",
|
||||
[
|
||||
{ symbol: "CRASH_X64CFI_ALLOC_SMALL", trust: "context" },
|
||||
|
@ -17,4 +17,4 @@ add_task(async function run_test() {
|
|||
],
|
||||
["--force-use-module", exe.path]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
add_task(async function run_test() {
|
||||
await do_x64CFITest("CRASH_X64CFI_PUSH_NONVOL", [
|
||||
function run_test() {
|
||||
do_x64CFITest("CRASH_X64CFI_PUSH_NONVOL", [
|
||||
{ symbol: "CRASH_X64CFI_PUSH_NONVOL", trust: "context" },
|
||||
{ symbol: "CRASH_X64CFI_LAUNCHER", trust: "cfi" },
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
add_task(async function run_test() {
|
||||
await do_x64CFITest("CRASH_X64CFI_SAVE_NONVOL", [
|
||||
function run_test() {
|
||||
do_x64CFITest("CRASH_X64CFI_SAVE_NONVOL", [
|
||||
{ symbol: "CRASH_X64CFI_SAVE_NONVOL", trust: "context" },
|
||||
{ symbol: "CRASH_X64CFI_LAUNCHER", trust: "cfi" },
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
add_task(async function run_test() {
|
||||
await do_x64CFITest("CRASH_X64CFI_SAVE_NONVOL_FAR", [
|
||||
function run_test() {
|
||||
do_x64CFITest("CRASH_X64CFI_SAVE_NONVOL_FAR", [
|
||||
{ symbol: "CRASH_X64CFI_SAVE_NONVOL_FAR", trust: "context" },
|
||||
{ symbol: "CRASH_X64CFI_LAUNCHER", trust: "cfi" },
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
add_task(async function run_test() {
|
||||
await do_x64CFITest("CRASH_X64CFI_SAVE_XMM128", [
|
||||
function run_test() {
|
||||
do_x64CFITest("CRASH_X64CFI_SAVE_XMM128", [
|
||||
{ symbol: "CRASH_X64CFI_SAVE_XMM128", trust: "context" },
|
||||
{ symbol: "CRASH_X64CFI_LAUNCHER", trust: "cfi" },
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
add_task(async function run_test() {
|
||||
await do_x64CFITest("CRASH_X64CFI_SAVE_XMM128_FAR", [
|
||||
function run_test() {
|
||||
do_x64CFITest("CRASH_X64CFI_SAVE_XMM128_FAR", [
|
||||
{ symbol: "CRASH_X64CFI_SAVE_XMM128_FAR", trust: "context" },
|
||||
{ symbol: "CRASH_X64CFI_LAUNCHER", trust: "cfi" },
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
add_task(async function run_test() {
|
||||
function run_test() {
|
||||
// In the case of an unknown unwind code or missing CFI,
|
||||
// make certain we can still walk the stack via stack scan. The crashing
|
||||
// function places NO_MANS_LAND on the stack so it will get picked up via
|
||||
// stack scan.
|
||||
await do_x64CFITest("CRASH_X64CFI_UNKNOWN_OPCODE", [
|
||||
do_x64CFITest("CRASH_X64CFI_UNKNOWN_OPCODE", [
|
||||
{ symbol: "CRASH_X64CFI_UNKNOWN_OPCODE", trust: "context" },
|
||||
// Trust may either be scan or frame_pointer; we don't really care as
|
||||
// long as the address is expected.
|
||||
{ symbol: "CRASH_X64CFI_NO_MANS_LAND", trust: null },
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
add_task(async function run_test() {
|
||||
function run_test() {
|
||||
if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
|
||||
dump(
|
||||
"INFO | test_crash_oom.js | Can't test crashreporter in a non-libxul build.\n"
|
||||
|
@ -10,7 +10,7 @@ add_task(async function run_test() {
|
|||
// head.js so that nsICrashReporter::saveMemoryReport can use a profile
|
||||
// within the crasher subprocess.
|
||||
|
||||
await do_crash(
|
||||
do_crash(
|
||||
function() {
|
||||
// Delay crashing so that the memory report has time to complete.
|
||||
shouldDelay = true;
|
||||
|
@ -49,4 +49,4 @@ add_task(async function run_test() {
|
|||
},
|
||||
true
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -59,12 +59,26 @@ function run_test() {
|
|||
} catch (ex) {
|
||||
Assert.equal(ex.result, Cr.NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
try {
|
||||
cr.annotateCrashReport("TestKey", "da\0ta");
|
||||
do_throw(
|
||||
"Calling annotateCrashReport() with a '\\0' in data should have thrown!"
|
||||
);
|
||||
} catch (ex) {
|
||||
Assert.equal(ex.result, Cr.NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
cr.annotateCrashReport("TestKey", "testData1");
|
||||
// Replace previous data.
|
||||
cr.annotateCrashReport("TestKey", "testData2");
|
||||
// Allow nul chars in annotations.
|
||||
cr.annotateCrashReport("TestKey", "da\0ta");
|
||||
|
||||
try {
|
||||
cr.appendAppNotesToCrashReport("da\0ta");
|
||||
do_throw(
|
||||
"Calling appendAppNotesToCrashReport() with a '\\0' in data should have thrown!"
|
||||
);
|
||||
} catch (ex) {
|
||||
Assert.equal(ex.result, Cr.NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
cr.appendAppNotesToCrashReport("additional testData3");
|
||||
// Add more data.
|
||||
cr.appendAppNotesToCrashReport("additional testData4");
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
add_task(async function run_test() {
|
||||
await do_crash(
|
||||
function run_test() {
|
||||
do_crash(
|
||||
function() {
|
||||
let appAddr = CrashTestUtils.saveAppMemory();
|
||||
crashReporter.registerAppMemory(appAddr, 32);
|
||||
|
@ -10,4 +10,4 @@ add_task(async function run_test() {
|
|||
Assert.ok(CrashTestUtils.dumpCheckMemory(mdump.path));
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
add_task(async function run_test() {
|
||||
function run_test() {
|
||||
var is_win7_or_newer = false;
|
||||
var is_windows = false;
|
||||
var ph = Cc["@mozilla.org/network/protocol;1?name=http"].getService(
|
||||
|
@ -17,7 +17,7 @@ add_task(async function run_test() {
|
|||
}
|
||||
|
||||
// try a basic crash
|
||||
await do_crash(null, function(mdump, extra) {
|
||||
do_crash(null, function(mdump, extra) {
|
||||
Assert.ok(mdump.exists());
|
||||
Assert.ok(mdump.fileSize > 0);
|
||||
Assert.ok("StartupTime" in extra);
|
||||
|
@ -51,14 +51,11 @@ add_task(async function run_test() {
|
|||
});
|
||||
|
||||
// check setting some basic data
|
||||
await do_crash(
|
||||
do_crash(
|
||||
function() {
|
||||
// Add various annotations
|
||||
crashReporter.annotateCrashReport("TestKey", "TestValue");
|
||||
crashReporter.annotateCrashReport(
|
||||
"TestUnicode",
|
||||
"\u{1F4A9}\n\u{0000}Escape"
|
||||
);
|
||||
crashReporter.annotateCrashReport("TestUnicode", "\u{1F4A9}");
|
||||
crashReporter.annotateCrashReport("Add-ons", "test%40mozilla.org:0.1");
|
||||
crashReporter.appendAppNotesToCrashReport("Junk");
|
||||
crashReporter.appendAppNotesToCrashReport("MoreJunk");
|
||||
|
@ -73,7 +70,7 @@ add_task(async function run_test() {
|
|||
},
|
||||
function(mdump, extra) {
|
||||
Assert.equal(extra.TestKey, "TestValue");
|
||||
Assert.equal(extra.TestUnicode, "\u{1F4A9}\n\u{0000}Escape");
|
||||
Assert.equal(extra.TestUnicode, "\u{1F4A9}");
|
||||
Assert.equal(extra.Notes, "JunkMoreJunk");
|
||||
Assert.equal(extra["Add-ons"], "test%40mozilla.org:0.1");
|
||||
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
||||
|
@ -96,7 +93,7 @@ add_task(async function run_test() {
|
|||
}
|
||||
);
|
||||
|
||||
await do_crash(
|
||||
do_crash(
|
||||
function() {
|
||||
// Enable the FHR, official policy bypass (since we're in a test) and
|
||||
// specify a telemetry server & client ID.
|
||||
|
@ -149,7 +146,7 @@ add_task(async function run_test() {
|
|||
}
|
||||
);
|
||||
|
||||
await do_crash(
|
||||
do_crash(
|
||||
function() {
|
||||
// Disable the FHR upload, no telemetry annotations should be present.
|
||||
Services.prefs.setBoolPref(
|
||||
|
@ -183,7 +180,7 @@ add_task(async function run_test() {
|
|||
}
|
||||
);
|
||||
|
||||
await do_crash(
|
||||
do_crash(
|
||||
function() {
|
||||
// No telemetry annotations should be present if the user has not been
|
||||
// notified yet
|
||||
|
@ -217,4 +214,4 @@ add_task(async function run_test() {
|
|||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
add_task(async function run_test() {
|
||||
function run_test() {
|
||||
if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
|
||||
dump(
|
||||
"INFO | test_crash_oom.js | Can't test crashreporter in a non-libxul build.\n"
|
||||
|
@ -6,7 +6,7 @@ add_task(async function run_test() {
|
|||
return;
|
||||
}
|
||||
|
||||
await do_crash(
|
||||
do_crash(
|
||||
function() {
|
||||
crashType = CrashTestUtils.CRASH_OOM;
|
||||
crashReporter.annotateCrashReport("TestKey", "Yes");
|
||||
|
@ -25,4 +25,4 @@ add_task(async function run_test() {
|
|||
},
|
||||
true
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
add_task(async function run_test() {
|
||||
function run_test() {
|
||||
// Ensure that attempting to override the exception handler doesn't cause
|
||||
// us to lose our exception handler.
|
||||
await do_crash(
|
||||
do_crash(
|
||||
function() {
|
||||
CrashTestUtils.TryOverrideExceptionHandler();
|
||||
},
|
||||
function(mdump, extra) {},
|
||||
true
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* import-globals-from ../unit/head_crashreporter.js */
|
||||
load("../unit/head_crashreporter.js");
|
||||
|
||||
add_task(async function run_test() {
|
||||
function run_test() {
|
||||
if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
|
||||
dump(
|
||||
"INFO | test_content_annotation.js | Can't test crashreporter in a non-libxul build.\n"
|
||||
|
@ -15,7 +15,7 @@ add_task(async function run_test() {
|
|||
scope.TelemetryController.testSetup();
|
||||
|
||||
// Try crashing with a runtime abort
|
||||
await do_content_crash(
|
||||
do_content_crash(
|
||||
function() {
|
||||
crashType = CrashTestUtils.CRASH_MOZ_CRASH;
|
||||
crashReporter.annotateCrashReport("TestKey", "TestValue");
|
||||
|
@ -29,4 +29,4 @@ add_task(async function run_test() {
|
|||
Assert.notEqual(extra.Notes.indexOf("!!!foo!!!"), -1);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* import-globals-from ../unit/head_crashreporter.js */
|
||||
load("../unit/head_crashreporter.js");
|
||||
|
||||
add_task(async function run_test() {
|
||||
function run_test() {
|
||||
if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
|
||||
dump(
|
||||
"INFO | test_content_annotation.js | Can't test crashreporter in a non-libxul build.\n"
|
||||
|
@ -10,7 +10,7 @@ add_task(async function run_test() {
|
|||
}
|
||||
|
||||
// Try crashing with an OOM
|
||||
await do_content_crash(
|
||||
do_content_crash(
|
||||
function() {
|
||||
crashType = CrashTestUtils.CRASH_OOM;
|
||||
},
|
||||
|
@ -18,4 +18,4 @@ add_task(async function run_test() {
|
|||
Assert.ok("OOMAllocationSize" in extra);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
/* import-globals-from ../unit/head_crashreporter.js */
|
||||
load("../unit/head_crashreporter.js");
|
||||
|
||||
add_task(async function run_test() {
|
||||
function run_test() {
|
||||
var is_win7_or_newer = false;
|
||||
var ph = Cc["@mozilla.org/network/protocol;1?name=http"].getService(
|
||||
Ci.nsIHttpProtocolHandler
|
||||
|
@ -18,7 +18,7 @@ add_task(async function run_test() {
|
|||
is_win7_or_newer = true;
|
||||
}
|
||||
|
||||
await do_content_crash(null, function(mdump, extra) {
|
||||
do_content_crash(null, function(mdump, extra) {
|
||||
Assert.ok(mdump.exists());
|
||||
Assert.ok(mdump.fileSize > 0);
|
||||
if (is_win7_or_newer) {
|
||||
|
@ -30,4 +30,4 @@ add_task(async function run_test() {
|
|||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* import-globals-from ../unit/head_crashreporter.js */
|
||||
load("../unit/head_crashreporter.js");
|
||||
|
||||
add_task(async function run_test() {
|
||||
function run_test() {
|
||||
if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
|
||||
dump(
|
||||
"INFO | test_content_annotation.js | Can't test crashreporter in a non-libxul build.\n"
|
||||
|
@ -10,7 +10,7 @@ add_task(async function run_test() {
|
|||
}
|
||||
|
||||
// Try crashing with an OOM
|
||||
await do_content_crash(
|
||||
do_content_crash(
|
||||
function() {
|
||||
crashType = CrashTestUtils.CRASH_OOM;
|
||||
},
|
||||
|
@ -24,4 +24,4 @@ add_task(async function run_test() {
|
|||
Assert.ok("AvailablePhysicalMemory" in extra);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* import-globals-from ../unit/head_crashreporter.js */
|
||||
load("../unit/head_crashreporter.js");
|
||||
|
||||
add_task(async function run_test() {
|
||||
function run_test() {
|
||||
if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
|
||||
dump(
|
||||
"INFO | test_content_phc.js | Can't test crashreporter in a non-libxul build.\n"
|
||||
|
@ -9,7 +9,7 @@ add_task(async function run_test() {
|
|||
return;
|
||||
}
|
||||
|
||||
await do_content_crash(
|
||||
do_content_crash(
|
||||
function() {
|
||||
crashType = CrashTestUtils.CRASH_PHC_USE_AFTER_FREE;
|
||||
},
|
||||
|
@ -28,4 +28,4 @@ add_task(async function run_test() {
|
|||
Assert.ok(/^(\d+,)*\d+$/.test(extra.PHCFreeStack));
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* import-globals-from ../unit/head_crashreporter.js */
|
||||
load("../unit/head_crashreporter.js");
|
||||
|
||||
add_task(async function run_test() {
|
||||
function run_test() {
|
||||
if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
|
||||
dump(
|
||||
"INFO | test_content_phc.js | Can't test crashreporter in a non-libxul build.\n"
|
||||
|
@ -12,7 +12,7 @@ add_task(async function run_test() {
|
|||
// For some unknown reason, having two do_content_crash() calls in a single
|
||||
// test doesn't work. That explains why this test exists separately from
|
||||
// test_content_phc.js.
|
||||
await do_content_crash(
|
||||
do_content_crash(
|
||||
function() {
|
||||
crashType = CrashTestUtils.CRASH_PHC_DOUBLE_FREE;
|
||||
},
|
||||
|
@ -31,4 +31,4 @@ add_task(async function run_test() {
|
|||
Assert.ok(/^(\d+,)*\d+$/.test(extra.PHCFreeStack));
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* import-globals-from ../unit/head_crashreporter.js */
|
||||
load("../unit/head_crashreporter.js");
|
||||
|
||||
add_task(async function run_test() {
|
||||
function run_test() {
|
||||
if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
|
||||
dump(
|
||||
"INFO | test_content_rust_panic.js | Can't test crashreporter in a non-libxul build.\n"
|
||||
|
@ -10,7 +10,7 @@ add_task(async function run_test() {
|
|||
}
|
||||
|
||||
// Try crashing with a Rust panic
|
||||
await do_triggered_content_crash(
|
||||
do_triggered_content_crash(
|
||||
function() {
|
||||
Cc["@mozilla.org/xpcom/debug;1"]
|
||||
.getService(Ci.nsIDebug2)
|
||||
|
@ -20,4 +20,4 @@ add_task(async function run_test() {
|
|||
Assert.equal(extra.MozCrashReason, "OH NO");
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* import-globals-from ../unit/head_crashreporter.js */
|
||||
load("../unit/head_crashreporter.js");
|
||||
|
||||
add_task(async function run_test() {
|
||||
function run_test() {
|
||||
if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
|
||||
dump(
|
||||
"INFO | test_content_rust_panic.js | Can't test crashreporter in a non-libxul build.\n"
|
||||
|
@ -10,7 +10,7 @@ add_task(async function run_test() {
|
|||
}
|
||||
|
||||
// Try crashing with a Rust panic
|
||||
await do_triggered_content_crash(
|
||||
do_triggered_content_crash(
|
||||
function() {
|
||||
Cc["@mozilla.org/xpcom/debug;1"]
|
||||
.getService(Ci.nsIDebug2)
|
||||
|
@ -20,4 +20,4 @@ add_task(async function run_test() {
|
|||
Assert.equal(extra.MozCrashReason, "OH NO\nOH NOES!");
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -108,6 +108,7 @@
|
|||
"jsesc.js": ["jsesc"],
|
||||
"json2.js": ["JSON"],
|
||||
"keys.js": ["BulkKeyBundle", "SyncKeyBundle"],
|
||||
"KeyValueParser.jsm": ["parseKeyValuePairsFromLines", "parseKeyValuePairs", "parseKeyValuePairsFromFile", "parseKeyValuePairsFromFileAsync"],
|
||||
"kinto-http-client.js": ["KintoHttpClient"],
|
||||
"kinto-offline-client.js": ["Kinto"],
|
||||
"kinto-storage-adapter.js": ["FirefoxAdapter"],
|
||||
|
|
Загрузка…
Ссылка в новой задаче