"use strict";
const fs = require("fs");
const path = require("path");
const { EVENT_TYPES, parseTrace } = require("./trace_parser.js");
const {
buildWorkerSpans,
attachCpuSamples,
buildActiveTaskTimeline,
computeSchedulingDelays,
filterPointsOfInterest,
buildFlamegraphTree,
flattenFlamegraph,
buildFgData,
buildSpanData,
} = require("./trace_analysis.js");
async function main() {
const tracePath = process.argv[2] || path.join(__dirname, "demo-trace.bin");
if (!fs.existsSync(tracePath)) {
console.error(`Trace file not found: ${tracePath}`);
process.exit(1);
}
function fail(msg) {
console.log(`✗ ${msg}`);
process.exit(1);
}
function pass(msg) {
console.log(`✓ ${msg}`);
}
const trace = await parseTrace(fs.readFileSync(tracePath));
const evts = trace.events;
const wSet = new Set();
evts.forEach((e) => {
if (
e.eventType !== EVENT_TYPES.QueueSample &&
e.eventType !== EVENT_TYPES.WakeEvent
)
wSet.add(e.workerId);
});
const workerIds = [...wSet].sort((a, b) => a - b);
let minTs = evts[0].timestamp;
let maxTs = evts[evts.length - 1].timestamp;
for (const e of evts) {
if (e.timestamp < minTs) minTs = e.timestamp;
if (e.timestamp > maxTs) maxTs = e.timestamp;
}
const { workerSpans, perWorker, queueSamples, workerQueueSamples, maxLocalQueue, wakesByTask, wakesByWorker } = buildWorkerSpans(
evts,
workerIds,
maxTs
);
function testPollsHaveValidRange() {
for (const w of workerIds) {
for (const p of workerSpans[w].polls) {
if (p.start > p.end)
fail(`Worker ${w}: poll start > end (${p.start} > ${p.end})`);
}
}
pass("All polls have start <= end");
}
function testNoOverlappingPolls() {
for (const w of workerIds) {
const polls = workerSpans[w].polls;
for (let i = 1; i < polls.length; i++) {
if (polls[i].start < polls[i - 1].end)
fail(`Worker ${w}: overlapping polls at index ${i}`);
}
}
pass("No overlapping polls on same worker");
}
function testActiveRatiosInRange() {
for (const w of workerIds) {
for (const a of workerSpans[w].actives) {
if (a.ratio < 0 || a.ratio > 1)
fail(`Worker ${w}: active ratio ${a.ratio} out of [0, 1]`);
}
}
pass("Active period ratios in [0, 1]");
}
function testParksHaveValidRange() {
for (const w of workerIds) {
for (const p of workerSpans[w].parks) {
if (p.start > p.end) fail(`Worker ${w}: park start > end`);
}
}
pass("All parks have start <= end");
}
function testQueueSamplesExist() {
if (queueSamples.length === 0) fail("No queue samples");
pass(`${queueSamples.length} queue samples`);
}
const cpuResult = attachCpuSamples(trace.cpuSamples, workerSpans);
function testAttachedSamplesWithinPollBounds() {
for (const w of workerIds) {
for (const p of workerSpans[w].polls) {
if (p.cpuSamples) {
for (const s of p.cpuSamples) {
if (s.timestamp < p.start || s.timestamp > p.end)
fail(
`Worker ${w}: cpu sample at ${s.timestamp} outside poll [${p.start}, ${p.end}]`
);
}
}
if (p.schedSamples) {
for (const s of p.schedSamples) {
if (s.timestamp < p.start || s.timestamp > p.end)
fail(
`Worker ${w}: sched sample at ${s.timestamp} outside poll [${p.start}, ${p.end}]`
);
}
}
}
}
pass("All attached samples fall within poll bounds");
}
function testCpuResultCounts() {
if (
cpuResult.pollsWithCpuSamples < 0 ||
cpuResult.pollsWithSchedSamples < 0
)
fail("Negative sample counts");
pass(
`${cpuResult.pollsWithCpuSamples} polls with cpu samples, ${cpuResult.pollsWithSchedSamples} with sched samples`
);
}
function testLocalQueueNonNegative() {
for (const w of workerIds) {
for (const s of workerQueueSamples[w]) {
if (s.local < 0) fail(`Worker ${w}: negative local queue ${s.local}`);
}
}
pass("All local queue depths non-negative");
}
function testMaxLocalQueue() {
if (maxLocalQueue < 1) fail(`maxLocalQueue ${maxLocalQueue} < 1`);
pass(`maxLocalQueue = ${maxLocalQueue}`);
}
const { activeTaskSamples, taskFirstPoll } = buildActiveTaskTimeline(
trace.taskSpawnTimes,
trace.taskTerminateTimes
);
function testTimelineSorted() {
for (let i = 1; i < activeTaskSamples.length; i++) {
if (activeTaskSamples[i].t < activeTaskSamples[i - 1].t)
fail(`Timeline not sorted at index ${i}`);
}
pass("Timeline sorted by timestamp");
}
function testCountNonNegative() {
for (const s of activeTaskSamples) {
if (s.count < 0) fail(`Negative task count ${s.count}`);
}
pass("Task counts non-negative");
}
function testWakesByTaskSorted() {
for (const arr of Object.values(wakesByTask)) {
for (let i = 1; i < arr.length; i++) {
if (arr[i].timestamp < arr[i - 1].timestamp)
fail("wakesByTask not sorted");
}
}
pass("wakesByTask arrays sorted by timestamp");
}
function testWakesByWorkerSorted() {
for (const arr of Object.values(wakesByWorker)) {
for (let i = 1; i < arr.length; i++) {
if (arr[i].timestamp < arr[i - 1].timestamp)
fail("wakesByWorker not sorted");
}
}
pass("wakesByWorker arrays sorted by timestamp");
}
function testWakeCountsConsistent() {
let taskTotal = 0;
for (const arr of Object.values(wakesByTask)) taskTotal += arr.length;
let workerTotal = 0;
for (const arr of Object.values(wakesByWorker)) workerTotal += arr.length;
if (taskTotal !== workerTotal)
fail(
`wakesByTask total ${taskTotal} != wakesByWorker total ${workerTotal}`
);
pass(`${taskTotal} wake events indexed consistently`);
}
const schedDelays = computeSchedulingDelays(
workerSpans,
workerIds,
wakesByTask
);
function testDelaysPositive() {
for (const sd of schedDelays) {
if (sd.delay <= 0) fail(`Non-positive delay: ${sd.delay}`);
}
pass("All delays positive");
}
function testDelaysBounded() {
for (const sd of schedDelays) {
if (sd.delay >= 1e9) fail(`Delay >= 1s: ${sd.delay}`);
}
pass("All delays < 1s");
}
function testWakeBeforePoll() {
for (const sd of schedDelays) {
if (sd.wakeTime >= sd.pollTime)
fail(`wakeTime ${sd.wakeTime} >= pollTime ${sd.pollTime}`);
}
pass("wakeTime < pollTime for all delays");
}
function testDelaysSorted() {
for (let i = 1; i < schedDelays.length; i++) {
if (schedDelays[i].wakeTime < schedDelays[i - 1].wakeTime)
fail("schedDelays not sorted by wakeTime");
}
pass("schedDelays sorted by wakeTime");
}
function testLongPollFilter() {
const pois = filterPointsOfInterest(
"long-poll",
workerSpans,
workerIds,
schedDelays,
{ hasSchedWait: trace.hasSchedWait }
);
if (pois.length === 0) fail("No long-poll points of interest found");
for (const p of pois) {
if (p.type !== "long-poll") fail(`Wrong type: ${p.type}`);
if (p.value <= 1) fail(`long-poll value ${p.value} <= 1ms`);
}
pass(`long-poll filter: ${pois.length} results, all > 1ms`);
}
function testCpuSampledFilter() {
const pois = filterPointsOfInterest(
"cpu-sampled",
workerSpans,
workerIds,
schedDelays,
{ hasSchedWait: trace.hasSchedWait }
);
if (pois.length === 0) fail("No cpu-sampled points of interest found");
for (const p of pois) {
if (p.type !== "cpu-sampled") fail(`Wrong type: ${p.type}`);
if (p.value <= 0) fail(`cpu-sampled value ${p.value} <= 0`);
}
pass(`cpu-sampled filter: ${pois.length} results, all with samples`);
}
function testWakeDelayFilter() {
const pois = filterPointsOfInterest(
"wake-delay",
workerSpans,
workerIds,
schedDelays,
{ hasSchedWait: trace.hasSchedWait }
);
if (pois.length === 0) fail("No wake-delay points of interest found");
for (const p of pois) {
if (p.type !== "wake-delay") fail(`Wrong type: ${p.type}`);
if (p.value <= 100) fail(`wake-delay value ${p.value} <= 100µs`);
}
pass(`wake-delay filter: ${pois.length} results, all > 100µs`);
}
function testSortByWorst() {
const pois = filterPointsOfInterest(
"long-poll",
workerSpans,
workerIds,
schedDelays,
{ hasSchedWait: trace.hasSchedWait, sortByWorst: true }
);
for (let i = 1; i < pois.length; i++) {
if (pois[i].value > pois[i - 1].value) fail("sortByWorst not descending");
}
pass("sortByWorst produces descending order");
}
function testFlamegraphTree() {
const cpuSamples = trace.cpuSamples.filter((s) => s.source !== 1);
if (cpuSamples.length === 0) fail("No CPU samples found");
const root = buildFlamegraphTree(cpuSamples, trace.callframeSymbols);
if (root.count !== cpuSamples.length)
fail(`Root count ${root.count} != sample count ${cpuSamples.length}`);
pass(`Root count matches sample count (${root.count})`);
}
function testFlattenFlamegraph() {
const cpuSamples = trace.cpuSamples.filter((s) => s.source !== 1);
if (cpuSamples.length === 0) fail("No CPU samples found");
const root = buildFlamegraphTree(cpuSamples, trace.callframeSymbols);
const { nodes, maxDepth } = flattenFlamegraph(root, cpuSamples.length);
for (const n of nodes) {
if (n.x < 0 || n.x >= 1) fail(`Node x=${n.x} out of [0, 1)`);
if (n.w <= 0) fail(`Node w=${n.w} <= 0`);
}
if (maxDepth < 0) fail(`maxDepth ${maxDepth} < 0`);
pass(`${nodes.length} flamegraph nodes, maxDepth=${maxDepth}`);
}
function testBuildFgData() {
const cpuSamples = trace.cpuSamples.filter((s) => s.source !== 1);
if (cpuSamples.length === 0) fail("No CPU samples found");
const data = buildFgData(cpuSamples, trace.callframeSymbols);
if (!data) fail("buildFgData returned null for non-empty samples");
if (data.totalSamples !== cpuSamples.length)
fail(`totalSamples ${data.totalSamples} != ${cpuSamples.length}`);
pass(
`buildFgData: ${data.nodes.length} nodes, ${data.totalSamples} samples`
);
}
function testBuildFgDataEmpty() {
const data = buildFgData([], trace.callframeSymbols);
if (data !== null) fail("buildFgData should return null for empty samples");
pass("buildFgData returns null for empty samples");
}
function testBuildSpanDataPairing() {
const customEvents = [
{ name: "SpanEnterEvent", timestamp: 1000, fields: { worker_id: 0, span_id: 1, parent_span_id: null, span_name: "handle_request", fields: { user_id: "42" } } },
{ name: "SpanEnterEvent", timestamp: 1100, fields: { worker_id: 0, span_id: 2, parent_span_id: 1, span_name: "redis_get", fields: { key: "foo" } } },
{ name: "SpanExitEvent", timestamp: 1200, fields: { worker_id: 0, span_id: 2, span_name: "redis_get", fields: { key: "foo" } } },
{ name: "SpanExitEvent", timestamp: 1300, fields: { worker_id: 0, span_id: 1, span_name: "handle_request", fields: { user_id: "42" } } },
];
const { spansByWorker, spanMeta } = buildSpanData(customEvents);
const w0 = spansByWorker[0] || [];
if (w0.length !== 2) fail(`Expected 2 span intervals on worker 0, got ${w0.length}`);
if (w0[0].spanName !== "handle_request" && w0[1].spanName !== "handle_request")
fail("Missing handle_request span");
if (w0[0].spanName !== "redis_get" && w0[1].spanName !== "redis_get")
fail("Missing redis_get span");
if (w0[0].start > w0[1].start) fail("Spans not sorted by start time");
const redis = w0.find(s => s.spanName === "redis_get");
if (redis.start !== 1100 || redis.end !== 1200) fail("redis_get timing wrong");
if (!spanMeta.has(1) || !spanMeta.has(2)) fail("spanMeta missing entries");
pass(`${w0.length} span intervals paired correctly`);
}
function testBuildSpanDataParent() {
const customEvents = [
{ name: "SpanEnterEvent", timestamp: 1000, fields: { worker_id: 0, span_id: 10, parent_span_id: null, span_name: "root", fields: {} } },
{ name: "SpanEnterEvent", timestamp: 1100, fields: { worker_id: 0, span_id: 20, parent_span_id: 10, span_name: "child", fields: {} } },
{ name: "SpanExitEvent", timestamp: 1200, fields: { worker_id: 0, span_id: 20, span_name: "child", fields: {} } },
{ name: "SpanExitEvent", timestamp: 1300, fields: { worker_id: 0, span_id: 10, span_name: "root", fields: {} } },
];
const { spansByWorker } = buildSpanData(customEvents);
const child = spansByWorker[0].find(s => s.spanName === "child");
if (child.parentSpanId !== 10) fail(`Expected parentSpanId=10, got ${child.parentSpanId}`);
const root = spansByWorker[0].find(s => s.spanName === "root");
if (root.parentSpanId !== null) fail(`Expected root parentSpanId=null, got ${root.parentSpanId}`);
pass("Parent span IDs preserved correctly");
}
function testBuildSpanDataEmpty() {
const { spansByWorker, spanMeta } = buildSpanData([]);
if (Object.keys(spansByWorker).length !== 0) fail("Expected empty spansByWorker");
if (spanMeta.size !== 0) fail("Expected empty spanMeta");
pass("Empty input produces empty output");
}
function testBuildSpanDataDepth() {
const customEvents = [
{ name: "SpanEnterEvent", timestamp: 1000, fields: { worker_id: 0, span_id: 1, parent_span_id: null, span_name: "root", fields: {} } },
{ name: "SpanEnterEvent", timestamp: 1100, fields: { worker_id: 0, span_id: 2, parent_span_id: 1, span_name: "mid", fields: {} } },
{ name: "SpanEnterEvent", timestamp: 1200, fields: { worker_id: 0, span_id: 3, parent_span_id: 2, span_name: "leaf", fields: {} } },
{ name: "SpanExitEvent", timestamp: 1300, fields: { worker_id: 0, span_id: 3, span_name: "leaf", fields: {} } },
{ name: "SpanExitEvent", timestamp: 1400, fields: { worker_id: 0, span_id: 2, span_name: "mid", fields: {} } },
{ name: "SpanExitEvent", timestamp: 1500, fields: { worker_id: 0, span_id: 1, span_name: "root", fields: {} } },
];
const { spansByWorker, maxDepth } = buildSpanData(customEvents);
const spans = spansByWorker[0];
const root = spans.find(s => s.spanName === "root");
const mid = spans.find(s => s.spanName === "mid");
const leaf = spans.find(s => s.spanName === "leaf");
if (root.depth !== 0) fail(`root depth=${root.depth}, expected 0`);
if (mid.depth !== 1) fail(`mid depth=${mid.depth}, expected 1`);
if (leaf.depth !== 2) fail(`leaf depth=${leaf.depth}, expected 2`);
if (maxDepth !== 2) fail(`maxDepth=${maxDepth}, expected 2`);
pass("Depth computed correctly for 3-level nesting");
}
function testBuildSpanDataCycleDetection() {
const customEvents = [
{ name: "SpanEnterEvent", timestamp: 1000, fields: { worker_id: 0, span_id: 1, parent_span_id: 2, span_name: "a", fields: {} } },
{ name: "SpanEnterEvent", timestamp: 1100, fields: { worker_id: 0, span_id: 2, parent_span_id: 1, span_name: "b", fields: {} } },
{ name: "SpanExitEvent", timestamp: 1200, fields: { worker_id: 0, span_id: 2, span_name: "b", fields: {} } },
{ name: "SpanExitEvent", timestamp: 1300, fields: { worker_id: 0, span_id: 1, span_name: "a", fields: {} } },
];
const { spansByWorker } = buildSpanData(customEvents);
if (!spansByWorker[0] || spansByWorker[0].length !== 2) fail("Expected 2 spans");
pass("Cyclic parent chain does not stack overflow");
}
function testBuildSpanDataRecycledId() {
const customEvents = [
{ name: "SpanEnterEvent", timestamp: 1000, fields: { worker_id: 0, span_id: 1, parent_span_id: null, span_name: "alpha", fields: {} } },
{ name: "SpanExitEvent", timestamp: 1100, fields: { worker_id: 0, span_id: 1, span_name: "alpha", fields: {} } },
{ name: "SpanEnterEvent", timestamp: 2000, fields: { worker_id: 0, span_id: 1, parent_span_id: null, span_name: "beta", fields: {} } },
{ name: "SpanExitEvent", timestamp: 2100, fields: { worker_id: 0, span_id: 1, span_name: "beta", fields: {} } },
{ name: "SpanEnterEvent", timestamp: 3000, fields: { worker_id: 0, span_id: 2, parent_span_id: 1, span_name: "child", fields: {} } },
{ name: "SpanExitEvent", timestamp: 3100, fields: { worker_id: 0, span_id: 2, span_name: "child", fields: {} } },
];
const { spansByWorker } = buildSpanData(customEvents);
const spans = spansByWorker[0];
if (spans.length !== 3) fail(`Expected 3 spans, got ${spans.length}`);
const alpha = spans.find(s => s.spanName === "alpha");
const beta = spans.find(s => s.spanName === "beta");
if (!alpha || !beta) fail("Missing alpha or beta span");
if (alpha.start !== 1000 || beta.start !== 2000) fail("Span intervals not distinct");
pass("Recycled span IDs produce separate intervals");
}
function testBuildSpanDataPerCallsiteSchema() {
const customEvents = [
{ name: "SpanEnter:myapp::handle:src/main.rs:10", timestamp: 1000, fields: { worker_id: 0, span_id: 1, parent_span_id: null, span_name: "handle", request_id: "abc-123" } },
{ name: "SpanExit:myapp::handle:src/main.rs:10", timestamp: 1100, fields: { worker_id: 0, span_id: 1, span_name: "handle", request_id: "abc-123" } },
];
const { spansByWorker } = buildSpanData(customEvents);
const spans = spansByWorker[0];
if (!spans || spans.length !== 1) fail(`Expected 1 span, got ${spans?.length}`);
if (spans[0].spanName !== "handle") fail(`Expected span name 'handle', got '${spans[0].spanName}'`);
if (spans[0].fields.request_id !== "abc-123") fail(`Expected request_id='abc-123', got '${spans[0].fields.request_id}'`);
if (spans[0].fields.worker_id) fail("worker_id should not be in user fields");
if (spans[0].fields.span_name) fail("span_name should not be in user fields");
pass("Per-callsite schema with typed fields parsed correctly");
}
function testBuildSpanDataUnmatched() {
const customEvents = [
{ name: "SpanEnter:app::a:f:1", timestamp: 1000, fields: { worker_id: 0, span_id: 1, parent_span_id: null, span_name: "a" } },
{ name: "SpanExit:app::a:f:1", timestamp: 1100, fields: { worker_id: 0, span_id: 1, span_name: "a" } },
{ name: "SpanEnter:app::b:f:2", timestamp: 1200, fields: { worker_id: 0, span_id: 2, parent_span_id: null, span_name: "b" } },
];
const { spansByWorker, unmatchedSpans } = buildSpanData(customEvents);
const matched = spansByWorker[0] || [];
if (matched.length !== 1) fail(`Expected 1 matched span, got ${matched.length}`);
if (!unmatchedSpans || unmatchedSpans.length !== 1) fail(`Expected 1 unmatched span, got ${unmatchedSpans?.length}`);
if (unmatchedSpans[0].spanName !== "b") fail(`Expected unmatched span 'b', got '${unmatchedSpans[0].spanName}'`);
if (unmatchedSpans[0].spanId !== 2) fail(`Expected unmatched spanId 2, got ${unmatchedSpans[0].spanId}`);
pass("Unmatched spans (enter without exit) detected correctly");
}
function testOpenPollStartDiscarded() {
const syntheticEvents = [
{ eventType: EVENT_TYPES.PollStart, timestamp: 1000, workerId: 0, taskId: 1, spawnLocId: null, spawnLoc: null, localQueue: 0 },
{ eventType: EVENT_TYPES.PollEnd, timestamp: 2000, workerId: 0 },
{ eventType: EVENT_TYPES.PollStart, timestamp: 3000, workerId: 0, taskId: 2, spawnLocId: null, spawnLoc: null, localQueue: 0 },
];
const syntheticMaxTs = 1_000_000; const result = buildWorkerSpans(syntheticEvents, [0], syntheticMaxTs);
const polls = result.workerSpans[0].polls;
if (polls.length !== 1) fail(`Expected 1 poll, got ${polls.length} — open PollStart was not discarded`);
if (polls[0].start !== 1000 || polls[0].end !== 2000) fail(`Unexpected poll range`);
pass("Open PollStart at trace end is discarded (no phantom long poll)");
}
console.log("\nbuildWorkerSpans:");
testOpenPollStartDiscarded();
testPollsHaveValidRange();
testNoOverlappingPolls();
testActiveRatiosInRange();
testParksHaveValidRange();
testQueueSamplesExist();
console.log("\nattachCpuSamples:");
testAttachedSamplesWithinPollBounds();
testCpuResultCounts();
console.log("\nextractLocalQueueSamples:");
testLocalQueueNonNegative();
testMaxLocalQueue();
console.log("\nbuildActiveTaskTimeline:");
testTimelineSorted();
testCountNonNegative();
console.log("\nindexWakeEvents:");
testWakesByTaskSorted();
testWakesByWorkerSorted();
testWakeCountsConsistent();
console.log("\ncomputeSchedulingDelays:");
testDelaysPositive();
testDelaysBounded();
testWakeBeforePoll();
testDelaysSorted();
console.log("\nfilterPointsOfInterest:");
testLongPollFilter();
testCpuSampledFilter();
testWakeDelayFilter();
testSortByWorst();
console.log("\nflamegraph:");
testFlamegraphTree();
testFlattenFlamegraph();
testBuildFgData();
testBuildFgDataEmpty();
console.log("\nbuildSpanData:");
testBuildSpanDataPairing();
testBuildSpanDataParent();
testBuildSpanDataEmpty();
testBuildSpanDataDepth();
testBuildSpanDataCycleDetection();
testBuildSpanDataRecycledId();
testBuildSpanDataPerCallsiteSchema();
testBuildSpanDataUnmatched();
console.log("\n✓ All analysis checks passed!");
}
main().catch((e) => {
console.error(e);
process.exit(1);
});