Skip to content

Commit 638905c

Browse files
farhan-saucechristian-bromann
authored andcommitted
fetch detail stats for timeRange (#16)
* [PERF-1342] implement detailStatsForTimeRange * [PERF-1342] add unit-tests for getDetailStats * update readme * remove constructor
1 parent 8242267 commit 638905c

6 files changed

Lines changed: 219 additions & 2 deletions

File tree

.assets/detail.png

8.49 KB
Loading

README.md

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ $ npm install --save tracelib
1717

1818
## `getSummary`
1919

20-
Fetch time-durations of scripting, rendering, painting from tracelogs.
20+
Fetch total time-durations of scripting, rendering, painting from tracelogs.
2121

2222
![Summary Data](./.assets/summary.png "Summary Data")
2323

@@ -166,6 +166,46 @@ console.log(memoryInfo)
166166
*/
167167
```
168168

169+
## `getDetailStats`
170+
171+
Fetch data (timestamp and values) of scripting, rendering, painting from tracelogs.
172+
173+
![Detail Data](./.assets/detail.png "Detail Data")
174+
175+
```js
176+
import Tracelib from 'tracelib'
177+
import JANK_TRACE_LOG from './jankTraceLog.json'
178+
179+
const tasks = new Tracelib(JANK_TRACE_LOG)
180+
const detail = tasks.getDetailStats()
181+
console.log(detail)
182+
183+
/**
184+
* output:
185+
* {
186+
* rendering: {
187+
* times: [ 49970556.092, ..., 49972763.552 ],
188+
* values: [1, ..., 5]
189+
* },
190+
* painting: {
191+
* times: [ 49970556.092, ..., 49972763.552 ],
192+
* values: [1, ..., 5]
193+
* },
194+
* other: {
195+
* times: [ 49970556.092, ..., 49972763.552 ],
196+
* values: [1, ..., 5]
197+
* },
198+
* scripting: {
199+
* times: [ 49970556.092, ..., 49972763.552 ],
200+
* values: [1, ..., 5]
201+
* },
202+
* range: {
203+
* times: [ 289959855.634, 289961229.717 ],
204+
* values: [ 289959855.634, 289961229.717 ]
205+
* }
206+
* }
207+
```
208+
169209
# Test
170210
171211
To test this package, run:

src/index.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Range, StatsObject, CountersData } from '../devtools/types'
1+
import { Range, StatsObject, CountersData, StatsArray } from '../devtools/types'
22
import TimelineLoader from '../devtools/loader'
33
import { calcFPS } from '../devtools/utils'
44
import Track, { TrackType } from '../devtools/timelineModel/track'
@@ -7,6 +7,7 @@ import PerformanceModel from '../devtools/timelineModel/performanceModel'
77
import TimelineData from '../devtools/timelineModel/timelineData'
88
import Event from '../devtools/tracingModel/event'
99
import CountersGraph from '../devtools/timelineModel/counterGraph'
10+
import CustomUtils from './utils'
1011

1112
export default class Tracelib {
1213
public tracelog: object
@@ -93,4 +94,24 @@ export default class Tracelib {
9394
}
9495
}), {})
9596
}
97+
98+
public getDetailStats(from?: number, to?: number): StatsArray {
99+
const timelineUtils = new CustomUtils()
100+
const startTime = from || this._performanceModel.timelineModel().minimumRecordTime()
101+
const endTime = to || this._performanceModel.timelineModel().maximumRecordTime()
102+
const mainTrack = this._findMainTrack()
103+
104+
// We are facing data mutaion issue in devtools, to avoid it cloning syncEvents
105+
const syncEvents = mainTrack.syncEvents().slice()
106+
107+
return {
108+
...timelineUtils.detailStatsForTimeRange(
109+
syncEvents, startTime, endTime
110+
),
111+
range: {
112+
time: [startTime, endTime],
113+
value: [startTime, endTime]
114+
}
115+
}
116+
}
96117
}

src/utils.ts

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import TimelineUIUtils from './../devtools/timelineModel/timelineUIUtils'
2+
import { StatsArray } from './../devtools/types'
3+
import TimelineModel from './../devtools/timelineModel'
4+
import Event from './../devtools/tracingModel/event'
5+
import TracingModel from './../devtools/tracingModel'
6+
7+
export default class CustomUtils extends TimelineUIUtils {
8+
/**
9+
* @param {!Array<!SDK.TracingModel.Event>} events
10+
* @param {number} startTime
11+
* @param {number} endTime
12+
* @return {!Object<string, number>}
13+
*/
14+
public detailStatsForTimeRange(events: Event[], startTime: number, endTime: number): StatsArray {
15+
const eventStyle = this.eventStyle.bind(this)
16+
const visibleEventsFilterFunc = this.visibleEventsFilter.bind(this)
17+
18+
if (!events.length) {
19+
return {
20+
idle: {
21+
'time': [endTime - startTime],
22+
'value': [endTime - startTime]
23+
}
24+
}
25+
}
26+
27+
// aggeregatedStats is a map by categories. For each category there's an array
28+
// containing sorted time points which records accumulated value of the category.
29+
const aggregatedStats: StatsArray = {}
30+
const categoryStack: string[] = []
31+
let lastTime = 0
32+
TimelineModel.forEachEvent(
33+
events,
34+
onStartEvent,
35+
onEndEvent,
36+
undefined,
37+
undefined,
38+
undefined,
39+
filterForStats()
40+
)
41+
42+
/**
43+
* @return {function(!SDK.TracingModel.Event):boolean}
44+
*/
45+
function filterForStats(): any {
46+
const visibleEventsFilter = visibleEventsFilterFunc()
47+
return (event: Event): any => visibleEventsFilter.accept(event) || TracingModel.isTopLevelEvent(event)
48+
}
49+
50+
/**
51+
* @param {string} category
52+
* @param {number} time
53+
*/
54+
function updateCategory(category: string, time: number): void {
55+
let statsArrays = aggregatedStats[category]
56+
if (!statsArrays) {
57+
statsArrays = { time: [], value: [] }
58+
aggregatedStats[category] = statsArrays
59+
}
60+
if (statsArrays.time.length && statsArrays.time[statsArrays.time.length - 1] === time) {
61+
return
62+
}
63+
// const lastValue = statsArrays.value.length ? statsArrays.value[statsArrays.value.length - 1] : 0
64+
statsArrays.value.push(time - lastTime)
65+
statsArrays.time.push(time)
66+
}
67+
68+
/**
69+
* @param {?string} from
70+
* @param {?string} to
71+
* @param {number} time
72+
*/
73+
function categoryChange(from?: string, to?: string, time?: number): void {
74+
if (from) {
75+
updateCategory(from, time)
76+
}
77+
78+
lastTime = time
79+
80+
if (to) {
81+
updateCategory(to, time)
82+
}
83+
}
84+
85+
/**
86+
* @param {!SDK.TracingModel.Event} e
87+
*/
88+
function onStartEvent(e: Event): void {
89+
const category = eventStyle(e).category.name
90+
const parentCategory = categoryStack.length ? categoryStack[categoryStack.length - 1] : null
91+
if (category !== parentCategory) {
92+
categoryChange(parentCategory, category, e.startTime)
93+
}
94+
categoryStack.push(category)
95+
}
96+
97+
/**
98+
* @param {!SDK.TracingModel.Event} e
99+
*/
100+
function onEndEvent(e: Event): void {
101+
const category = categoryStack.pop()
102+
const parentCategory = categoryStack.length ? categoryStack[categoryStack.length - 1] : null
103+
if (category !== parentCategory) {
104+
categoryChange(category, parentCategory, e.endTime)
105+
}
106+
}
107+
return aggregatedStats
108+
}
109+
}

