Skip to content

Commit

Permalink
Extract relevant part of stack and make source-tracking optional
Browse files Browse the repository at this point in the history
Signed-off-by: Darshan Sen <raisinten@gmail.com>
  • Loading branch information
RaisinTen committed Nov 6, 2024
1 parent a475e89 commit 64ff5fe
Show file tree
Hide file tree
Showing 7 changed files with 42 additions and 21 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ process.on("beforeExit", () => {
});

// Enables tracking require() calls.
trackRequires(true);
trackRequires(true, { trackSource: true });

// The assert module takes milliseconds to load, so it would be distinctly
// visible in the performance trace.
Expand Down
6 changes: 4 additions & 2 deletions docs/api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ Call `traceEvents.getEvents()` to get the PerformanceEntry objects in the [Trace
]
```

## `trackRequires(bool)` (only available in [CommonJS](https://nodejs.org/api/modules.html#modules-commonjs-modules))
## `trackRequires(switch, options)` (only available in [CommonJS](https://nodejs.org/api/modules.html#modules-commonjs-modules))

Call `trackRequires(true)` to enable tracking `require()`s and call `trackRequires(false)` to disable tracking `require()`s.
- `switch` (**bool**) - Call `trackRequires(true)` to enable tracking `require()`s and call `trackRequires(false)` to disable tracking `require()`s.
- `options` (**object**)
- `trackSource` (**bool**) - Call `trackRequires(true, { trackSource: true })` to enable tracking `require()`s and also capture the source locations. This creates a throw-away error object and parses the `stack` property, so it introduces an overhead.
2 changes: 1 addition & 1 deletion docs/examples/tracing-requires/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Before the process exits, the `TraceEvents` object is destroyed and the trace ev

https://github.com/RaisinTen/perftrace/blob/7cb1ce34d19ed8816e15094b3ef4fd03bb157c76/docs/examples/tracing-requires/index.js#L6-L10

The `require()` calls can be tracked by passing `true` to `trackRequires()` like it is being done in:
The `require()` calls can be tracked by calling `trackRequires()` like it is being done in:

https://github.com/RaisinTen/perftrace/blob/7cb1ce34d19ed8816e15094b3ef4fd03bb157c76/docs/examples/tracing-requires/index.js#L12

Expand Down
2 changes: 1 addition & 1 deletion docs/examples/tracing-requires/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ process.on("beforeExit", () => {
writeFileSync("events.json", JSON.stringify(events));
});

trackRequires(true);
trackRequires(true, { trackSource: true });

// Express Hello world example
// Refs: https://expressjs.com/en/starter/hello-world.html
Expand Down
Binary file modified docs/examples/tracing-requires/perfetto.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 15 additions & 12 deletions index.cjs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
const { PerformanceObserver, performance } = require('node:perf_hooks');

// Refs: https://stackoverflow.com/a/47105238
function thisLine() {
const e = new Error();
const regex = /\((.*):(\d+):(\d+)\)$/;
const match = regex.exec(e.stack.split("\n")[4]);
return {
filepath: match[1],
line: Number(match[2]),
column: Number(match[3]),
};
function getSource() {
const error = new Error();
const stack = error.stack.split("\n");
// Ignore the first 3 elements of the error stack to get the relevant source:
// Error
// at getSource (.../perftrace/index.cjs:...:...)
// at Module.require (.../perftrace/index.cjs:...:...)
// ...
const source = stack.slice(3);
return source;
}

class TraceEvents {
Expand Down Expand Up @@ -50,10 +50,13 @@ class TraceEvents {
const Module = require('module');
const originalRequire = Module.prototype.require;

function trackRequires(shouldTrackRequires) {
function trackRequires(shouldTrackRequires, options) {
if (shouldTrackRequires) {
Module.prototype.require = function () {
const source = thisLine();
let source = null;
if (options?.trackSource) {
source = getSource();
}
performance.mark(`require("${arguments[0]}")`);
const ret = originalRequire.apply(this, arguments);
performance.measure(
Expand Down
24 changes: 20 additions & 4 deletions test/node-test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ describe("track requires", async () => {
traceEvents = new TraceEvents();
trackRequires(true);
require("node:tls");
trackRequires(true, { trackSource: true });
require("node:os");
trackRequires(true, { trackSource: false });
require("node:async_hooks");
trackRequires(false);
require("node:net");

Expand All @@ -83,18 +87,30 @@ describe("track requires", async () => {
const events = traceEvents.getEvents();
const requireTls = events.find((element) => element.name === `require("node:tls")`);
ok(requireTls);
const requireOs = events.find((element) => element.name === `require("node:os")`);
ok(requireOs);
const requireAsyncHooks = events.find((element) => element.name === `require("node:async_hooks")`);
ok(requireAsyncHooks);
const requireNet = events.find((element) => element.name === `require("node:net")`);
ok(!requireNet);
});

test("source location", () => {
const events = traceEvents.getEvents();

const requireTls = events.find((element) => element.name === `require("node:tls")`);
ok(requireTls);
ok(requireTls.args);
strictEqual(requireTls.args.filepath, __filename);
strictEqual(requireTls.args.line, 75);
strictEqual(requireTls.args.column, 5);
strictEqual(requireTls.args, null);

const requireOs = events.find((element) => element.name === `require("node:os")`);
ok(requireOs);
ok(requireOs.args);
ok(requireOs.args.length > 5);
ok(requireOs.args[1].includes(__filename));

const requireAsyncHooks = events.find((element) => element.name === `require("node:async_hooks")`);
ok(requireAsyncHooks);
strictEqual(requireAsyncHooks.args, null);
});

after(() => {
Expand Down

0 comments on commit 64ff5fe

Please sign in to comment.