Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | Components.utils.import("resource://gre/modules/osfile.jsm"); |
michael@0 | 2 | |
michael@0 | 3 | function getEventDir() { |
michael@0 | 4 | return OS.Path.join(do_get_tempdir().path, "crash-events"); |
michael@0 | 5 | } |
michael@0 | 6 | |
michael@0 | 7 | /* |
michael@0 | 8 | * Run an xpcshell subprocess and crash it. |
michael@0 | 9 | * |
michael@0 | 10 | * @param setup |
michael@0 | 11 | * A string of JavaScript code to execute in the subprocess |
michael@0 | 12 | * before crashing. If this is a function and not a string, |
michael@0 | 13 | * it will have .toSource() called on it, and turned into |
michael@0 | 14 | * a call to itself. (for programmer convenience) |
michael@0 | 15 | * This code will be evaluted between crasher_subprocess_head.js |
michael@0 | 16 | * and crasher_subprocess_tail.js, so it will have access |
michael@0 | 17 | * to everything defined in crasher_subprocess_head.js, |
michael@0 | 18 | * which includes "crashReporter", a variable holding |
michael@0 | 19 | * the crash reporter service. |
michael@0 | 20 | * |
michael@0 | 21 | * @param callback |
michael@0 | 22 | * A JavaScript function to be called after the subprocess |
michael@0 | 23 | * crashes. It will be passed (minidump, extra), where |
michael@0 | 24 | * minidump is an nsILocalFile of the minidump file produced, |
michael@0 | 25 | * and extra is an object containing the key,value pairs from |
michael@0 | 26 | * the .extra file. |
michael@0 | 27 | * |
michael@0 | 28 | * @param canReturnZero |
michael@0 | 29 | * If true, the subprocess may return with a zero exit code. |
michael@0 | 30 | * Certain types of crashes may not cause the process to |
michael@0 | 31 | * exit with an error. |
michael@0 | 32 | */ |
michael@0 | 33 | function do_crash(setup, callback, canReturnZero) |
michael@0 | 34 | { |
michael@0 | 35 | // get current process filename (xpcshell) |
michael@0 | 36 | let ds = Components.classes["@mozilla.org/file/directory_service;1"] |
michael@0 | 37 | .getService(Components.interfaces.nsIProperties); |
michael@0 | 38 | let bin = ds.get("CurProcD", Components.interfaces.nsILocalFile); |
michael@0 | 39 | bin.append("xpcshell"); |
michael@0 | 40 | if (!bin.exists()) { |
michael@0 | 41 | bin.leafName = "xpcshell.exe"; |
michael@0 | 42 | do_check_true(bin.exists()); |
michael@0 | 43 | if (!bin.exists()) |
michael@0 | 44 | // weird, can't find xpcshell binary? |
michael@0 | 45 | do_throw("Can't find xpcshell binary!"); |
michael@0 | 46 | } |
michael@0 | 47 | // get Gre dir (GreD) |
michael@0 | 48 | let greD = ds.get("GreD", Components.interfaces.nsILocalFile); |
michael@0 | 49 | let headfile = do_get_file("crasher_subprocess_head.js"); |
michael@0 | 50 | let tailfile = do_get_file("crasher_subprocess_tail.js"); |
michael@0 | 51 | // run xpcshell -g GreD -f head -e "some setup code" -f tail |
michael@0 | 52 | let process = Components.classes["@mozilla.org/process/util;1"] |
michael@0 | 53 | .createInstance(Components.interfaces.nsIProcess); |
michael@0 | 54 | process.init(bin); |
michael@0 | 55 | let args = ['-g', greD.path, |
michael@0 | 56 | '-f', headfile.path]; |
michael@0 | 57 | if (setup) { |
michael@0 | 58 | if (typeof(setup) == "function") |
michael@0 | 59 | // funky, but convenient |
michael@0 | 60 | setup = "("+setup.toSource()+")();"; |
michael@0 | 61 | args.push('-e', setup); |
michael@0 | 62 | } |
michael@0 | 63 | args.push('-f', tailfile.path); |
michael@0 | 64 | |
michael@0 | 65 | let env = Components.classes["@mozilla.org/process/environment;1"] |
michael@0 | 66 | .getService(Components.interfaces.nsIEnvironment); |
michael@0 | 67 | |
michael@0 | 68 | let crashD = do_get_tempdir(); |
michael@0 | 69 | crashD.append("crash-events"); |
michael@0 | 70 | if (!crashD.exists()) { |
michael@0 | 71 | crashD.create(crashD.DIRECTORY_TYPE, 0700); |
michael@0 | 72 | } |
michael@0 | 73 | |
michael@0 | 74 | env.set("CRASHES_EVENTS_DIR", crashD.path); |
michael@0 | 75 | |
michael@0 | 76 | try { |
michael@0 | 77 | process.run(true, args, args.length); |
michael@0 | 78 | } |
michael@0 | 79 | catch(ex) {} // on Windows we exit with a -1 status when crashing. |
michael@0 | 80 | finally { |
michael@0 | 81 | env.set("CRASHES_EVENTS_DIR", ""); |
michael@0 | 82 | } |
michael@0 | 83 | |
michael@0 | 84 | if (!canReturnZero) { |
michael@0 | 85 | // should exit with an error (should have crashed) |
michael@0 | 86 | do_check_neq(process.exitValue, 0); |
michael@0 | 87 | } |
michael@0 | 88 | |
michael@0 | 89 | handleMinidump(callback); |
michael@0 | 90 | } |
michael@0 | 91 | |
michael@0 | 92 | function handleMinidump(callback) |
michael@0 | 93 | { |
michael@0 | 94 | // find minidump |
michael@0 | 95 | let minidump = null; |
michael@0 | 96 | let en = do_get_tempdir().directoryEntries; |
michael@0 | 97 | while (en.hasMoreElements()) { |
michael@0 | 98 | let f = en.getNext().QueryInterface(Components.interfaces.nsILocalFile); |
michael@0 | 99 | if (f.leafName.substr(-4) == ".dmp") { |
michael@0 | 100 | minidump = f; |
michael@0 | 101 | break; |
michael@0 | 102 | } |
michael@0 | 103 | } |
michael@0 | 104 | |
michael@0 | 105 | if (minidump == null) |
michael@0 | 106 | do_throw("No minidump found!"); |
michael@0 | 107 | |
michael@0 | 108 | let extrafile = minidump.clone(); |
michael@0 | 109 | extrafile.leafName = extrafile.leafName.slice(0, -4) + ".extra"; |
michael@0 | 110 | |
michael@0 | 111 | // Just in case, don't let these files linger. |
michael@0 | 112 | do_register_cleanup(function() { |
michael@0 | 113 | if (minidump.exists()) |
michael@0 | 114 | minidump.remove(false); |
michael@0 | 115 | if (extrafile.exists()) |
michael@0 | 116 | extrafile.remove(false); |
michael@0 | 117 | }); |
michael@0 | 118 | do_check_true(extrafile.exists()); |
michael@0 | 119 | let extra = parseKeyValuePairsFromFile(extrafile); |
michael@0 | 120 | |
michael@0 | 121 | if (callback) |
michael@0 | 122 | callback(minidump, extra); |
michael@0 | 123 | |
michael@0 | 124 | if (minidump.exists()) |
michael@0 | 125 | minidump.remove(false); |
michael@0 | 126 | if (extrafile.exists()) |
michael@0 | 127 | extrafile.remove(false); |
michael@0 | 128 | } |
michael@0 | 129 | |
michael@0 | 130 | function do_content_crash(setup, callback) |
michael@0 | 131 | { |
michael@0 | 132 | do_load_child_test_harness(); |
michael@0 | 133 | do_test_pending(); |
michael@0 | 134 | |
michael@0 | 135 | // Setting the minidump path won't work in the child, so we need to do |
michael@0 | 136 | // that here. |
michael@0 | 137 | let crashReporter = |
michael@0 | 138 | Components.classes["@mozilla.org/toolkit/crash-reporter;1"] |
michael@0 | 139 | .getService(Components.interfaces.nsICrashReporter); |
michael@0 | 140 | crashReporter.minidumpPath = do_get_tempdir(); |
michael@0 | 141 | |
michael@0 | 142 | let headfile = do_get_file("../unit/crasher_subprocess_head.js"); |
michael@0 | 143 | let tailfile = do_get_file("../unit/crasher_subprocess_tail.js"); |
michael@0 | 144 | if (setup) { |
michael@0 | 145 | if (typeof(setup) == "function") |
michael@0 | 146 | // funky, but convenient |
michael@0 | 147 | setup = "("+setup.toSource()+")();"; |
michael@0 | 148 | } |
michael@0 | 149 | |
michael@0 | 150 | let handleCrash = function() { |
michael@0 | 151 | try { |
michael@0 | 152 | handleMinidump(callback); |
michael@0 | 153 | } catch (x) { |
michael@0 | 154 | do_report_unexpected_exception(x); |
michael@0 | 155 | } |
michael@0 | 156 | do_test_finished(); |
michael@0 | 157 | }; |
michael@0 | 158 | |
michael@0 | 159 | sendCommand("load(\"" + headfile.path.replace(/\\/g, "/") + "\");", function() |
michael@0 | 160 | sendCommand(setup, function() |
michael@0 | 161 | sendCommand("load(\"" + tailfile.path.replace(/\\/g, "/") + "\");", |
michael@0 | 162 | function() do_execute_soon(handleCrash) |
michael@0 | 163 | ) |
michael@0 | 164 | ) |
michael@0 | 165 | ); |
michael@0 | 166 | } |
michael@0 | 167 | |
michael@0 | 168 | // Import binary APIs via js-ctypes. |
michael@0 | 169 | Components.utils.import("resource://test/CrashTestUtils.jsm"); |
michael@0 | 170 | Components.utils.import("resource://gre/modules/KeyValueParser.jsm"); |