Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .assets/detail.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
42 changes: 41 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ $ npm install --save tracelib

## `getSummary`

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

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

Expand Down Expand Up @@ -166,6 +166,46 @@ console.log(memoryInfo)
*/
```

## `getDetailStats`

Fetch data (timestamp and values) of scripting, rendering, painting from tracelogs.

![Detail Data](./.assets/detail.png "Detail Data")

```js
import Tracelib from 'tracelib'
import JANK_TRACE_LOG from './jankTraceLog.json'

const tasks = new Tracelib(JANK_TRACE_LOG)
const detail = tasks.getDetailStats()
console.log(detail)

/**
* output:
* {
* rendering: {
* times: [ 49970556.092, ..., 49972763.552 ],
* values: [1, ..., 5]
* },
* painting: {
* times: [ 49970556.092, ..., 49972763.552 ],
* values: [1, ..., 5]
* },
* other: {
* times: [ 49970556.092, ..., 49972763.552 ],
* values: [1, ..., 5]
* },
* scripting: {
* times: [ 49970556.092, ..., 49972763.552 ],
* values: [1, ..., 5]
* },
* range: {
* times: [ 289959855.634, 289961229.717 ],
* values: [ 289959855.634, 289961229.717 ]
* }
* }
```

# Test

To test this package, run:
Expand Down
23 changes: 22 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Range, StatsObject, CountersData } from '../devtools/types'
import { Range, StatsObject, CountersData, StatsArray } from '../devtools/types'
import TimelineLoader from '../devtools/loader'
import { calcFPS } from '../devtools/utils'
import Track, { TrackType } from '../devtools/timelineModel/track'
Expand All @@ -7,6 +7,7 @@ import PerformanceModel from '../devtools/timelineModel/performanceModel'
import TimelineData from '../devtools/timelineModel/timelineData'
import Event from '../devtools/tracingModel/event'
import CountersGraph from '../devtools/timelineModel/counterGraph'
import CustomUtils from './utils'

export default class Tracelib {
public tracelog: object
Expand Down Expand Up @@ -93,4 +94,24 @@ export default class Tracelib {
}
}), {})
}

public getDetailStats(from?: number, to?: number): StatsArray {
const timelineUtils = new CustomUtils()
const startTime = from || this._performanceModel.timelineModel().minimumRecordTime()
const endTime = to || this._performanceModel.timelineModel().maximumRecordTime()
const mainTrack = this._findMainTrack()

// We are facing data mutaion issue in devtools, to avoid it cloning syncEvents
const syncEvents = mainTrack.syncEvents().slice()

return {
...timelineUtils.detailStatsForTimeRange(
syncEvents, startTime, endTime
),
range: {
time: [startTime, endTime],
value: [startTime, endTime]
}
}
}
}
109 changes: 109 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import TimelineUIUtils from './../devtools/timelineModel/timelineUIUtils'
import { StatsArray } from './../devtools/types'
import TimelineModel from './../devtools/timelineModel'
import Event from './../devtools/tracingModel/event'
import TracingModel from './../devtools/tracingModel'

export default class CustomUtils extends TimelineUIUtils {
/**
* @param {!Array<!SDK.TracingModel.Event>} events
* @param {number} startTime
* @param {number} endTime
* @return {!Object<string, number>}
*/
public detailStatsForTimeRange(events: Event[], startTime: number, endTime: number): StatsArray {
const eventStyle = this.eventStyle.bind(this)
const visibleEventsFilterFunc = this.visibleEventsFilter.bind(this)

if (!events.length) {
return {
idle: {
'time': [endTime - startTime],
'value': [endTime - startTime]
}
}
}

// aggeregatedStats is a map by categories. For each category there's an array
// containing sorted time points which records accumulated value of the category.
const aggregatedStats: StatsArray = {}
const categoryStack: string[] = []
let lastTime = 0
TimelineModel.forEachEvent(
events,
onStartEvent,
onEndEvent,
undefined,
undefined,
undefined,
filterForStats()
)

/**
* @return {function(!SDK.TracingModel.Event):boolean}
*/
function filterForStats(): any {
const visibleEventsFilter = visibleEventsFilterFunc()
return (event: Event): any => visibleEventsFilter.accept(event) || TracingModel.isTopLevelEvent(event)
}

/**
* @param {string} category
* @param {number} time
*/
function updateCategory(category: string, time: number): void {
let statsArrays = aggregatedStats[category]
if (!statsArrays) {
statsArrays = { time: [], value: [] }
aggregatedStats[category] = statsArrays
}
if (statsArrays.time.length && statsArrays.time[statsArrays.time.length - 1] === time) {
return
}
// const lastValue = statsArrays.value.length ? statsArrays.value[statsArrays.value.length - 1] : 0
statsArrays.value.push(time - lastTime)
statsArrays.time.push(time)
}

/**
* @param {?string} from
* @param {?string} to
* @param {number} time
*/
function categoryChange(from?: string, to?: string, time?: number): void {
if (from) {
updateCategory(from, time)
}

lastTime = time

if (to) {
updateCategory(to, time)
}
}

/**
* @param {!SDK.TracingModel.Event} e
*/
function onStartEvent(e: Event): void {
const category = eventStyle(e).category.name
const parentCategory = categoryStack.length ? categoryStack[categoryStack.length - 1] : null
if (category !== parentCategory) {
categoryChange(parentCategory, category, e.startTime)
}
categoryStack.push(category)
}

/**
* @param {!SDK.TracingModel.Event} e
*/
function onEndEvent(e: Event): void {
const category = categoryStack.pop()
const parentCategory = categoryStack.length ? categoryStack[categoryStack.length - 1] : null
if (category !== parentCategory) {
categoryChange(category, parentCategory, e.endTime)
}
}
return aggregatedStats
}
}
30 changes: 30 additions & 0 deletions tests/__snapshots__/index.test.ts.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,35 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`getDetailStats should get detail stats 1`] = `
Array [
"rendering",
"painting",
"other",
"scripting",
"range",
]
`;

exports[`getDetailStats should get summary data between passed range 1`] = `
Array [
"rendering",
"painting",
"other",
"scripting",
"range",
]
`;

exports[`getDetailStats should not throw error on second call of getDetailStats 1`] = `
Array [
"rendering",
"painting",
"other",
"scripting",
"range",
]
`;

exports[`getSummary should get summary data 1`] = `
Object {
"endTime": 289961229.717,
Expand Down
17 changes: 17 additions & 0 deletions tests/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,20 @@ describe('mainTrackEvents', () => {
expect(result.length).toEqual(56244)
})
})

describe('getDetailStats', () => {
it('should get detail stats', () => {
const result = trace.getDetailStats()
expect(Object.keys(result)).toMatchSnapshot()
Comment thread
christian-bromann marked this conversation as resolved.
})

it('should not throw error on second call of getDetailStats', () => {
const result = trace.getDetailStats()
expect(Object.keys(result)).toMatchSnapshot()
Comment thread
christian-bromann marked this conversation as resolved.
})

it('should get summary data between passed range', () => {
const result = trace.getDetailStats(289960055.634, 289960729.717)
expect(Object.keys(result)).toMatchSnapshot()
Comment thread
christian-bromann marked this conversation as resolved.
})
})