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