diff options
Diffstat (limited to 'toolkit/crashreporter/test/unit/head_crashreporter.js')
-rw-r--r-- | toolkit/crashreporter/test/unit/head_crashreporter.js | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/toolkit/crashreporter/test/unit/head_crashreporter.js b/toolkit/crashreporter/test/unit/head_crashreporter.js new file mode 100644 index 000000000..45c491ad2 --- /dev/null +++ b/toolkit/crashreporter/test/unit/head_crashreporter.js @@ -0,0 +1,173 @@ +Components.utils.import("resource://gre/modules/osfile.jsm"); + +function getEventDir() { + return OS.Path.join(do_get_tempdir().path, "crash-events"); +} + +/* + * Run an xpcshell subprocess and crash it. + * + * @param setup + * A string of JavaScript code to execute in the subprocess + * before crashing. If this is a function and not a string, + * it will have .toSource() called on it, and turned into + * a call to itself. (for programmer convenience) + * This code will be evaluted between crasher_subprocess_head.js + * and crasher_subprocess_tail.js, so it will have access + * to everything defined in crasher_subprocess_head.js, + * which includes "crashReporter", a variable holding + * the crash reporter service. + * + * @param callback + * A JavaScript function to be called after the subprocess + * crashes. It will be passed (minidump, extra), where + * minidump is an nsILocalFile of the minidump file produced, + * and extra is an object containing the key,value pairs from + * the .extra file. + * + * @param canReturnZero + * If true, the subprocess may return with a zero exit code. + * Certain types of crashes may not cause the process to + * exit with an error. + */ +function do_crash(setup, callback, canReturnZero) +{ + // get current process filename (xpcshell) + let ds = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties); + let bin = ds.get("XREExeF", Components.interfaces.nsILocalFile); + if (!bin.exists()) { + // weird, can't find xpcshell binary? + do_throw("Can't find xpcshell binary!"); + } + // get Gre dir (GreD) + let greD = ds.get("GreD", Components.interfaces.nsILocalFile); + let headfile = do_get_file("crasher_subprocess_head.js"); + let tailfile = do_get_file("crasher_subprocess_tail.js"); + // run xpcshell -g GreD -f head -e "some setup code" -f tail + let process = Components.classes["@mozilla.org/process/util;1"] + .createInstance(Components.interfaces.nsIProcess); + process.init(bin); + let args = ['-g', greD.path, + '-f', headfile.path]; + if (setup) { + if (typeof(setup) == "function") + // funky, but convenient + setup = "("+setup.toSource()+")();"; + args.push('-e', setup); + } + args.push('-f', tailfile.path); + + let env = Components.classes["@mozilla.org/process/environment;1"] + .getService(Components.interfaces.nsIEnvironment); + + let crashD = do_get_tempdir(); + crashD.append("crash-events"); + if (!crashD.exists()) { + crashD.create(crashD.DIRECTORY_TYPE, 0o700); + } + + env.set("CRASHES_EVENTS_DIR", crashD.path); + + try { + process.run(true, args, args.length); + } + catch (ex) {} // on Windows we exit with a -1 status when crashing. + finally { + env.set("CRASHES_EVENTS_DIR", ""); + } + + if (!canReturnZero) { + // should exit with an error (should have crashed) + do_check_neq(process.exitValue, 0); + } + + handleMinidump(callback); +} + +function handleMinidump(callback) +{ + // find minidump + let minidump = null; + let en = do_get_tempdir().directoryEntries; + while (en.hasMoreElements()) { + let f = en.getNext().QueryInterface(Components.interfaces.nsILocalFile); + if (f.leafName.substr(-4) == ".dmp") { + minidump = f; + break; + } + } + + if (minidump == null) + do_throw("No minidump found!"); + + let extrafile = minidump.clone(); + extrafile.leafName = extrafile.leafName.slice(0, -4) + ".extra"; + + let memoryfile = minidump.clone(); + memoryfile.leafName = memoryfile.leafName.slice(0, -4) + ".memory.json.gz"; + + // Just in case, don't let these files linger. + do_register_cleanup(function() { + if (minidump.exists()) + minidump.remove(false); + if (extrafile.exists()) + extrafile.remove(false); + if (memoryfile.exists()) + memoryfile.remove(false); + }); + do_check_true(extrafile.exists()); + let extra = parseKeyValuePairsFromFile(extrafile); + + if (callback) + callback(minidump, extra); + + if (minidump.exists()) + minidump.remove(false); + if (extrafile.exists()) + extrafile.remove(false); + if (memoryfile.exists()) + memoryfile.remove(false); +} + +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. + let crashReporter = + Components.classes["@mozilla.org/toolkit/crash-reporter;1"] + .getService(Components.interfaces.nsICrashReporter); + crashReporter.minidumpPath = do_get_tempdir(); + + let headfile = do_get_file("../unit/crasher_subprocess_head.js"); + let tailfile = do_get_file("../unit/crasher_subprocess_tail.js"); + if (setup) { + if (typeof(setup) == "function") + // funky, but convenient + setup = "("+setup.toSource()+")();"; + } + + let handleCrash = function() { + try { + handleMinidump(callback); + } catch (x) { + do_report_unexpected_exception(x); + } + do_test_finished(); + }; + + sendCommand("load(\"" + headfile.path.replace(/\\/g, "/") + "\");", () => + sendCommand(setup, () => + sendCommand("load(\"" + tailfile.path.replace(/\\/g, "/") + "\");", () => + do_execute_soon(handleCrash) + ) + ) + ); +} + +// Import binary APIs via js-ctypes. +Components.utils.import("resource://test/CrashTestUtils.jsm"); +Components.utils.import("resource://gre/modules/KeyValueParser.jsm"); |