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