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