Skip to content

Commit 79756bc

Browse files
committed
Fix profiling <-> long task association regression
1 parent d475125 commit 79756bc

File tree

4 files changed

+106
-55
lines changed

4 files changed

+106
-55
lines changed

packages/rum-core/src/domain/longTask/longTaskCollection.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { RelativeTime, ServerDuration } from '@datadog/browser-core'
1+
import type { Duration, RelativeTime, ServerDuration } from '@datadog/browser-core'
22
import { registerCleanupTask } from '@datadog/browser-core/test'
33
import {
44
collectAndValidateRawRumEvents,
@@ -104,7 +104,7 @@ describe('longTaskCollection', () => {
104104
notifyPerformanceEntries([entry])
105105

106106
const longTask = (rawRumEvents[0].rawRumEvent as RawRumLongTaskEvent).long_task
107-
const longTasks = longTaskCollection.longTaskContexts.findLongTasks(1234 as RelativeTime)
107+
const longTasks = longTaskCollection.longTaskContexts.findLongTasks(1234 as RelativeTime, 100 as Duration)
108108
expect(longTasks).toContain({
109109
id: longTask.id,
110110
startClocks: jasmine.objectContaining({ relative: entry.startTime }),
@@ -164,7 +164,7 @@ describe('longTaskCollection', () => {
164164
notifyPerformanceEntries([entry])
165165

166166
const longTask = (rawRumEvents[0].rawRumEvent as RawRumLongTaskEvent).long_task
167-
const longTasks = longTaskCollection.longTaskContexts.findLongTasks(1234 as RelativeTime)
167+
const longTasks = longTaskCollection.longTaskContexts.findLongTasks(1234 as RelativeTime, 100 as Duration)
168168
expect(longTasks).toContain({
169169
id: longTask.id,
170170
startClocks: jasmine.objectContaining({ relative: entry.startTime }),

packages/rum-core/src/domain/longTask/longTaskCollection.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export interface LongTaskContext {
3333
}
3434

3535
export interface LongTaskContexts {
36-
findLongTasks: (startTime: RelativeTime) => LongTaskContext[]
36+
findLongTasks: (startTime: RelativeTime, duration: Duration) => LongTaskContext[]
3737
}
3838

3939
export function startLongTaskCollection(lifeCycle: LifeCycle, configuration: RumConfiguration) {
@@ -70,7 +70,7 @@ export function startLongTaskCollection(lifeCycle: LifeCycle, configuration: Rum
7070
})
7171

7272
const longTaskContexts: LongTaskContexts = {
73-
findLongTasks: (startTime: RelativeTime) => history.findAll(startTime),
73+
findLongTasks: (startTime: RelativeTime, duration: Duration) => history.findAll(startTime, duration),
7474
}
7575

7676
return {

packages/rum/src/domain/profiling/profiler.spec.ts

Lines changed: 98 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,31 @@
1-
import type { ViewHistoryEntry } from '@datadog/browser-rum-core'
1+
import type { LongTaskContext, ViewHistoryEntry } from '@datadog/browser-rum-core'
22
import { LifeCycle, LifeCycleEventType, RumPerformanceEntryType, createHooks } from '@datadog/browser-rum-core'
3-
import type { Duration, RelativeTime, TimeStamp } from '@datadog/browser-core'
4-
import { clocksOrigin, createIdentityEncoder, deepClone, relativeNow, timeStampNow } from '@datadog/browser-core'
3+
import type { Duration, RelativeTime } from '@datadog/browser-core'
4+
import {
5+
addDuration,
6+
clocksNow,
7+
clocksOrigin,
8+
createIdentityEncoder,
9+
createValueHistory,
10+
deepClone,
11+
relativeNow,
12+
timeStampNow,
13+
} from '@datadog/browser-core'
514
import {
615
setPageVisibility,
716
restorePageVisibility,
817
createNewEvent,
918
interceptRequests,
1019
DEFAULT_FETCH_MOCK,
1120
readFormDataRequest,
21+
mockClock,
1222
} from '@datadog/browser-core/test'
13-
import type { RumPerformanceEntry } from 'packages/rum-core/src/browser/performanceObservable'
14-
import {
15-
createPerformanceEntry,
16-
createRumSessionManagerMock,
17-
mockPerformanceObserver,
18-
mockRumConfiguration,
19-
mockViewHistory,
20-
} from '../../../../rum-core/test'
23+
import { LONG_TASK_ID_HISTORY_TIME_OUT_DELAY } from 'packages/rum-core/src/domain/longTask/longTaskCollection'
24+
import { createRumSessionManagerMock, mockRumConfiguration, mockViewHistory } from '../../../../rum-core/test'
2125
import { mockProfiler } from '../../../test'
2226
import { mockedTrace } from './test-utils/mockedTrace'
2327
import { createRumProfiler } from './profiler'
24-
import type { ProfilerTrace, RUMProfiler, RumProfilerTrace } from './types'
28+
import type { ProfilerTrace, RumProfilerTrace } from './types'
2529
import type { ProfilingContextManager } from './profilingContext'
2630
import { startProfilingContext } from './profilingContext'
2731
import type { ProfileEventPayload } from './transport/assembly'
@@ -44,18 +48,11 @@ describe('profiler', () => {
4448

4549
let lifeCycle = new LifeCycle()
4650

47-
function setupProfiler(currentView?: ViewHistoryEntry): {
48-
profiler: RUMProfiler
49-
notifyPerformanceEntries: (entries: RumPerformanceEntry[]) => void
50-
profilingContextManager: ProfilingContextManager
51-
mockedRumProfilerTrace: RumProfilerTrace
52-
longTaskContexts: { findLongTasks: jasmine.Spy }
53-
} {
51+
function setupProfiler(currentView?: ViewHistoryEntry) {
5452
const sessionManager = createRumSessionManagerMock().setId('session-id-1')
5553
lifeCycle = new LifeCycle()
5654
const hooks = createHooks()
5755
const profilingContextManager: ProfilingContextManager = startProfilingContext(hooks)
58-
const { notifyPerformanceEntries } = mockPerformanceObserver()
5956

6057
const mockProfilerTrace: ProfilerTrace = deepClone(mockedTrace)
6158

@@ -89,9 +86,21 @@ describe('profiler', () => {
8986
mockProfiler(mockProfilerTrace)
9087

9188
// Mock longTaskContexts
92-
const longTaskContexts = {
93-
findLongTasks: jasmine.createSpy('findLongTasks').and.returnValue([]),
89+
function mockLongTaskContexts() {
90+
const longTaskContexts = createValueHistory<LongTaskContext>({
91+
expireDelay: LONG_TASK_ID_HISTORY_TIME_OUT_DELAY,
92+
})
93+
94+
return {
95+
findLongTasks: (startTime: RelativeTime, duration: Duration): LongTaskContext[] =>
96+
longTaskContexts.findAll(startTime, duration),
97+
addLongTask: (longTask: LongTaskContext) =>
98+
longTaskContexts
99+
.add(longTask, longTask.startClocks.relative)
100+
.close(addDuration(longTask.startClocks.relative, longTask.duration)),
101+
}
94102
}
103+
const longTaskContexts = mockLongTaskContexts()
95104

96105
// Start collection of profile.
97106
const profiler = createRumProfiler(
@@ -110,7 +119,7 @@ describe('profiler', () => {
110119
minProfileDurationMs: 0,
111120
}
112121
)
113-
return { profiler, notifyPerformanceEntries, profilingContextManager, mockedRumProfilerTrace, longTaskContexts }
122+
return { profiler, profilingContextManager, mockedRumProfilerTrace, longTaskContexts }
114123
}
115124

116125
it('should start profiling collection and collect data on stop', async () => {
@@ -185,44 +194,86 @@ describe('profiler', () => {
185194
expect(request['wall-time.json']).toEqual(mockedRumProfilerTrace)
186195
})
187196

188-
it('should collect long task from core and then attach long task id to the Profiler trace', async () => {
189-
const { profiler, notifyPerformanceEntries, profilingContextManager, longTaskContexts } = setupProfiler()
190-
longTaskContexts.findLongTasks.and.returnValue([
191-
{
192-
id: 'long-task-id-1',
193-
startClocks: { relative: 12345 as RelativeTime, timeStamp: 12345 as TimeStamp },
194-
duration: 1000 as Duration,
195-
},
196-
])
197+
it('should collect long task happening during a profiling session', async () => {
198+
const clock = mockClock()
199+
const { profiler, profilingContextManager, longTaskContexts } = setupProfiler()
197200

201+
// Start collection of profile.
198202
profiler.start()
199-
200203
await waitForBoolean(() => profiler.isRunning())
201204

202205
expect(profilingContextManager.get()?.status).toBe('running')
206+
longTaskContexts.addLongTask({
207+
id: 'long-task-id-1',
208+
startClocks: clocksNow(),
209+
duration: 50 as Duration,
210+
entryType: RumPerformanceEntryType.LONG_ANIMATION_FRAME,
211+
})
212+
clock.tick(50)
203213

204-
// Notify Profiler that some long tasks have been collected (via its PerformanceObserver)
205-
notifyPerformanceEntries([
206-
createPerformanceEntry(RumPerformanceEntryType.LONG_ANIMATION_FRAME, {
207-
startTime: 12345 as RelativeTime,
208-
entryType: RumPerformanceEntryType.LONG_ANIMATION_FRAME,
209-
}),
210-
])
214+
longTaskContexts.addLongTask({
215+
id: 'long-task-id-2',
216+
startClocks: clocksNow(),
217+
duration: 100 as Duration,
218+
entryType: RumPerformanceEntryType.LONG_ANIMATION_FRAME,
219+
})
211220

212-
// Stop collection of profile.
221+
// Stop first profiling session.
222+
clock.tick(105)
213223
await profiler.stop()
224+
await waitForBoolean(() => profiler.isStopped())
214225

215-
// Wait for stop of collection.
226+
// start a new profiling session
227+
profiler.start()
228+
await waitForBoolean(() => profiler.isRunning())
229+
230+
longTaskContexts.addLongTask({
231+
id: 'long-task-id-3',
232+
startClocks: clocksNow(),
233+
duration: 100 as Duration,
234+
entryType: RumPerformanceEntryType.LONG_ANIMATION_FRAME,
235+
})
236+
237+
clock.tick(500)
238+
239+
// stop the second profiling session
240+
await profiler.stop()
216241
await waitForBoolean(() => profiler.isStopped())
217242

243+
expect(interceptor.requests.length).toBe(2)
218244
expect(profilingContextManager.get()?.status).toBe('stopped')
219245

220-
const request = await readFormDataRequest<ProfileEventPayload>(interceptor.requests[0])
221-
const trace = request['wall-time.json']
246+
const requestOne = await readFormDataRequest<ProfileEventPayload>(interceptor.requests[0])
247+
const requestTwo = await readFormDataRequest<ProfileEventPayload>(interceptor.requests[1])
222248

223-
expect(request.event.long_task?.id.length).toBe(1)
224-
expect(trace.longTasks[0].id).toBeDefined()
225-
expect(trace.longTasks[0].startClocks.relative).toBe(12345 as RelativeTime)
249+
const traceOne = requestOne['wall-time.json']
250+
const traceTwo = requestTwo['wall-time.json']
251+
252+
expect(requestOne.event.long_task?.id.length).toBe(2)
253+
expect(traceOne.longTasks).toEqual([
254+
{
255+
id: 'long-task-id-2',
256+
startClocks: jasmine.any(Object),
257+
duration: 100 as Duration,
258+
entryType: RumPerformanceEntryType.LONG_ANIMATION_FRAME,
259+
},
260+
{
261+
id: 'long-task-id-1',
262+
startClocks: jasmine.any(Object),
263+
duration: 50 as Duration,
264+
entryType: RumPerformanceEntryType.LONG_ANIMATION_FRAME,
265+
},
266+
])
267+
268+
expect(requestTwo.event.long_task?.id.length).toBe(1)
269+
expect(traceTwo.longTasks).toEqual([
270+
{
271+
id: 'long-task-id-3',
272+
startClocks: jasmine.any(Object),
273+
duration: 100 as Duration,
274+
entryType: RumPerformanceEntryType.LONG_ANIMATION_FRAME,
275+
},
276+
])
226277
})
227278

228279
it('should collect views and set default view name in the Profile', async () => {

packages/rum/src/domain/profiling/profiler.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -230,9 +230,9 @@ export function createRumProfiler(
230230
.stop()
231231
.then((trace) => {
232232
const endClocks = clocksNow()
233-
const longTasks = longTaskContexts.findLongTasks(startClocks.relative)
234-
const isBelowDurationThreshold =
235-
elapsed(startClocks.timeStamp, endClocks.timeStamp) < profilerConfiguration.minProfileDurationMs
233+
const duration = elapsed(startClocks.timeStamp, endClocks.timeStamp)
234+
const longTasks = longTaskContexts.findLongTasks(startClocks.relative, duration)
235+
const isBelowDurationThreshold = duration < profilerConfiguration.minProfileDurationMs
236236
const isBelowSampleThreshold = getNumberOfSamples(trace.samples) < profilerConfiguration.minNumberOfSamples
237237

238238
if (longTasks.length === 0 && (isBelowDurationThreshold || isBelowSampleThreshold)) {

0 commit comments

Comments
 (0)