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:
Razvan Maries 2019-11-16 13:00:43 +02:00
Родитель 5e32e89575
Коммит b90bde90fc
90 изменённых файлов: 1212 добавлений и 1610 удалений

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

@ -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> &parameters,
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> &parameters,
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> &parameters,
+ const string &parameters,
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> &parameters,
+bool HTTPUpload::GenerateRequestBody(const string &parameters,
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> &parameters) {
- 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> &parameters,
+ const string &parameters,
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> &parameters,
+ static bool GenerateRequestBody(const string &parameters,
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> &parameters);
-
// 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> &parameters,
+ const string &parameters,
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> &parameters) {
- 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> &parameters,
+ const string &parameters,
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> &parameters);
-
// 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 &parameters,
const map<string, string> &parameters,
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> &parameters) {
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 &parameters,
const map<string, string> &parameters,
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> &parameters);
// 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 &parameters,
const map<wstring, wstring> &parameters,
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 &parameters,
bool HTTPUpload::GenerateRequestBody(const map<wstring, wstring> &parameters,
const map<wstring, wstring> &files,
const wstring &boundary,
string *request_body) {
@ -271,19 +276,14 @@ bool HTTPUpload::GenerateRequestBody(const string &parameters,
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> &parameters) {
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 &parameters,
const map<wstring, wstring> &parameters,
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 &parameters,
static bool GenerateRequestBody(const map<wstring, wstring> &parameters,
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> &parameters);
// 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"],