123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- import {lastOfArray} from 'sentry/utils';
- import {CallTreeNode} from 'sentry/utils/profiling/callTreeNode';
- import {Frame} from 'sentry/utils/profiling/frame';
- import {stackMarkerToHumanReadable} from './../formatters/stackMarkerToHumanReadable';
- import {resolveJSSelfProfilingStack} from './../jsSelfProfiling';
- import {Profile} from './profile';
- import {createFrameIndex} from './utils';
- function sortJSSelfProfileSamples(samples: Readonly<JSSelfProfiling.Trace['samples']>) {
- return [...samples].sort((a, b) => {
- return a.stackId - b.stackId;
- });
- }
- export class JSSelfProfile extends Profile {
- static FromProfile(
- profile: JSSelfProfiling.Trace,
- frameIndex: ReturnType<typeof createFrameIndex>,
- options: {type: 'flamechart' | 'flamegraph'}
- ): JSSelfProfile {
-
-
- const markers: JSSelfProfiling.Marker[] = [
- 'gc',
- 'layout',
- 'other',
- 'paint',
- 'script',
- 'style',
- ];
- for (const marker of markers) {
- frameIndex[marker] = new Frame(
- {
- key: marker,
- name: stackMarkerToHumanReadable(marker),
- line: undefined,
- column: undefined,
- is_application: false,
- },
- 'web'
- );
- }
- const startedAt = profile.samples[0].timestamp;
- const endedAt = lastOfArray(profile.samples).timestamp;
- const jsSelfProfile = new JSSelfProfile({
- duration: endedAt - startedAt,
- startedAt,
- endedAt,
- name: 'JSSelfProfiling',
- unit: 'milliseconds',
- threadId: 0,
- type: options.type,
- });
- const samples =
- options.type === 'flamegraph'
- ? sortJSSelfProfileSamples(profile.samples)
- : profile.samples;
- if (options.type === 'flamechart') {
-
-
-
- jsSelfProfile.appendSample(
- resolveJSSelfProfilingStack(
- profile,
- profile.samples[0].stackId,
- frameIndex,
- profile.samples[0].marker
- ),
- 0
- );
- }
-
-
- for (let i = 1; i < samples.length; i++) {
-
-
-
- if (samples[i].marker === 'gc' && options.type === 'flamechart') {
- jsSelfProfile.appendSample(
- resolveJSSelfProfilingStack(
- profile,
-
- samples[i - 1].stackId,
- frameIndex,
- samples[i].marker
- ),
- samples[i].timestamp - samples[i - 1].timestamp
- );
- } else {
- jsSelfProfile.appendSample(
- resolveJSSelfProfilingStack(
- profile,
- samples[i].stackId,
- frameIndex,
- samples[i].marker
- ),
- samples[i].timestamp - samples[i - 1].timestamp
- );
- }
- }
- return jsSelfProfile.build();
- }
- appendSample(stack: Frame[], weight: number): void {
- this.trackSampleStats(weight);
- let node = this.callTree;
- const framesInStack: CallTreeNode[] = [];
- for (const frame of stack) {
- const last = lastOfArray(node.children);
- if (last && !last.isLocked() && last.frame === frame) {
- node = last;
- } else {
- const parent = node;
- node = new CallTreeNode(frame, node);
- parent.children.push(node);
- }
- node.totalWeight += weight;
-
-
-
- let stackHeight = framesInStack.length - 1;
- while (stackHeight >= 0) {
- if (framesInStack[stackHeight].frame === node.frame) {
-
- framesInStack[stackHeight].recursive = node;
- node.recursive = framesInStack[stackHeight];
- break;
- }
- stackHeight--;
- }
- framesInStack.push(node);
- }
- node.selfWeight += weight;
- if (weight > 0) {
- this.minFrameDuration = Math.min(weight, this.minFrameDuration);
- }
-
-
-
- for (const child of node.children) {
- child.lock();
- }
- node.frame.selfWeight += weight;
- for (const stackNode of framesInStack) {
- stackNode.frame.totalWeight += weight;
- stackNode.count++;
- }
-
- if (node === lastOfArray(this.samples)) {
- this.weights[this.weights.length - 1] += weight;
- } else {
- this.samples.push(node);
- this.weights.push(weight);
- }
- }
- build(): JSSelfProfile {
- this.duration = Math.max(
- this.duration,
- this.weights.reduce((a, b) => a + b, 0)
- );
-
-
- if (
- this.minFrameDuration === Number.POSITIVE_INFINITY ||
- this.minFrameDuration === 0
- ) {
- this.minFrameDuration = this.duration;
- }
- return this;
- }
- }
|