Skip to content

Commit

Permalink
Add special handling for X events with matching ts & dur
Browse files Browse the repository at this point in the history
  • Loading branch information
jlfwong committed Oct 25, 2020
1 parent b867028 commit 95183a4
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 28 deletions.
13 changes: 7 additions & 6 deletions src/import/__snapshots__/trace-event.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -204,17 +204,17 @@ Object {
"key": "alpha",
"line": undefined,
"name": "alpha",
"selfWeight": 1,
"totalWeight": 6,
"selfWeight": 8,
"totalWeight": 10,
},
Frame {
"col": undefined,
"file": undefined,
"key": "beta",
"line": undefined,
"name": "beta",
"selfWeight": 4,
"totalWeight": 5,
"selfWeight": 1,
"totalWeight": 2,
},
Frame {
"col": undefined,
Expand All @@ -230,8 +230,9 @@ Object {
"stacks": Array [
"alpha 1.00µs",
"alpha;beta;gamma 1.00µs",
"alpha;beta 3.00µs",
"alpha;beta;gamma;beta 1.00µs",
"alpha 3.00µs",
"alpha;gamma;beta 1.00µs",
"alpha 4.00µs",
],
}
`;
Expand Down
79 changes: 57 additions & 22 deletions src/import/trace-event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ interface XTraceEvent extends TraceEvent {

// The trace format supports a number of event types that we ignore.
type ImportableTraceEvent = BTraceEvent | ETraceEvent | XTraceEvent
type DurationEvent = BTraceEvent | ETraceEvent
type DurationEvent = {ev: BTraceEvent | ETraceEvent; sortIndex: number}

function filterIgnoredEventTypes(events: TraceEvent[]): ImportableTraceEvent[] {
const ret: ImportableTraceEvent[] = []
Expand All @@ -81,14 +81,16 @@ function filterIgnoredEventTypes(events: TraceEvent[]): ImportableTraceEvent[] {

function convertToDurationEvents(events: ImportableTraceEvent[]): DurationEvent[] {
const ret: DurationEvent[] = []
for (let ev of events) {
for (let i = 0; i < events.length; i++) {
const ev = events[i]

switch (ev.ph) {
case 'B':
ret.push(ev)
ret.push({ev, sortIndex: i})
break

case 'E':
ret.push(ev)
ret.push({ev, sortIndex: i})
break

case 'X':
Expand All @@ -101,8 +103,35 @@ function convertToDurationEvents(events: ImportableTraceEvent[]): DurationEvent[
continue
}

ret.push({...ev, ph: 'B'} as BTraceEvent)
ret.push({...ev, ph: 'E', ts: ev.ts + dur} as ETraceEvent)
// We convert 'X' events into 'B' & 'E' event pairs. We need to be careful
// with how we handle pairs of 'X' events with exactly the same ts & dur.
//
// For example, consider the following two events:
//
// { "pid": 0, "tid": 0, "ph": "X", "ts": 0, "dur": 10, "name": "A" },
// { "pid": 0, "tid": 0, "ph": "X", "ts": 0, "dur": 10, "name": "B" },
//
// The equivalent resulting event sequence we want is:
//
// { "pid": 0, "tid": 0, "ph": "B", "ts": 0, "name": "A" },
// { "pid": 0, "tid": 0, "ph": "B", "ts": 0, "name": "B" },
// { "pid": 0, "tid": 0, "ph": "E", "ts": 10, "name": "B" },
// { "pid": 0, "tid": 0, "ph": "E", "ts": 10, "name": "A" },
//
// Note that this is *not* equivalent to the following:
//
// { "pid": 0, "tid": 0, "ph": "B", "ts": 0, "name": "B" },
// { "pid": 0, "tid": 0, "ph": "B", "ts": 0, "name": "A" },
// { "pid": 0, "tid": 0, "ph": "E", "ts": 10, "name": "A" },
// { "pid": 0, "tid": 0, "ph": "E", "ts": 10, "name": "B" },
//
// To support this, we need to carefully manage the sort order of pairs of
// 'B' events, and do so differently than how we manage the corresponding
// 'E' events
const eventB = {...ev, ph: 'B'} as BTraceEvent
const eventE = {...ev, ph: 'E', ts: ev.ts + dur} as ETraceEvent
ret.push({ev: eventB, sortIndex: i})
ret.push({ev: eventE, sortIndex: -i})
break

default:
Expand Down Expand Up @@ -163,37 +192,43 @@ function eventListToProfileGroup(events: TraceEvent[]): ProfileGroup {
const threadNamesByPidTid = getThreadNamesByPidTid(events)

durationEvents.sort((a, b) => {
if (a.ts < b.ts) return -1
if (a.ts > b.ts) return 1
if (a.pid < b.pid) return -1
if (a.pid > b.pid) return 1
if (a.tid < b.tid) return -1
if (a.tid > b.tid) return 1
const aEv = a.ev
const bEv = b.ev
if (aEv.pid < bEv.pid) return -1
if (aEv.pid > bEv.pid) return 1
if (aEv.tid < bEv.tid) return -1
if (aEv.tid > bEv.tid) return 1
if (aEv.ts < bEv.ts) return -1
if (aEv.ts > bEv.ts) return 1

// We have to be careful with events that have the same timestamp
// and the same pid/tid
const aKey = keyForEvent(a)
const bKey = keyForEvent(b)
const aKey = keyForEvent(aEv)
const bKey = keyForEvent(bEv)
if (aKey === bKey) {
// If the two elements have the same key, we need to process the begin
// event before the end event. This will be a zero-duration event.
if (a.ph === 'B' && b.ph === 'E') return -1
if (a.ph === 'E' && b.ph === 'B') return 1
if (aEv.ph === 'B' && bEv.ph === 'E') return -1
if (aEv.ph === 'E' && bEv.ph === 'B') return 1
} else {
// If the two elements have *different* keys, we want to process
// the end of an event before the beginning of the event to prevent
// out-of-order push/pops from the call-stack.
if (a.ph === 'B' && b.ph === 'E') return 1
if (a.ph === 'E' && b.ph === 'B') return -1
if (aEv.ph === 'B' && bEv.ph === 'E') return 1
if (aEv.ph === 'E' && bEv.ph === 'B') return -1
}

// In all other cases, retain the original sort order.
if (a.sortIndex < b.sortIndex) return -1
if (a.sortIndex > b.sortIndex) return 1
return 0
})

if (durationEvents.length > 0) {
const firstTs = durationEvents[0].ts
for (let ev of durationEvents) {
let firstTs = Number.MAX_VALUE
for (let {ev} of durationEvents) {
firstTs = Math.min(firstTs, ev.ts)
}
for (let {ev} of durationEvents) {
ev.ts -= firstTs
}
}
Expand Down Expand Up @@ -225,7 +260,7 @@ function eventListToProfileGroup(events: TraceEvent[]): ProfileGroup {
return state
}

for (let ev of durationEvents) {
for (let {ev} of durationEvents) {
const {profile, eventStack} = getOrCreateProfileState(ev.pid, ev.tid)
const frameInfo = frameInfoForEvent(ev)
switch (ev.ph) {
Expand Down

0 comments on commit 95183a4

Please sign in to comment.