Skip to content

Commit 1327380

Browse files
committed
evolve AnimationRequestHistory
1 parent 0626dd9 commit 1327380

File tree

5 files changed

+172
-34
lines changed

5 files changed

+172
-34
lines changed

src/api-monitor-cs-main.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ let panels: TPanelVisibilityMap;
4545
const eachSecond = new Timer(
4646
() => {
4747
meetMedia(document.querySelectorAll('video,audio'));
48+
panels.requestAnimationFrame && wrapper.updateAnimationsFramerate();
4849
},
4950
1e3,
5051
{ interval: true }

src/api/wrappers.ts

Lines changed: 82 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ export type TRequestAnimationFrameHistory = {
9191
calls: number;
9292
handler: number | undefined | string;
9393
selfTime: number | null;
94+
online: number;
95+
canceledCounter: number;
96+
canceledByTraceIds: string[] | null;
97+
fps: number;
9498
};
9599
export type TCancelAnimationFrameHistory = {
96100
traceId: string;
@@ -145,18 +149,22 @@ export type TWrapperMetrics = {
145149
};
146150

147151
export class Wrapper {
148-
onlineTimers: Map<number, TOnlineTimerMetrics> = new Map();
149-
setTimeoutHistory: Map<string, TSetTimerHistory> = new Map();
150-
clearTimeoutHistory: Map<string, TClearTimerHistory> = new Map();
151-
setIntervalHistory: Map<string, TSetTimerHistory> = new Map();
152-
clearIntervalHistory: Map<string, TClearTimerHistory> = new Map();
153-
evalHistory: Map<string, TEvalHistory> = new Map();
154-
rafHistory: Map<string, TRequestAnimationFrameHistory> = new Map();
155-
cafHistory: Map<string, TCancelAnimationFrameHistory> = new Map();
156-
/** mapping ric/cic handler to ric traceId */
157-
onlineIdleCallbackLookup: Map<number, string> = new Map();
158-
ricHistory: Map<string, TRequestIdleCallbackHistory> = new Map();
159-
cicHistory: Map<string, TCancelIdleCallbackHistory> = new Map();
152+
onlineTimers: Map</*handler*/ number, TOnlineTimerMetrics> = new Map();
153+
setTimeoutHistory: Map</*traceId*/ string, TSetTimerHistory> = new Map();
154+
clearTimeoutHistory: Map</*traceId*/ string, TClearTimerHistory> = new Map();
155+
setIntervalHistory: Map</*traceId*/ string, TSetTimerHistory> = new Map();
156+
clearIntervalHistory: Map</*traceId*/ string, TClearTimerHistory> = new Map();
157+
evalHistory: Map</*traceId*/ string, TEvalHistory> = new Map();
158+
animationCallsMap = new Map</*traceId*/ string, /*calls*/ number>();
159+
onlineAnimationFrameLookup: Map</*handler*/ number, /*traceId*/ string> =
160+
new Map();
161+
rafHistory: Map</*traceId*/ string, TRequestAnimationFrameHistory> =
162+
new Map();
163+
cafHistory: Map</*traceId*/ string, TCancelAnimationFrameHistory> = new Map();
164+
onlineIdleCallbackLookup: Map</*handler*/ number, /*traceId*/ string> =
165+
new Map();
166+
ricHistory: Map</*traceId*/ string, TRequestIdleCallbackHistory> = new Map();
167+
cicHistory: Map</*traceId*/ string, TCancelIdleCallbackHistory> = new Map();
160168
callCounter: TWrapperMetrics['callCounter'] = {
161169
activeTimers: 0,
162170
setTimeout: 0,
@@ -382,10 +390,8 @@ export class Wrapper {
382390
}
383391
}
384392

385-
updateRecordSelfTime(
386-
map:
387-
| Map<string, TRequestAnimationFrameHistory>
388-
| Map<string, TSetTimerHistory>,
393+
updateTimersSelfTime(
394+
map: Map<string, TSetTimerHistory>,
389395
traceId: string,
390396
selfTime: number
391397
) {
@@ -396,22 +402,53 @@ export class Wrapper {
396402
}
397403
}
398404

405+
updateAnimationsFramerate() {
406+
for (let [, rafRecord] of this.rafHistory) {
407+
const prevCalls = this.animationCallsMap.get(rafRecord.traceId) || 0;
408+
const fps = rafRecord.calls - prevCalls;
409+
410+
this.animationCallsMap.set(rafRecord.traceId, rafRecord.calls);
411+
rafRecord.fps = fps;
412+
}
413+
}
414+
399415
updateRafHistory(handler: number, callstack: TCallstack) {
400416
const existing = this.rafHistory.get(callstack.traceId);
401417

402418
if (existing) {
403419
existing.calls++;
404420
existing.handler = handler;
421+
existing.online++;
405422
} else {
406423
this.rafHistory.set(callstack.traceId, {
407424
traceId: callstack.traceId,
408425
trace: callstack.trace,
409426
traceDomain: this.#getTraceDomain(callstack.trace[0]),
410427
calls: 1,
411428
handler,
429+
online: 1,
430+
canceledCounter: 0,
431+
canceledByTraceIds: null,
412432
selfTime: null,
433+
fps: 1,
413434
});
414435
}
436+
437+
this.onlineAnimationFrameLookup.set(handler, callstack.traceId);
438+
}
439+
440+
rafFired(handler: number, traceId: string, selfTime: number) {
441+
const rafRecord = this.rafHistory.get(traceId);
442+
if (!rafRecord) {
443+
return;
444+
}
445+
446+
rafRecord.selfTime = trim2microsecond(selfTime);
447+
448+
if (this.onlineAnimationFrameLookup.has(handler)) {
449+
this.onlineAnimationFrameLookup.delete(handler);
450+
rafRecord.online--;
451+
}
415452
}
416453

417454
updateCafHistory(handler: number | string, callstack: TCallstack) {
@@ -434,9 +471,24 @@ export class Wrapper {
434471
handler,
435472
});
436473
}
474+
475+
const rafTraceId = this.onlineAnimationFrameLookup.get(Number(handler));
476+
const rafRecord = rafTraceId && this.rafHistory.get(rafTraceId);
477+
if (rafRecord) {
478+
this.onlineAnimationFrameLookup.delete(Number(handler));
479+
480+
rafRecord.online--;
481+
482+
if (rafRecord.canceledByTraceIds === null) {
483+
rafRecord.canceledByTraceIds = [callstack.traceId];
484+
} else if (!rafRecord.canceledByTraceIds.includes(callstack.traceId)) {
485+
rafRecord.canceledByTraceIds.push(callstack.traceId);
486+
}
487+
rafRecord.canceledCounter++;
488+
}
437489
}
438490

439-
ricOffline(
491+
ricFired(
440492
handler: number,
441493
traceId: string,
442494
deadline: IdleDeadline,
@@ -451,7 +503,7 @@ export class Wrapper {
451503
ricRecord.selfTime = trim2microsecond(selfTime);
452504

453505
if (this.onlineIdleCallbackLookup.get(handler)) {
454-
this.onlineIdleCallbackLookup.delete(Number(handler));
506+
this.onlineIdleCallbackLookup.delete(handler);
455507
ricRecord.online--;
456508
}
457509
}
@@ -539,6 +591,8 @@ export class Wrapper {
539591
this.ricHistory.clear();
540592
this.cicHistory.clear();
541593
this.onlineIdleCallbackLookup.clear();
594+
this.onlineAnimationFrameLookup.clear();
595+
this.animationCallsMap.clear();
542596
this.callCounter.setTimeout =
543597
this.callCounter.clearTimeout =
544598
this.callCounter.setInterval =
@@ -630,7 +684,7 @@ export class Wrapper {
630684
debugger;
631685
}
632686
fn(deadline);
633-
this.ricOffline(
687+
this.ricFired(
634688
handler,
635689
callstack.traceId,
636690
deadline,
@@ -650,11 +704,12 @@ export class Wrapper {
650704
) {
651705
const err = new Error(TRACE_ERROR_MESSAGE);
652706
const callstack = this.createCallstack(err);
707+
708+
this.updateCicHistory(handler, callstack);
709+
this.callCounter.cancelIdleCallback++;
653710
if (this.#traceForDebug === callstack.traceId) {
654711
debugger;
655712
}
656-
this.updateCicHistory(handler, callstack);
657-
this.callCounter.cancelIdleCallback++;
658713
this.native.cancelIdleCallback(handler);
659714
}.bind(this);
660715
}
@@ -674,11 +729,7 @@ export class Wrapper {
674729
debugger;
675730
}
676731
fn(...args);
677-
this.updateRecordSelfTime(
678-
this.rafHistory,
679-
callstack.traceId,
680-
performance.now() - start
681-
);
732+
this.rafFired(handler, callstack.traceId, performance.now() - start);
682733
});
683734
this.updateRafHistory(handler, callstack);
684735

@@ -693,11 +744,12 @@ export class Wrapper {
693744
) {
694745
const err = new Error(TRACE_ERROR_MESSAGE);
695746
const callstack = this.createCallstack(err);
747+
748+
this.updateCafHistory(handler, callstack);
749+
this.callCounter.cancelAnimationFrame++;
696750
if (this.#traceForDebug === callstack.traceId) {
697751
debugger;
698752
}
699-
this.updateCafHistory(handler, callstack);
700-
this.callCounter.cancelAnimationFrame++;
701753
this.native.cancelAnimationFrame(handler);
702754
}.bind(this);
703755
}
@@ -770,7 +822,7 @@ export class Wrapper {
770822
}
771823
const stop = performance.now() - start;
772824
this.timerOffline(handler, null, stop);
773-
this.updateRecordSelfTime(
825+
this.updateTimersSelfTime(
774826
this.setTimeoutHistory,
775827
callstack.traceId,
776828
stop
@@ -856,7 +908,7 @@ export class Wrapper {
856908
}
857909
code(...params);
858910
}
859-
this.updateRecordSelfTime(
911+
this.updateTimersSelfTime(
860912
this.setIntervalHistory,
861913
callstack.traceId,
862914
performance.now() - start

src/view/components/AnimationMetrics.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<AnimationRequestHistory
1111
caption="requestAnimationFrame History"
1212
metrics={metrics.rafHistory}
13+
cafHistory={metrics.cafHistory}
1314
/>
1415
{/if}
1516

src/view/components/AnimationRequestHistory.svelte

Lines changed: 87 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,39 @@
11
<script lang="ts">
2-
import type { TRequestAnimationFrameHistory } from '../../api/wrappers.ts';
2+
import type {
3+
TCancelAnimationFrameHistory,
4+
TRequestAnimationFrameHistory,
5+
} from '../../api/wrappers.ts';
36
import {
47
DEFAULT_SORT_RAF,
58
getSettings,
69
setSettings,
710
ESortOrder,
811
} from '../../api/settings.ts';
912
import { compareByFieldOrder } from '../../api/comparator.ts';
13+
import { CALLED_ABORTED_TOOLTIP } from '../../api/const.ts';
1014
import Variable from './Variable.svelte';
1115
import Trace from './Trace.svelte';
1216
import TraceDomain from './TraceDomain.svelte';
1317
import SortableColumn from './SortableColumn.svelte';
1418
import FrameSensitiveTime from './FrameSensitiveTime.svelte';
1519
import TraceBreakpoint from './TraceBreakpoint.svelte';
20+
import Dialog from './Dialog.svelte';
21+
import Alert from './Alert.svelte';
22+
import AnimationCancelHistory from './AnimationCancelHistory.svelte';
1623
1724
let {
1825
metrics,
26+
cafHistory,
1927
caption = '',
20-
}: { metrics: TRequestAnimationFrameHistory[]; caption: string } = $props();
28+
}: {
29+
metrics: TRequestAnimationFrameHistory[];
30+
cafHistory: TCancelAnimationFrameHistory[] | null;
31+
caption: string;
32+
} = $props();
2133
let sortField = $state(DEFAULT_SORT_RAF.field);
2234
let sortOrder = $state(DEFAULT_SORT_RAF.order);
35+
let dialogEl: Dialog | null = null;
36+
let alertEl: Alert | null = null;
2337
let sortedMetrics = $derived.by(() =>
2438
metrics.sort(compareByFieldOrder(sortField, sortOrder))
2539
);
@@ -40,8 +54,50 @@
4054
},
4155
});
4256
}
57+
58+
let cafHistoryMetrics: TCancelAnimationFrameHistory[] = $state([]);
59+
60+
function onFindRegressors(regressors: string[] | null) {
61+
if (!dialogEl || !alertEl || !regressors?.length) {
62+
return;
63+
}
64+
65+
for (let n = regressors.length - 1; n >= 0; n--) {
66+
const traceId = regressors[n];
67+
let record = cafHistory?.find((r) => r.traceId === traceId);
68+
if (record) {
69+
cafHistoryMetrics.push(record);
70+
}
71+
}
72+
73+
if (cafHistoryMetrics.length) {
74+
dialogEl.show();
75+
} else {
76+
alertEl.show();
77+
}
78+
}
79+
80+
function onCloseDialog() {
81+
cafHistoryMetrics.splice(0);
82+
}
4383
</script>
4484

85+
<Dialog
86+
bind:this={dialogEl}
87+
eventClose={onCloseDialog}
88+
title="Places from which requestAnimationFrame with current callstack was prematurely canceled"
89+
description="The information is actual only on time of demand. Requires cancelAnimationFrame panel enabled."
90+
>
91+
<AnimationCancelHistory
92+
caption="Canceled by"
93+
metrics={$state.snapshot(cafHistoryMetrics)}
94+
/>
95+
</Dialog>
96+
97+
<Alert bind:this={alertEl} title="Attention">
98+
Requires cancelAnimationFrame panel enabled
99+
</Alert>
100+
45101
<table data-navigation-tag={caption}>
46102
<caption class="bc-invert ta-l">
47103
{caption}
@@ -59,6 +115,7 @@
59115
eventChangeSorting={onChangeSort}>Self</SortableColumn
60116
>
61117
</th>
118+
<th class="ta-c" title="Frames per second">FPS</th>
62119
<th class="ta-c">
63120
<SortableColumn
64121
field="calls"
@@ -75,6 +132,14 @@
75132
eventChangeSorting={onChangeSort}>Handler</SortableColumn
76133
>
77134
</th>
135+
<th>
136+
<SortableColumn
137+
field="online"
138+
currentField={sortField}
139+
currentFieldOrder={sortOrder}
140+
eventChangeSorting={onChangeSort}>Set</SortableColumn
141+
>
142+
</th>
78143
</tr>
79144

80145
{#each sortedMetrics as metric (metric.traceId)}
@@ -85,8 +150,27 @@
85150
<Trace trace={metric.trace} />
86151
</td>
87152
<td class="ta-r"><FrameSensitiveTime value={metric.selfTime} /></td>
88-
<td class="ta-c"><Variable value={metric.calls} /></td>
153+
<td class="ta-c">{metric.fps || undefined}</td>
154+
<td class="ta-c">
155+
<Variable value={metric.calls} />{#if metric.canceledCounter}-<a
156+
role="button"
157+
href="void(0)"
158+
title={CALLED_ABORTED_TOOLTIP}
159+
onclick={(e) => {
160+
e.preventDefault();
161+
onFindRegressors(metric.canceledByTraceIds);
162+
}}
163+
><Variable value={metric.canceledCounter} />/{metric
164+
.canceledByTraceIds?.length}</a
165+
>
166+
{/if}
167+
</td>
89168
<td class="ta-c"><Variable value={metric.handler} /></td>
169+
<td class="ta-r">
170+
{#if metric.online}
171+
<Variable value={metric.online} />
172+
{/if}
173+
</td>
90174
</tr>
91175
{/each}
92176
</tbody>

src/view/components/IdleCallbackRequestHistory.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@
8787
bind:this={dialogEl}
8888
eventClose={onCloseDialog}
8989
title="Places from which requestIdleCallback with current callstack was prematurely canceled"
90-
description="The information is actual only on time of demand. For full coverage - requires cancelIdleCallback panels enabled."
90+
description="The information is actual only on time of demand. Requires cancelIdleCallback panel enabled."
9191
>
9292
<IdleCallbackCancelHistory
9393
caption="Canceled by"

0 commit comments

Comments
 (0)