• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3// This tests that --heap-prof, --heap-prof-dir and --heap-prof-name works.
4
5const common = require('../common');
6
7const fixtures = require('../common/fixtures');
8common.skipIfInspectorDisabled();
9
10const assert = require('assert');
11const fs = require('fs');
12const path = require('path');
13const { spawnSync } = require('child_process');
14
15const tmpdir = require('../common/tmpdir');
16
17function getHeapProfiles(dir) {
18  const list = fs.readdirSync(dir);
19  return list
20    .filter((file) => file.endsWith('.heapprofile'))
21    .map((file) => path.join(dir, file));
22}
23
24function findFirstFrameInNode(root, func) {
25  const first = root.children.find(
26    (child) => child.callFrame.functionName === func
27  );
28  if (first) {
29    return first;
30  }
31  for (const child of root.children) {
32    const first = findFirstFrameInNode(child, func);
33    if (first) {
34      return first;
35    }
36  }
37  return undefined;
38}
39
40function findFirstFrame(file, func) {
41  const data = fs.readFileSync(file, 'utf8');
42  const profile = JSON.parse(data);
43  const first = findFirstFrameInNode(profile.head, func);
44  return { frame: first, roots: profile.head.children };
45}
46
47function verifyFrames(output, file, func) {
48  const { frame, roots } = findFirstFrame(file, func);
49  if (!frame) {
50    // Show native debug output and the profile for debugging.
51    console.log(output.stderr.toString());
52    console.log(roots);
53  }
54  assert.notDeepStrictEqual(frame, undefined);
55}
56
57// We need to set --heap-prof-interval to a small enough value to make
58// sure we can find our workload in the samples, so we need to set
59// TEST_ALLOCATION > kHeapProfInterval.
60const kHeapProfInterval = 128;
61const TEST_ALLOCATION = kHeapProfInterval * 2;
62
63const env = {
64  ...process.env,
65  TEST_ALLOCATION,
66  NODE_DEBUG_NATIVE: 'INSPECTOR_PROFILER'
67};
68
69// Test --heap-prof without --heap-prof-interval. Here we just verify that
70// we manage to generate a profile.
71{
72  tmpdir.refresh();
73  const output = spawnSync(process.execPath, [
74    '--heap-prof',
75    fixtures.path('workload', 'allocation.js'),
76  ], {
77    cwd: tmpdir.path,
78    env
79  });
80  if (output.status !== 0) {
81    console.log(output.stderr.toString());
82    console.log(output);
83  }
84  assert.strictEqual(output.status, 0);
85  const profiles = getHeapProfiles(tmpdir.path);
86  assert.strictEqual(profiles.length, 1);
87}
88
89// Outputs heap profile when event loop is drained.
90// TODO(joyeecheung): share the fixutres with v8 coverage tests
91{
92  tmpdir.refresh();
93  const output = spawnSync(process.execPath, [
94    '--heap-prof',
95    '--heap-prof-interval',
96    kHeapProfInterval,
97    fixtures.path('workload', 'allocation.js'),
98  ], {
99    cwd: tmpdir.path,
100    env
101  });
102  if (output.status !== 0) {
103    console.log(output.stderr.toString());
104    console.log(output);
105  }
106  assert.strictEqual(output.status, 0);
107  const profiles = getHeapProfiles(tmpdir.path);
108  assert.strictEqual(profiles.length, 1);
109  verifyFrames(output, profiles[0], 'runAllocation');
110}
111
112// Outputs heap profile when process.exit(55) exits process.
113{
114  tmpdir.refresh();
115  const output = spawnSync(process.execPath, [
116    '--heap-prof',
117    '--heap-prof-interval',
118    kHeapProfInterval,
119    fixtures.path('workload', 'allocation-exit.js'),
120  ], {
121    cwd: tmpdir.path,
122    env
123  });
124  if (output.status !== 55) {
125    console.log(output.stderr.toString());
126  }
127  assert.strictEqual(output.status, 55);
128  const profiles = getHeapProfiles(tmpdir.path);
129  assert.strictEqual(profiles.length, 1);
130  verifyFrames(output, profiles[0], 'runAllocation');
131}
132
133// Outputs heap profile when process.kill(process.pid, "SIGINT"); exits process.
134{
135  tmpdir.refresh();
136  const output = spawnSync(process.execPath, [
137    '--heap-prof',
138    '--heap-prof-interval',
139    kHeapProfInterval,
140    fixtures.path('workload', 'allocation-sigint.js'),
141  ], {
142    cwd: tmpdir.path,
143    env
144  });
145  if (!common.isWindows) {
146    if (output.signal !== 'SIGINT') {
147      console.log(output.stderr.toString());
148    }
149    assert.strictEqual(output.signal, 'SIGINT');
150  }
151  const profiles = getHeapProfiles(tmpdir.path);
152  assert.strictEqual(profiles.length, 1);
153  verifyFrames(output, profiles[0], 'runAllocation');
154}
155
156// Outputs heap profile from worker when execArgv is set.
157{
158  tmpdir.refresh();
159  const output = spawnSync(process.execPath, [
160    fixtures.path('workload', 'allocation-worker-argv.js'),
161  ], {
162    cwd: tmpdir.path,
163    env: {
164      ...process.env,
165      HEAP_PROF_INTERVAL: '128'
166    }
167  });
168  if (output.status !== 0) {
169    console.log(output.stderr.toString());
170  }
171  assert.strictEqual(output.status, 0);
172  const profiles = getHeapProfiles(tmpdir.path);
173  assert.strictEqual(profiles.length, 1);
174  verifyFrames(output, profiles[0], 'runAllocation');
175}
176
177// --heap-prof-name without --heap-prof
178{
179  tmpdir.refresh();
180  const output = spawnSync(process.execPath, [
181    '--heap-prof-name',
182    'test.heapprofile',
183    fixtures.path('workload', 'allocation.js'),
184  ], {
185    cwd: tmpdir.path,
186    env
187  });
188  const stderr = output.stderr.toString().trim();
189  if (output.status !== 9) {
190    console.log(stderr);
191  }
192  assert.strictEqual(output.status, 9);
193  assert.strictEqual(
194    stderr,
195    `${process.execPath}: --heap-prof-name must be used with --heap-prof`);
196}
197
198// --heap-prof-dir without --heap-prof
199{
200  tmpdir.refresh();
201  const output = spawnSync(process.execPath, [
202    '--heap-prof-dir',
203    'prof',
204    fixtures.path('workload', 'allocation.js'),
205  ], {
206    cwd: tmpdir.path,
207    env
208  });
209  const stderr = output.stderr.toString().trim();
210  if (output.status !== 9) {
211    console.log(stderr);
212  }
213  assert.strictEqual(output.status, 9);
214  assert.strictEqual(
215    stderr,
216    `${process.execPath}: --heap-prof-dir must be used with --heap-prof`);
217}
218
219// --heap-prof-interval without --heap-prof
220{
221  tmpdir.refresh();
222  const output = spawnSync(process.execPath, [
223    '--heap-prof-interval',
224    kHeapProfInterval,
225    fixtures.path('workload', 'allocation.js'),
226  ], {
227    cwd: tmpdir.path,
228    env
229  });
230  const stderr = output.stderr.toString().trim();
231  if (output.status !== 9) {
232    console.log(stderr);
233  }
234  assert.strictEqual(output.status, 9);
235  assert.strictEqual(
236    stderr,
237    `${process.execPath}: ` +
238    '--heap-prof-interval must be used with --heap-prof');
239}
240
241// --heap-prof-name
242{
243  tmpdir.refresh();
244  const file = path.join(tmpdir.path, 'test.heapprofile');
245  const output = spawnSync(process.execPath, [
246    '--heap-prof',
247    '--heap-prof-name',
248    'test.heapprofile',
249    '--heap-prof-interval',
250    kHeapProfInterval,
251    fixtures.path('workload', 'allocation.js'),
252  ], {
253    cwd: tmpdir.path,
254    env
255  });
256  if (output.status !== 0) {
257    console.log(output.stderr.toString());
258  }
259  assert.strictEqual(output.status, 0);
260  const profiles = getHeapProfiles(tmpdir.path);
261  assert.deepStrictEqual(profiles, [file]);
262  verifyFrames(output, file, 'runAllocation');
263}
264
265// relative --heap-prof-dir
266{
267  tmpdir.refresh();
268  const output = spawnSync(process.execPath, [
269    '--heap-prof',
270    '--heap-prof-dir',
271    'prof',
272    '--heap-prof-interval',
273    kHeapProfInterval,
274    fixtures.path('workload', 'allocation.js'),
275  ], {
276    cwd: tmpdir.path,
277    env
278  });
279  if (output.status !== 0) {
280    console.log(output.stderr.toString());
281  }
282  assert.strictEqual(output.status, 0);
283  const dir = path.join(tmpdir.path, 'prof');
284  assert(fs.existsSync(dir));
285  const profiles = getHeapProfiles(dir);
286  assert.strictEqual(profiles.length, 1);
287  verifyFrames(output, profiles[0], 'runAllocation');
288}
289
290// absolute --heap-prof-dir
291{
292  tmpdir.refresh();
293  const dir = path.join(tmpdir.path, 'prof');
294  const output = spawnSync(process.execPath, [
295    '--heap-prof',
296    '--heap-prof-dir',
297    dir,
298    '--heap-prof-interval',
299    kHeapProfInterval,
300    fixtures.path('workload', 'allocation.js'),
301  ], {
302    cwd: tmpdir.path,
303    env
304  });
305  if (output.status !== 0) {
306    console.log(output.stderr.toString());
307  }
308  assert.strictEqual(output.status, 0);
309  assert(fs.existsSync(dir));
310  const profiles = getHeapProfiles(dir);
311  assert.strictEqual(profiles.length, 1);
312  verifyFrames(output, profiles[0], 'runAllocation');
313}
314
315// --heap-prof-dir and --heap-prof-name
316{
317  tmpdir.refresh();
318  const dir = path.join(tmpdir.path, 'prof');
319  const file = path.join(dir, 'test.heapprofile');
320  const output = spawnSync(process.execPath, [
321    '--heap-prof',
322    '--heap-prof-name',
323    'test.heapprofile',
324    '--heap-prof-dir',
325    dir,
326    '--heap-prof-interval',
327    kHeapProfInterval,
328    fixtures.path('workload', 'allocation.js'),
329  ], {
330    cwd: tmpdir.path,
331    env
332  });
333  if (output.status !== 0) {
334    console.log(output.stderr.toString());
335  }
336  assert.strictEqual(output.status, 0);
337  assert(fs.existsSync(dir));
338  const profiles = getHeapProfiles(dir);
339  assert.deepStrictEqual(profiles, [file]);
340  verifyFrames(output, file, 'runAllocation');
341}
342
343{
344  tmpdir.refresh();
345  const output = spawnSync(process.execPath, [
346    '--heap-prof-interval',
347    kHeapProfInterval,
348    '--heap-prof-dir',
349    'prof',
350    '--heap-prof',
351    fixtures.path('workload', 'allocation-worker.js'),
352  ], {
353    cwd: tmpdir.path,
354    env
355  });
356  if (output.status !== 0) {
357    console.log(output.stderr.toString());
358  }
359  assert.strictEqual(output.status, 0);
360  const dir = path.join(tmpdir.path, 'prof');
361  assert(fs.existsSync(dir));
362  const profiles = getHeapProfiles(dir);
363  assert.strictEqual(profiles.length, 2);
364  const profile1 = findFirstFrame(profiles[0], 'runAllocation');
365  const profile2 = findFirstFrame(profiles[1], 'runAllocation');
366  if (!profile1.frame && !profile2.frame) {
367    // Show native debug output and the profile for debugging.
368    console.log(output.stderr.toString());
369    console.log('heap path: ', profiles[0]);
370    console.log(profile1.roots);
371    console.log('heap path: ', profiles[1]);
372    console.log(profile2.roots);
373  }
374  assert(profile1.frame || profile2.frame);
375}
376