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