summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/test/unit/head_crashreporter.js
blob: 45c491ad2a25be665433393613be22bbc52282da (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
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");