Skip to content

Commit

Permalink
lib: add diagnostics_channel events to module loading
Browse files Browse the repository at this point in the history
  • Loading branch information
Stephen Belanger committed Dec 15, 2022
1 parent f00969e commit 99f0481
Show file tree
Hide file tree
Showing 6 changed files with 253 additions and 9 deletions.
8 changes: 7 additions & 1 deletion lib/internal/modules/cjs/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ const {
packageImportsResolve
} = require('internal/modules/esm/resolve');

const dc = require('diagnostics_channel');
const onLoad = dc.tracingChannel('module.require');

const isWindows = process.platform === 'win32';

const relativeResolveCache = ObjectCreate(null);
Expand Down Expand Up @@ -1102,7 +1105,10 @@ Module.prototype.require = function(id) {
}
requireDepth++;
try {
return Module._load(id, this, /* isMain */ false);
return onLoad.traceSync(Module._load, {
parentFilename: this.filename,
id,
}, Module, id, this, /* isMain */ false);
} finally {
requireDepth--;
}
Expand Down
28 changes: 20 additions & 8 deletions lib/internal/modules/esm/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ function getTranslators() {
}
const { getOptionValue } = require('internal/options');

const dc = require('diagnostics_channel');
const onLoad = dc.tracingChannel('module.import');

/**
* @typedef {object} ExportedHooks
* @property {Function} globalPreload Global preload hook.
Expand Down Expand Up @@ -374,14 +377,18 @@ class ESMLoader {
return module;
};
const ModuleJob = require('internal/modules/esm/module_job');
const job = new ModuleJob(
this, url, undefined, evalInstance, false, false);
this.moduleMap.set(url, undefined, job);
const { module } = await job.run();
const namespace = await onLoad.tracePromise(async () => {
const job = new ModuleJob(
this, url, undefined, evalInstance, false, false);
this.moduleMap.set(url, undefined, job);

const { module } = await job.run();
return module.getNamespace();
}, { parentURL: '<eval>', url });

return {
__proto__: null,
namespace: module.getNamespace(),
namespace,
};
}

Expand Down Expand Up @@ -513,9 +520,14 @@ class ESMLoader {
const jobs = new Array(count);

for (let i = 0; i < count; i++) {
jobs[i] = this.getModuleJob(specifiers[i], parentURL, importAssertions)
.then((job) => job.run())
.then(({ module }) => module.getNamespace());
jobs[i] = onLoad.tracePromise(async () => {
const job = await this.getModuleJob(specifiers[i], parentURL, importAssertions);
const { module } = await job.run();
return module.getNamespace();
}, {
parentURL,
url: specifiers[i]
});
}

const namespaces = await SafePromiseAllReturnArrayLike(jobs);
Expand Down
65 changes: 65 additions & 0 deletions test/parallel/test-diagnostics-channel-module-import-error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const dc = require('diagnostics_channel');

const trace = dc.tracingChannel('module.import');
const events = [];
let lastEvent;

function track(name) {
return (event) => {
// Verify every event after the first is the same object
if (events.length) {
assert.strictEqual(event, lastEvent);
}
lastEvent = event;

events.push({ name, ...event });
}
}

trace.subscribe({
start: common.mustCall(track('start')),
end: common.mustCall(track('end')),
asyncStart: common.mustCall(track('asyncStart')),
asyncEnd: common.mustCall(track('asyncEnd')),
error: common.mustCall(track('error')),
});

import('does-not-exist').then(
common.mustNotCall(),
common.mustCall((error) => {
// Verify order and contents of each event
assert.deepStrictEqual(events, [
{
name: 'start',
parentURL: `file://${module.filename}`,
url: 'does-not-exist',
},
{
name: 'end',
parentURL: `file://${module.filename}`,
url: 'does-not-exist',
},
{
name: 'error',
parentURL: `file://${module.filename}`,
url: 'does-not-exist',
error,
},
{
name: 'asyncStart',
parentURL: `file://${module.filename}`,
url: 'does-not-exist',
error,
},
{
name: 'asyncEnd',
parentURL: `file://${module.filename}`,
url: 'does-not-exist',
error,
},
]);
})
);
59 changes: 59 additions & 0 deletions test/parallel/test-diagnostics-channel-module-import.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const dc = require('diagnostics_channel');

const trace = dc.tracingChannel('module.import');
const events = [];
let lastEvent;

function track (name) {
return (event) => {
// Verify every event after the first is the same object
if (events.length) {
assert.strictEqual(event, lastEvent);
}
lastEvent = event;

events.push({ name, ...event });
}
}

trace.subscribe({
start: common.mustCall(track('start')),
end: common.mustCall(track('end')),
asyncStart: common.mustCall(track('asyncStart')),
asyncEnd: common.mustCall(track('asyncEnd')),
error: common.mustNotCall(track('error')),
});

import('http').then(
common.mustCall((result) => {
// Verify order and contents of each event
assert.deepStrictEqual(events, [
{
name: 'start',
parentURL: `file://${module.filename}`,
url: 'http',
},
{
name: 'end',
parentURL: `file://${module.filename}`,
url: 'http',
},
{
name: 'asyncStart',
parentURL: `file://${module.filename}`,
url: 'http',
result,
},
{
name: 'asyncEnd',
parentURL: `file://${module.filename}`,
url: 'http',
result,
},
]);
}),
common.mustNotCall(),
);
56 changes: 56 additions & 0 deletions test/parallel/test-diagnostics-channel-module-require-error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const dc = require('diagnostics_channel');

const trace = dc.tracingChannel('module.require');
const events = [];
let lastEvent;

function track (name) {
return (event) => {
// Verify every event after the first is the same object
if (events.length) {
assert.strictEqual(event, lastEvent);
}
lastEvent = event;

events.push({ name, ...event });
}
}

trace.subscribe({
start: common.mustCall(track('start')),
end: common.mustCall(track('end')),
asyncStart: common.mustNotCall(track('asyncStart')),
asyncEnd: common.mustNotCall(track('asyncEnd')),
error: common.mustCall(track('error')),
});

let error;
try {
require('does-not-exist');
} catch (err) {
error = err;
}

// Verify order and contents of each event
assert.deepStrictEqual(events, [
{
name: 'start',
parentFilename: module.filename,
id: 'does-not-exist',
},
{
name: 'error',
parentFilename: module.filename,
id: 'does-not-exist',
error,
},
{
name: 'end',
parentFilename: module.filename,
id: 'does-not-exist',
error,
},
]);
46 changes: 46 additions & 0 deletions test/parallel/test-diagnostics-channel-module-require.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const dc = require('diagnostics_channel');

const trace = dc.tracingChannel('module.require');
const events = [];
let lastEvent;

function track (name) {
return (event) => {
// Verify every event after the first is the same object
if (events.length) {
assert.strictEqual(event, lastEvent);
}
lastEvent = event;

events.push({ name, ...event });
}
}

trace.subscribe({
start: common.mustCall(track('start')),
end: common.mustCall(track('end')),
asyncStart: common.mustNotCall(track('asyncStart')),
asyncEnd: common.mustNotCall(track('asyncEnd')),
error: common.mustNotCall(track('error')),
});

const result = require('http');

// Verify order and contents of each event
assert.deepStrictEqual(events, [
{
name: 'start',
parentFilename: module.filename,
id: 'http',
},
{
name: 'end',
parentFilename: module.filename,
id: 'http',
result,
},
]);

0 comments on commit 99f0481

Please sign in to comment.