Skip to content

Commit

Permalink
Add working regression test for #2564
Browse files Browse the repository at this point in the history
The previous attempt at a test didn't fail because it didn't actually exercise the inspector protocol. If it had, it would have encounterd a WebSocket error and failed.

This commit removes the previous test, uncomments out the profiling test, factors it out into a separate function, then uses that function to implement two separate test cases, one to actually test profiling, and one to test repeated inspector connections.
  • Loading branch information
harrishancock committed Aug 21, 2024
1 parent 9f455c1 commit 6d4211d
Showing 1 changed file with 62 additions and 59 deletions.
121 changes: 62 additions & 59 deletions src/workerd/server/tests/inspector/driver.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -54,68 +54,71 @@ async function connectInspector(port) {
});
}

// TODO(soon): This test reproduces a null pointer dereference in workerd (possibly the same issue
// as https://github.com/cloudflare/workerd/issues/2564), but the test doesn't fail. :(
test('Can repeatedly connect and disconnect to the inspector port', async () => {
for (let i = 0; i < 5; ++i) {
let inspectorClient = await connectInspector(
await workerd.getListenInspectorPort()
async function profileAndExpectDeriveBitsFrames(inspectorClient) {
// Enable and start profiling.
await inspectorClient.Profiler.enable();
await inspectorClient.Profiler.start();

// Drive the worker with a test request. A single one is sufficient.
let httpPort = await workerd.getListenPort('http');
const response = await fetch(`http://localhost:${httpPort}`);
await response.arrayBuffer();

// Stop and disable profiling.
const profile = await inspectorClient.Profiler.stop();
await inspectorClient.Profiler.disable();

// Figure out which function name was most frequently sampled.
let hitCountMap = new Map();

for (let node of profile.profile.nodes) {
if (hitCountMap.get(node.callFrame.functionName) === undefined) {
hitCountMap.set(node.callFrame.functionName, 0);
}
hitCountMap.set(
node.callFrame.functionName,
hitCountMap.get(node.callFrame.functionName) + node.hitCount
);
}

// Drive the worker with a test request.
let httpPort = await workerd.getListenPort('http');
const response = await fetch(`http://localhost:${httpPort}`);
let body = await response.arrayBuffer();
console.log(body);
let max = {
name: null,
count: 0,
};

await inspectorClient.close();
for (let [name, count] of hitCountMap) {
if (count > max.count) {
max.name = name;
max.count = count;
}
}

// The most CPU-intensive function our test script runs is `deriveBits()`, so we expect that to be
// the most frequently sampled function.
assert.equal(max.name, 'deriveBits');
assert.notEqual(max.count, 0);
}

// Regression test for https://github.com/cloudflare/workerd/issues/1754.
//
// At one time, workerd produced only "(program)" frames.
test('Profiler mostly sees deriveBits() frames', async () => {
let inspectorClient = await connectInspector(
await workerd.getListenInspectorPort()
);
await profileAndExpectDeriveBitsFrames(inspectorClient);
await inspectorClient.close();
});

// TODO(soon): Re-enable once https://github.com/cloudflare/workerd/issues/2564 is solved.
// test("Profiler mostly sees deriveBits() frames", async () => {
// let inspectorClient = await connectInspector(await workerd.getListenInspectorPort());

// // Enable and start profiling.
// await inspectorClient.Profiler.enable();
// await inspectorClient.Profiler.start();

// // Drive the worker with a test request. A single one is sufficient.
// let httpPort = await workerd.getListenPort("http");
// const response = await fetch(`http://localhost:${httpPort}`);
// await response.arrayBuffer();

// // Stop and disable profiling.
// const profile = await inspectorClient.Profiler.stop();
// await inspectorClient.Profiler.disable();

// // Figure out which function name was most frequently sampled.
// let hitCountMap = new Map();

// for (let node of profile.profile.nodes) {
// if (hitCountMap.get(node.callFrame.functionName) === undefined) {
// hitCountMap.set(node.callFrame.functionName, 0);
// }
// hitCountMap.set(node.callFrame.functionName,
// hitCountMap.get(node.callFrame.functionName) + node.hitCount);
// }

// let max = {
// name: null,
// count: 0,
// };

// for (let [name, count] of hitCountMap) {
// if (count > max.count) {
// max.name = name;
// max.count = count;
// }
// }

// // The most CPU-intensive function our test script runs is `deriveBits()`, so we expect that to be
// // the most frequently sampled function.
// assert.equal(max.name, "deriveBits");
// assert.notEqual(max.count, 0);

// await inspectorClient.close();
// });
// Regression test for https://github.com/cloudflare/workerd/issues/2564.
//
// At one time, workerd segfaulted on the second inspector connection.
test('Can repeatedly reconnect the inspector and profiling still works', async () => {
for (let i = 0; i < 4; ++i) {
let inspectorClient = await connectInspector(
await workerd.getListenInspectorPort()
);
await profileAndExpectDeriveBitsFrames(inspectorClient);
await inspectorClient.close();
}
});

0 comments on commit 6d4211d

Please sign in to comment.