tests/__snapshots__/index.test.ts.snap

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,35 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`getDetailStats should get detail stats 1`] = `
4+
Array [
5+
"rendering",
6+
"painting",
7+
"other",
8+
"scripting",
9+
"range",
10+
]
11+
`;
12+
13+
exports[`getDetailStats should get summary data between passed range 1`] = `
14+
Array [
15+
"rendering",
16+
"painting",
17+
"other",
18+
"scripting",
19+
"range",
20+
]
21+
`;
22+
23+
exports[`getDetailStats should not throw error on second call of getDetailStats 1`] = `
24+
Array [
25+
"rendering",
26+
"painting",
27+
"other",
28+
"scripting",
29+
"range",
30+
]
31+
`;
32+
333
exports[`getSummary should get summary data 1`] = `
434
Object {
535
"endTime": 289961229.717,

tests/index.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,20 @@ describe('mainTrackEvents', () => {
6262
expect(result.length).toEqual(56244)
6363
})
6464
})
65+
66+
describe('getDetailStats', () => {
67+
it('should get detail stats', () => {
68+
const result = trace.getDetailStats()
69+
expect(Object.keys(result)).toMatchSnapshot()
70+
})
71+
72+
it('should not throw error on second call of getDetailStats', () => {
73+
const result = trace.getDetailStats()
74+
expect(Object.keys(result)).toMatchSnapshot()
75+
})
76+
77+
it('should get summary data between passed range', () => {
78+
const result = trace.getDetailStats(289960055.634, 289960729.717)
79+
expect(Object.keys(result)).toMatchSnapshot()
80+
})
81+
})

0 commit comments

Comments
 (0)