11cb0ef41Sopenharmony_ci'use strict'; 21cb0ef41Sopenharmony_ciconst common = require('../common'); 31cb0ef41Sopenharmony_ciconst assert = require('assert'); 41cb0ef41Sopenharmony_ciconst fs = require('fs'); 51cb0ef41Sopenharmony_ciconst path = require('path'); 61cb0ef41Sopenharmony_ciconst cp = require('child_process'); 71cb0ef41Sopenharmony_ciconst { spawnSync } = require('child_process'); 81cb0ef41Sopenharmony_ci 91cb0ef41Sopenharmony_ci// This is a sibling test to test/addons/uv-handle-leak. 101cb0ef41Sopenharmony_ci 111cb0ef41Sopenharmony_ciconst bindingPath = path.resolve( 121cb0ef41Sopenharmony_ci __dirname, '..', 'addons', 'uv-handle-leak', 'build', 131cb0ef41Sopenharmony_ci `${common.buildType}/binding.node`); 141cb0ef41Sopenharmony_ci 151cb0ef41Sopenharmony_ciif (!fs.existsSync(bindingPath)) 161cb0ef41Sopenharmony_ci common.skip('binding not built yet'); 171cb0ef41Sopenharmony_ci 181cb0ef41Sopenharmony_ciif (process.argv[2] === 'child') { 191cb0ef41Sopenharmony_ci 201cb0ef41Sopenharmony_ci const { Worker } = require('worker_threads'); 211cb0ef41Sopenharmony_ci 221cb0ef41Sopenharmony_ci // The worker thread loads and then unloads `bindingPath`. Because of this the 231cb0ef41Sopenharmony_ci // symbols in `bindingPath` are lost when the worker thread quits, but the 241cb0ef41Sopenharmony_ci // number of open handles in the worker thread's event loop is assessed in the 251cb0ef41Sopenharmony_ci // main thread afterwards, and the names of the callbacks associated with the 261cb0ef41Sopenharmony_ci // open handles is retrieved at that time as well. Thus, we require 271cb0ef41Sopenharmony_ci // `bindingPath` here so that the symbols and their names survive the life 281cb0ef41Sopenharmony_ci // cycle of the worker thread. 291cb0ef41Sopenharmony_ci require(bindingPath); 301cb0ef41Sopenharmony_ci 311cb0ef41Sopenharmony_ci new Worker(` 321cb0ef41Sopenharmony_ci const binding = require(${JSON.stringify(bindingPath)}); 331cb0ef41Sopenharmony_ci 341cb0ef41Sopenharmony_ci binding.leakHandle(); 351cb0ef41Sopenharmony_ci binding.leakHandle(0); 361cb0ef41Sopenharmony_ci binding.leakHandle(0x42); 371cb0ef41Sopenharmony_ci `, { eval: true }); 381cb0ef41Sopenharmony_ci} else { 391cb0ef41Sopenharmony_ci const child = cp.spawnSync(process.execPath, [__filename, 'child']); 401cb0ef41Sopenharmony_ci const stderr = child.stderr.toString(); 411cb0ef41Sopenharmony_ci 421cb0ef41Sopenharmony_ci assert.strictEqual(child.stdout.toString(), ''); 431cb0ef41Sopenharmony_ci 441cb0ef41Sopenharmony_ci const lines = stderr.split('\n'); 451cb0ef41Sopenharmony_ci 461cb0ef41Sopenharmony_ci let state = 'initial'; 471cb0ef41Sopenharmony_ci 481cb0ef41Sopenharmony_ci // Parse output that is formatted like this: 491cb0ef41Sopenharmony_ci 501cb0ef41Sopenharmony_ci // uv loop at [0x559b65ed5770] has open handles: 511cb0ef41Sopenharmony_ci // [0x7f2de0018430] timer (active) 521cb0ef41Sopenharmony_ci // Close callback: 0x7f2df31de220 CloseCallback(uv_handle_s*) [...] 531cb0ef41Sopenharmony_ci // Data: 0x7f2df33df140 example_instance [...] 541cb0ef41Sopenharmony_ci // (First field): 0x7f2df33dedc0 vtable for ExampleOwnerClass [...] 551cb0ef41Sopenharmony_ci // [0x7f2de000b870] timer 561cb0ef41Sopenharmony_ci // Close callback: 0x7f2df31de220 CloseCallback(uv_handle_s*) [...] 571cb0ef41Sopenharmony_ci // Data: (nil) 581cb0ef41Sopenharmony_ci // [0x7f2de000b910] timer 591cb0ef41Sopenharmony_ci // Close callback: 0x7f2df31de220 CloseCallback(uv_handle_s*) [...] 601cb0ef41Sopenharmony_ci // Data: 0x42 611cb0ef41Sopenharmony_ci // uv loop at [0x559b65ed5770] has 3 open handles in total 621cb0ef41Sopenharmony_ci 631cb0ef41Sopenharmony_ci function isGlibc() { 641cb0ef41Sopenharmony_ci try { 651cb0ef41Sopenharmony_ci const lddOut = spawnSync('ldd', [process.execPath]).stdout; 661cb0ef41Sopenharmony_ci const libcInfo = lddOut.toString().split('\n').map( 671cb0ef41Sopenharmony_ci (line) => line.match(/libc\.so.+=>\s*(\S+)\s/)).filter((info) => info); 681cb0ef41Sopenharmony_ci if (libcInfo.length === 0) 691cb0ef41Sopenharmony_ci return false; 701cb0ef41Sopenharmony_ci const nmOut = spawnSync('nm', ['-D', libcInfo[0][1]]).stdout; 711cb0ef41Sopenharmony_ci if (/gnu_get_libc_version/.test(nmOut)) 721cb0ef41Sopenharmony_ci return true; 731cb0ef41Sopenharmony_ci } catch { 741cb0ef41Sopenharmony_ci return false; 751cb0ef41Sopenharmony_ci } 761cb0ef41Sopenharmony_ci } 771cb0ef41Sopenharmony_ci 781cb0ef41Sopenharmony_ci 791cb0ef41Sopenharmony_ci if (!(common.isFreeBSD || 801cb0ef41Sopenharmony_ci common.isAIX || 811cb0ef41Sopenharmony_ci common.isIBMi || 821cb0ef41Sopenharmony_ci (common.isLinux && !isGlibc()) || 831cb0ef41Sopenharmony_ci common.isWindows)) { 841cb0ef41Sopenharmony_ci assert(stderr.includes('ExampleOwnerClass'), stderr); 851cb0ef41Sopenharmony_ci assert(stderr.includes('CloseCallback'), stderr); 861cb0ef41Sopenharmony_ci assert(stderr.includes('example_instance'), stderr); 871cb0ef41Sopenharmony_ci } 881cb0ef41Sopenharmony_ci 891cb0ef41Sopenharmony_ci while (lines.length > 0) { 901cb0ef41Sopenharmony_ci const line = lines.shift().trim(); 911cb0ef41Sopenharmony_ci 921cb0ef41Sopenharmony_ci switch (state) { 931cb0ef41Sopenharmony_ci case 'initial': 941cb0ef41Sopenharmony_ci assert.match(line, /^uv loop at \[.+\] has open handles:$/); 951cb0ef41Sopenharmony_ci state = 'handle-start'; 961cb0ef41Sopenharmony_ci break; 971cb0ef41Sopenharmony_ci case 'handle-start': 981cb0ef41Sopenharmony_ci if (/^uv loop at \[.+\] has \d+ open handles in total$/.test(line)) { 991cb0ef41Sopenharmony_ci state = 'assertion-failure'; 1001cb0ef41Sopenharmony_ci break; 1011cb0ef41Sopenharmony_ci } 1021cb0ef41Sopenharmony_ci assert.match(line, /^\[.+\] timer( \(active\))?$/); 1031cb0ef41Sopenharmony_ci state = 'close-callback'; 1041cb0ef41Sopenharmony_ci break; 1051cb0ef41Sopenharmony_ci case 'close-callback': 1061cb0ef41Sopenharmony_ci assert.match(line, /^Close callback:/); 1071cb0ef41Sopenharmony_ci state = 'data'; 1081cb0ef41Sopenharmony_ci break; 1091cb0ef41Sopenharmony_ci case 'data': 1101cb0ef41Sopenharmony_ci assert.match(line, /^Data: .+$/); 1111cb0ef41Sopenharmony_ci state = 'maybe-first-field'; 1121cb0ef41Sopenharmony_ci break; 1131cb0ef41Sopenharmony_ci case 'maybe-first-field': 1141cb0ef41Sopenharmony_ci if (!/^\(First field\)/.test(line)) { 1151cb0ef41Sopenharmony_ci lines.unshift(line); 1161cb0ef41Sopenharmony_ci } 1171cb0ef41Sopenharmony_ci state = 'handle-start'; 1181cb0ef41Sopenharmony_ci break; 1191cb0ef41Sopenharmony_ci case 'assertion-failure': 1201cb0ef41Sopenharmony_ci assert.match(line, /Assertion .+ failed/); 1211cb0ef41Sopenharmony_ci state = 'done'; 1221cb0ef41Sopenharmony_ci break; 1231cb0ef41Sopenharmony_ci case 'done': 1241cb0ef41Sopenharmony_ci break; 1251cb0ef41Sopenharmony_ci } 1261cb0ef41Sopenharmony_ci } 1271cb0ef41Sopenharmony_ci 1281cb0ef41Sopenharmony_ci assert.strictEqual(state, 'done'); 1291cb0ef41Sopenharmony_ci} 130