Skip to content

Commit 527498d

Browse files
committed
extract attatchCpuSamples
1 parent 5df0efc commit 527498d

2 files changed

Lines changed: 85 additions & 42 deletions

File tree

dial9-tokio-telemetry/trace_viewer/index.html

Lines changed: 3 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ <h3>⌨ Keyboard</h3>
477477
<script src="trace_parser.js"></script>
478478
<script>
479479
// Use the external parser
480-
const { EVENT_TYPES: ET, parseTrace, formatFrame, symbolizeChain, deduplicateSamples, buildWorkerSpans } = TraceParser;
480+
const { EVENT_TYPES: ET, parseTrace, formatFrame, symbolizeChain, deduplicateSamples, buildWorkerSpans, attachCpuSamples } = TraceParser;
481481

482482
function esc(s) {
483483
return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
@@ -760,42 +760,9 @@ <h3>⌨ Keyboard</h3>
760760
console.log(`Found ${queueSamples.length} global queue samples`);
761761

762762
// Attach CPU samples to poll spans they fall within
763-
// Use binary search: polls are sorted by start time per worker
764763
if (trace.cpuSamples.length > 0) {
765-
for (const sample of trace.cpuSamples) {
766-
const spans = workerSpans[sample.workerId];
767-
if (!spans) { sample.spawnLoc = null; continue; }
768-
if (sample.source !== 1) spans.cpuSampleTimes.push(sample.timestamp);
769-
const polls = spans.polls;
770-
const ts = sample.timestamp;
771-
// Binary search for rightmost poll with start <= ts
772-
let lo = 0, hi = polls.length - 1, found = false;
773-
while (lo <= hi) {
774-
const mid = (lo + hi) >> 1;
775-
if (polls[mid].start <= ts) { lo = mid + 1; } else { hi = mid - 1; }
776-
}
777-
// hi is now the index of the last poll with start <= ts
778-
if (hi >= 0 && ts <= polls[hi].end) {
779-
const poll = polls[hi];
780-
if (sample.source === 1) {
781-
(poll.schedSamples ??= []).push(sample);
782-
} else {
783-
(poll.cpuSamples ??= []).push(sample);
784-
}
785-
sample.spawnLoc = poll.spawnLoc;
786-
found = true;
787-
}
788-
if (!found) sample.spawnLoc = null;
789-
}
790-
let pollsWithSamples = 0;
791-
let pollsWithSched = 0;
792-
for (const w of workerIds) {
793-
for (const p of workerSpans[w].polls) {
794-
if (p.cpuSamples) pollsWithSamples++;
795-
if (p.schedSamples) pollsWithSched++;
796-
}
797-
}
798-
console.log(`CPU samples: ${trace.cpuSamples.length}, polls with cpu samples: ${pollsWithSamples}, polls with sched events: ${pollsWithSched}`);
764+
const { pollsWithCpuSamples, pollsWithSchedSamples } = attachCpuSamples(trace.cpuSamples, workerSpans);
765+
console.log(`CPU samples: ${trace.cpuSamples.length}, polls with cpu samples: ${pollsWithCpuSamples}, polls with sched events: ${pollsWithSchedSamples}`);
799766
}
800767

801768
// Per-worker local queue samples (from events that have local_queue)

dial9-tokio-telemetry/trace_viewer/trace_parser.js

Lines changed: 82 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -465,16 +465,26 @@
465465
*/
466466
function buildWorkerSpans(events, workerIds, maxTs) {
467467
const workerSpans = {};
468-
const openPoll = {}, openPark = {}, openUnpark = {};
468+
const openPoll = {},
469+
openPark = {},
470+
openUnpark = {};
469471
const openPollMeta = {};
470472
for (const w of workerIds) {
471-
workerSpans[w] = { polls: [], parks: [], actives: [], cpuSampleTimes: [] };
473+
workerSpans[w] = {
474+
polls: [],
475+
parks: [],
476+
actives: [],
477+
cpuSampleTimes: [],
478+
};
472479
}
473480

474481
// Group events by worker and sort per-worker by timestamp
475482
const perWorker = {};
476483
for (const e of events) {
477-
if (e.eventType !== EVENT_TYPES.QueueSample && e.eventType !== EVENT_TYPES.WakeEvent) {
484+
if (
485+
e.eventType !== EVENT_TYPES.QueueSample &&
486+
e.eventType !== EVENT_TYPES.WakeEvent
487+
) {
478488
(perWorker[e.workerId] ??= []).push(e);
479489
}
480490
}
@@ -486,10 +496,18 @@
486496
for (const e of wEvents) {
487497
if (e.eventType === EVENT_TYPES.PollStart) {
488498
openPoll[w] = e.timestamp;
489-
openPollMeta[w] = { taskId: e.taskId, spawnLocId: e.spawnLocId, spawnLoc: e.spawnLoc };
499+
openPollMeta[w] = {
500+
taskId: e.taskId,
501+
spawnLocId: e.spawnLocId,
502+
spawnLoc: e.spawnLoc,
503+
};
490504
} else if (e.eventType === EVENT_TYPES.PollEnd) {
491505
if (openPoll[w] != null) {
492-
const meta = openPollMeta[w] || { taskId: 0, spawnLocId: 0, spawnLoc: null };
506+
const meta = openPollMeta[w] || {
507+
taskId: 0,
508+
spawnLocId: 0,
509+
spawnLoc: null,
510+
};
493511
workerSpans[w].polls.push({
494512
start: openPoll[w],
495513
end: e.timestamp,
@@ -504,7 +522,8 @@
504522
if (openUnpark[w] != null) {
505523
const wallDelta = e.timestamp - openUnpark[w].timestamp;
506524
const cpuDelta = e.cpuTime - openUnpark[w].cpuTime;
507-
const ratio = wallDelta > 0 ? Math.min(cpuDelta / wallDelta, 1.0) : 1.0;
525+
const ratio =
526+
wallDelta > 0 ? Math.min(cpuDelta / wallDelta, 1.0) : 1.0;
508527
workerSpans[w].actives.push({
509528
start: openUnpark[w].timestamp,
510529
end: e.timestamp,
@@ -542,6 +561,61 @@
542561
return { workerSpans, perWorker, queueSamples };
543562
}
544563

564+
/**
565+
* Attach CPU samples to the poll spans they fall within using binary search.
566+
* Mutates workerSpans poll objects (adds .cpuSamples[], .schedSamples[])
567+
* and sample objects (sets .spawnLoc).
568+
* @param {CpuSample[]} cpuSamples
569+
* @param {Object} workerSpans
570+
* @returns {{ pollsWithCpuSamples: number, pollsWithSchedSamples: number }}
571+
*/
572+
function attachCpuSamples(cpuSamples, workerSpans) {
573+
for (const sample of cpuSamples) {
574+
const spans = workerSpans[sample.workerId];
575+
if (!spans) {
576+
sample.spawnLoc = null;
577+
continue;
578+
}
579+
if (sample.source !== 1) spans.cpuSampleTimes.push(sample.timestamp);
580+
const polls = spans.polls;
581+
const ts = sample.timestamp;
582+
// Binary search for rightmost poll with start <= ts
583+
let lo = 0,
584+
hi = polls.length - 1,
585+
found = false;
586+
while (lo <= hi) {
587+
const mid = (lo + hi) >> 1;
588+
if (polls[mid].start <= ts) {
589+
lo = mid + 1;
590+
} else {
591+
hi = mid - 1;
592+
}
593+
}
594+
// hi is now the index of the last poll with start <= ts
595+
if (hi >= 0 && ts <= polls[hi].end) {
596+
const poll = polls[hi];
597+
if (sample.source === 1) {
598+
(poll.schedSamples ??= []).push(sample);
599+
} else {
600+
(poll.cpuSamples ??= []).push(sample);
601+
}
602+
sample.spawnLoc = poll.spawnLoc;
603+
found = true;
604+
}
605+
if (!found) sample.spawnLoc = null;
606+
}
607+
608+
let pollsWithCpuSamples = 0;
609+
let pollsWithSchedSamples = 0;
610+
for (const w of Object.keys(workerSpans)) {
611+
for (const p of workerSpans[w].polls) {
612+
if (p.cpuSamples) pollsWithCpuSamples++;
613+
if (p.schedSamples) pollsWithSchedSamples++;
614+
}
615+
}
616+
return { pollsWithCpuSamples, pollsWithSchedSamples };
617+
}
618+
545619
// Export for both browser and Node.js
546620
if (typeof module !== "undefined" && module.exports) {
547621
module.exports = {
@@ -551,6 +625,7 @@
551625
symbolizeChain,
552626
deduplicateSamples,
553627
buildWorkerSpans,
628+
attachCpuSamples,
554629
};
555630
} else {
556631
exports.TraceParser = {
@@ -560,6 +635,7 @@
560635
symbolizeChain,
561636
deduplicateSamples,
562637
buildWorkerSpans,
638+
attachCpuSamples,
563639
};
564640
}
565641
})(typeof exports === "undefined" ? this : exports);

0 commit comments

Comments
 (0)