chrome-devtools-frontend
Version:
Chrome DevTools UI
611 lines (563 loc) • 29.4 kB
text/typescript
// Copyright 2023 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import * as Platform from '../../../core/platform/platform.js';
import * as Root from '../../../core/root/root.js';
import * as SDK from '../../../core/sdk/sdk.js';
import * as Bindings from '../../../models/bindings/bindings.js';
import * as Trace from '../../../models/trace/trace.js';
import * as Workspace from '../../../models/workspace/workspace.js';
import {describeWithEnvironment} from '../../../testing/EnvironmentHelpers.js';
import {
makeMockRendererHandlerData as makeRendererHandlerData,
makeProfileCall,
setupIgnoreListManagerEnvironment,
} from '../../../testing/TraceHelpers.js';
import {TraceLoader} from '../../../testing/TraceLoader.js';
import * as PerfUI from '../../../ui/legacy/components/perf_ui/perf_ui.js';
import * as Timeline from '../timeline.js';
const {urlString} = Platform.DevToolsPath;
function initTrackAppender(
flameChartData: PerfUI.FlameChart.FlameChartTimelineData,
parsedTrace: Trace.Handlers.Types.ParsedTrace,
entryData: Trace.Types.Events.Event[],
entryTypeByLevel: Timeline.TimelineFlameChartDataProvider.EntryType[],
): {
threadAppenders: Timeline.ThreadAppender.ThreadAppender[],
compatibilityTracksAppender: Timeline.CompatibilityTracksAppender.CompatibilityTracksAppender,
} {
setupIgnoreListManagerEnvironment();
const entityMapper = new Timeline.Utils.EntityMapper.EntityMapper(parsedTrace);
const compatibilityTracksAppender = new Timeline.CompatibilityTracksAppender.CompatibilityTracksAppender(
flameChartData, parsedTrace, entryData, entryTypeByLevel, entityMapper);
return {threadAppenders: compatibilityTracksAppender.threadAppenders(), compatibilityTracksAppender};
}
async function renderThreadAppendersFromTrace(context: Mocha.Context|Mocha.Suite, trace: string):
Promise<ReturnType<typeof renderThreadAppendersFromParsedData>> {
const {parsedTrace} = await TraceLoader.traceEngine(context, trace);
return renderThreadAppendersFromParsedData(parsedTrace);
}
function renderThreadAppendersFromParsedData(parsedTrace: Trace.Handlers.Types.ParsedTrace): {
entryTypeByLevel: Timeline.TimelineFlameChartDataProvider.EntryType[],
flameChartData: PerfUI.FlameChart.FlameChartTimelineData,
threadAppenders: Timeline.ThreadAppender.ThreadAppender[],
entryData: Trace.Types.Events.Event[],
parsedTrace: Readonly<Trace.Handlers.Types.ParsedTrace>,
compatibilityTracksAppender: Timeline.CompatibilityTracksAppender.CompatibilityTracksAppender,
} {
const entryTypeByLevel: Timeline.TimelineFlameChartDataProvider.EntryType[] = [];
const entryData: Trace.Types.Events.Event[] = [];
const flameChartData = PerfUI.FlameChart.FlameChartTimelineData.createEmpty();
const {threadAppenders, compatibilityTracksAppender} =
initTrackAppender(flameChartData, parsedTrace, entryData, entryTypeByLevel);
let level = 0;
for (const appender of threadAppenders) {
level = appender.appendTrackAtLevel(level);
}
return {
entryTypeByLevel,
flameChartData,
compatibilityTracksAppender,
parsedTrace,
threadAppenders,
entryData,
};
}
describeWithEnvironment('ThreadAppender', function() {
it('creates a thread appender for each thread in a trace', async function() {
const {threadAppenders} = await renderThreadAppendersFromTrace(this, 'simple-js-program.json.gz');
const expectedAppenderNames = [
'Thread',
'Thread',
'Thread',
];
assert.deepEqual(threadAppenders.map(g => g.appenderName), expectedAppenderNames);
});
it('renders tracks for threads in correct order', async function() {
const {flameChartData} = await renderThreadAppendersFromTrace(this, 'multiple-navigations-with-iframes.json.gz');
assert.strictEqual(flameChartData.groups[0].name, 'Main — http://localhost:5000/');
assert.strictEqual(flameChartData.groups[1].name, 'Frame — https://www.example.com/');
});
it('renders tracks for threads in correct order when a process url is about:blank', async function() {
const {flameChartData} = await renderThreadAppendersFromTrace(this, 'about-blank-first.json.gz');
const groupNames = flameChartData.groups.map(g => g.name.replace(/(new-tab-page\/).*/, '$1'));
assert.deepEqual(groupNames.slice(0, 3), [
'Frame — chrome-untrusted://new-tab-page/',
'Main — chrome://new-tab-page/',
'Main — about:blank',
]);
});
it('marks all levels used by the track with the TrackAppender type', async function() {
const {entryTypeByLevel} = await renderThreadAppendersFromTrace(this, 'simple-js-program.json.gz');
// This includes all tracks rendered by the ThreadAppender.
const execptedLevelTypes = [
Timeline.TimelineFlameChartDataProvider.EntryType.TRACK_APPENDER,
Timeline.TimelineFlameChartDataProvider.EntryType.TRACK_APPENDER,
Timeline.TimelineFlameChartDataProvider.EntryType.TRACK_APPENDER,
Timeline.TimelineFlameChartDataProvider.EntryType.TRACK_APPENDER,
Timeline.TimelineFlameChartDataProvider.EntryType.TRACK_APPENDER,
Timeline.TimelineFlameChartDataProvider.EntryType.TRACK_APPENDER,
Timeline.TimelineFlameChartDataProvider.EntryType.TRACK_APPENDER,
Timeline.TimelineFlameChartDataProvider.EntryType.TRACK_APPENDER,
Timeline.TimelineFlameChartDataProvider.EntryType.TRACK_APPENDER,
Timeline.TimelineFlameChartDataProvider.EntryType.TRACK_APPENDER,
];
assert.deepEqual(entryTypeByLevel, execptedLevelTypes);
});
it('creates a flamechart groups for track headers and titles', async function() {
const {flameChartData} = await renderThreadAppendersFromTrace(this, 'cls-single-frame.json.gz');
const expectedTrackNames = [
'Main — https://output.jsbin.com/zajamil/quiet',
'Raster',
'Rasterizer thread 1',
'Rasterizer thread 2',
'Thread pool',
'Thread pool worker 1',
];
assert.deepEqual(flameChartData.groups.map(g => g.name), expectedTrackNames);
});
it('builds flamechart groups for nested tracks correctly', async function() {
const {flameChartData} = await renderThreadAppendersFromTrace(this, 'cls-single-frame.json.gz');
// This group corresponds to the header that wraps the raster tracks
// together. It isn't selectable and isn't nested
assert.strictEqual(flameChartData.groups[1].name, 'Raster');
assert.isFalse(flameChartData.groups[1].selectable);
assert.strictEqual(flameChartData.groups[1].style.nestingLevel, 0);
// These groups correspond to the raster tracks titles, or the
// individual raster tracks themselves. They are selectable and
// nested
assert.strictEqual(flameChartData.groups[2].name, 'Rasterizer thread 1');
assert.isTrue(flameChartData.groups[2].selectable);
assert.strictEqual(flameChartData.groups[2].style.nestingLevel, 1);
assert.strictEqual(flameChartData.groups[3].name, 'Rasterizer thread 2');
assert.isTrue(flameChartData.groups[3].selectable);
assert.strictEqual(flameChartData.groups[3].style.nestingLevel, 1);
});
it('assigns correct names to multiple types of threads', async function() {
const {flameChartData} = await renderThreadAppendersFromTrace(this, 'simple-js-program.json.gz');
const expectedTrackNames = [
'Main — https://www.google.com',
'Thread pool',
'Thread pool worker 1',
'Thread pool worker 2',
];
assert.deepEqual(flameChartData.groups.map(g => g.name), expectedTrackNames);
});
it('adds thread IDs onto tracks when the trace is generic', async () => {
const {flameChartData} = await renderThreadAppendersFromTrace(this, 'generic-about-tracing.json.gz');
// This trace has a lot of tracks, so just test one.
assert.isTrue(flameChartData.groups.map(g => g.name)
.includes('CrBrowserMain (1213787)' as Platform.UIString.LocalizedString));
});
it('assigns the right color for events when the trace is generic', async () => {
const {threadAppenders, parsedTrace} = await renderThreadAppendersFromTrace(this, 'generic-about-tracing.json.gz');
const event = parsedTrace.Renderer.allTraceEntries.find(entry => {
return entry.name === 'ThreadControllerImpl::RunTask';
});
if (!event) {
throw new Error('Could not find event.');
}
assert.strictEqual(threadAppenders[0].colorForEvent(event), 'hsl(278, 40%, 70%)');
});
it('assigns correct names to worker threads', async function() {
const {flameChartData} = await renderThreadAppendersFromTrace(this, 'two-workers.json.gz');
const expectedTrackNames = [
'Main — https://chromedevtools.github.io/performance-stories/two-workers/index.html',
'Worker — https://chromedevtools.github.io/performance-stories/two-workers/fib-worker.js',
'Worker — https://chromedevtools.github.io/performance-stories/two-workers/fib-worker.js',
'Thread pool',
'Thread pool worker 1',
'Thread pool worker 2',
];
assert.deepEqual(flameChartData.groups.map(g => g.name), expectedTrackNames);
});
it('returns the correct title for a renderer event', async function() {
const {threadAppenders, parsedTrace} = await renderThreadAppendersFromTrace(this, 'simple-js-program.json.gz');
const events = parsedTrace.Renderer?.allTraceEntries;
if (!events) {
throw new Error('Could not find renderer events');
}
const title = threadAppenders[0].titleForEvent(events[0]);
assert.strictEqual(title, 'Task');
});
it('adds the type for EventDispatch events to the title', async function() {
const {threadAppenders, parsedTrace} = await renderThreadAppendersFromTrace(this, 'one-second-interaction.json.gz');
const events = parsedTrace.Renderer?.allTraceEntries;
if (!events) {
throw new Error('Could not find renderer events');
}
const clickEvent = events.find(event => {
return Trace.Types.Events.isDispatch(event) && event.args.data.type === 'click';
});
if (!clickEvent) {
throw new Error('Could not find expected click event');
}
const title = threadAppenders[0].titleForEvent(clickEvent);
assert.strictEqual(title, 'Event: click');
});
it('returns the correct title for a profile call', async function() {
const {threadAppenders, parsedTrace} = await renderThreadAppendersFromTrace(this, 'simple-js-program.json.gz');
const rendererHandler = parsedTrace.Renderer;
if (!rendererHandler) {
throw new Error('RendererHandler is undefined');
}
const [process] = rendererHandler.processes.values();
const [thread] = process.threads.values();
const profileCalls = thread.entries.filter(entry => Trace.Types.Events.isProfileCall(entry));
if (!profileCalls) {
throw new Error('Could not find renderer events');
}
const anonymousCall = threadAppenders[0].titleForEvent(profileCalls[0]);
assert.strictEqual(anonymousCall, '(anonymous)');
const n = threadAppenders[0].titleForEvent(profileCalls[7]);
assert.strictEqual(n, 'n');
});
it('will use the function name from the CPUProfile if it has been set', async function() {
const {threadAppenders, parsedTrace} = await renderThreadAppendersFromTrace(this, 'simple-js-program.json.gz');
const {Renderer, Samples} = parsedTrace;
const [process] = Renderer.processes.values();
const [thread] = process.threads.values();
const profileCalls = thread.entries.filter(Trace.Types.Events.isProfileCall);
if (!profileCalls || profileCalls.length === 0) {
throw new Error('Could not find renderer events');
}
const entry = profileCalls[0];
const cpuProfileNode =
Samples.profilesInProcess.get(entry.pid)?.get(entry.tid)?.parsedProfile.nodeById(entry.nodeId);
if (!cpuProfileNode) {
throw new Error('Could not find CPU Profile Node');
}
const anonymousCall = threadAppenders[0].titleForEvent(entry);
assert.strictEqual(anonymousCall, '(anonymous)');
const originalName = cpuProfileNode.functionName;
cpuProfileNode.setFunctionName('new-resolved-function-name');
assert.strictEqual(threadAppenders[0].titleForEvent(entry), 'new-resolved-function-name');
// Reset the value for future tests.
cpuProfileNode.setFunctionName(originalName);
});
function getDefaultInfo() {
const defaultInfo: Timeline.CompatibilityTracksAppender.PopoverInfo = {
title: 'title',
formattedTime: 'time',
warningElements: [],
additionalElements: [],
url: null,
};
return defaultInfo;
}
it('shows self time only for events with self time above the threshold when hovered', async function() {
const {threadAppenders, parsedTrace} = await renderThreadAppendersFromTrace(this, 'simple-js-program.json.gz');
const events = parsedTrace.Renderer?.allTraceEntries;
if (!events) {
throw new Error('Could not find renderer events');
}
const infoForShortEvent = getDefaultInfo();
threadAppenders[0].setPopoverInfo(events[0], infoForShortEvent);
assert.strictEqual(infoForShortEvent.formattedTime, '0.27\u00A0ms');
const longTask = events.find(e => (e.dur || 0) > 1_000_000);
if (!longTask) {
throw new Error('Could not find long task');
}
const infoForLongEvent = getDefaultInfo();
threadAppenders[0].setPopoverInfo(longTask, infoForLongEvent);
assert.strictEqual(infoForLongEvent.formattedTime, '1.30\u00A0s (self 47\xA0μs)');
});
it('shows the correct title for a ParseHTML event', async function() {
const {threadAppenders, parsedTrace} = await renderThreadAppendersFromTrace(this, 'simple-js-program.json.gz');
const events = parsedTrace.Renderer?.allTraceEntries;
if (!events) {
throw new Error('Could not find renderer events');
}
const infoForShortEvent = getDefaultInfo();
threadAppenders[0].setPopoverInfo(events[0], infoForShortEvent);
assert.strictEqual(infoForShortEvent.formattedTime, '0.27\u00A0ms');
const longTask = events.find(e => (e.dur || 0) > 1_000_000);
if (!longTask) {
throw new Error('Could not find long task');
}
const infoForLongEvent = getDefaultInfo();
threadAppenders[0].setPopoverInfo(longTask, infoForLongEvent);
assert.strictEqual(infoForLongEvent.formattedTime, '1.30\u00A0s (self 47\xA0μs)');
});
it('shows the right time for a profile call when hovered', async function() {
const {threadAppenders, parsedTrace} = await renderThreadAppendersFromTrace(this, 'simple-js-program.json.gz');
const rendererHandler = parsedTrace.Renderer;
if (!rendererHandler) {
throw new Error('RendererHandler is undefined');
}
const [process] = rendererHandler.processes.values();
const [thread] = process.threads.values();
const profileCalls = thread.entries.filter(entry => Trace.Types.Events.isProfileCall(entry));
if (!profileCalls) {
throw new Error('Could not find renderer events');
}
const info = getDefaultInfo();
threadAppenders[0].setPopoverInfo(profileCalls[0], info);
assert.strictEqual(info.formattedTime, '15\u00A0μs');
});
it('candy-stripes long tasks', async function() {
const {parsedTrace, flameChartData, entryData} =
await renderThreadAppendersFromTrace(this, 'simple-js-program.json.gz');
const events = parsedTrace.Renderer?.allTraceEntries;
if (!events) {
throw new Error('Could not find renderer events');
}
const longTask = events.find(e => (e.dur || 0) > 1_000_000);
if (!longTask) {
throw new Error('Could not find long task');
}
const entryIndex = entryData.indexOf(longTask);
const decorationsForEntry = flameChartData.entryDecorations[entryIndex];
assert.deepEqual(decorationsForEntry, [
{type: PerfUI.FlameChart.FlameChartDecorationType.WARNING_TRIANGLE},
{
type: PerfUI.FlameChart.FlameChartDecorationType.CANDY,
startAtTime: Trace.Types.Timing.Micro(50_000),
},
]);
});
it('does not candy-stripe tasks below the long task threshold', async function() {
const {parsedTrace, flameChartData, entryData} =
await renderThreadAppendersFromTrace(this, 'simple-js-program.json.gz');
const events = parsedTrace.Renderer?.allTraceEntries;
if (!events) {
throw new Error('Could not find renderer events');
}
const entryIndex = entryData.indexOf(events[0]);
const decorationsForEntry = flameChartData.entryDecorations[entryIndex];
assert.isUndefined(decorationsForEntry);
});
it('does not append a track if there are no visible events on it', async function() {
const {flameChartData} = await renderThreadAppendersFromTrace(this, 'one-second-interaction.json.gz');
const expectedTrackNames = [
'Main — https://chromedevtools.github.io/performance-stories/long-interaction/index.html?x=40',
'Thread pool',
// There are multiple ThreadPoolForegroundWorker threads present in
// the trace, but only one of these has trace events we deem as
// "visible".
'Thread pool worker 1',
// This second "worker" is the ThreadPoolServiceThread. TODO: perhaps hide ThreadPoolServiceThread completely?
'Thread pool worker 2',
];
assert.deepEqual(flameChartData.groups.map(g => g.name), expectedTrackNames);
});
describe('ignore listing', () => {
let ignoreListManager: Bindings.IgnoreListManager.IgnoreListManager;
beforeEach(() => {
const targetManager = SDK.TargetManager.TargetManager.instance({forceNew: true});
const workspace = Workspace.Workspace.WorkspaceImpl.instance({forceNew: true});
const resourceMapping = new Bindings.ResourceMapping.ResourceMapping(targetManager, workspace);
const debuggerWorkspaceBinding = Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance({
forceNew: true,
resourceMapping,
targetManager,
});
ignoreListManager = Bindings.IgnoreListManager.IgnoreListManager.instance({
forceNew: true,
debuggerWorkspaceBinding,
});
});
afterEach(() => {
SDK.TargetManager.TargetManager.removeInstance();
Workspace.Workspace.WorkspaceImpl.removeInstance();
Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.removeInstance();
Bindings.IgnoreListManager.IgnoreListManager.removeInstance();
});
it('removes entries from the data that match the ignored URL', async function() {
const initialTimelineData = await renderThreadAppendersFromTrace(this, 'react-hello-world.json.gz');
const initialFlamechartData = initialTimelineData.flameChartData;
const eventCountBeforeIgnoreList = initialFlamechartData.entryStartTimes.length;
const SCRIPT_TO_IGNORE = urlString`https://unpkg.com/react@18.2.0/umd/react.development.js`;
// Clear the data provider cache and add the React script to the ignore list.
ignoreListManager.ignoreListURL(SCRIPT_TO_IGNORE);
const finalTimelineData = await renderThreadAppendersFromTrace(this, 'react-hello-world.json.gz');
const finalFlamechartData = finalTimelineData.flameChartData;
const eventCountAfterIgnoreList = finalFlamechartData.entryStartTimes.length;
// Ensure that the amount of events we show on the flame chart is less
// than before, now we have added the React URL to the ignore list.
assert.isBelow(eventCountAfterIgnoreList, eventCountBeforeIgnoreList);
// // Clear the data provider cache and unignore the script again
ignoreListManager.unIgnoreListURL(SCRIPT_TO_IGNORE);
const finalTimelineData2 = await renderThreadAppendersFromTrace(this, 'react-hello-world.json.gz');
const finalFlamechartData2 = finalTimelineData2.flameChartData;
const eventCountAfterIgnoreList2 = finalFlamechartData2.entryStartTimes.length;
// // Ensure that now we have un-ignored the URL that we get the full set of events again.
assert.strictEqual(eventCountAfterIgnoreList2, eventCountBeforeIgnoreList);
});
it('appends a tree that contains ignore listed entries correctly', async function() {
const SCRIPT_TO_IGNORE = urlString`https://some-framework/bundled.js`;
// Create the following hierarchy with profile calls. Events marked
// with \\\\ represent ignored listed events.
// |----------A-----------|
// |\\\\\B\\\\\||----F----|
// |\\C\\||\D\|
// |--E--|
//
// Applying ignore listing in the appender, should yield the
// following flame chart:
// |----------A-----------|
// |\\\\\B\\\\||----F----|
// |--E--|
const callFrameA = makeProfileCall('A', 100, 200);
const callFrameB = makeProfileCall('IgnoreListedB', 100, 100);
callFrameB.callFrame.url = SCRIPT_TO_IGNORE;
const callFrameC = makeProfileCall('IgnoreListedC', 100, 50);
callFrameC.callFrame.url = SCRIPT_TO_IGNORE;
const callFrameD = makeProfileCall('IgnoreListedD', 151, 49);
callFrameD.callFrame.url = SCRIPT_TO_IGNORE;
const callFrameE = makeProfileCall('E', 100, 25);
const callFrameF = makeProfileCall('F', 200, 100);
const allEntries = [callFrameA, callFrameB, callFrameC, callFrameE, callFrameD, callFrameF];
const rendererData = makeRendererHandlerData(allEntries);
const workersData: Trace.Handlers.ModelHandlers.Workers.WorkersData = {
workerSessionIdEvents: [],
workerIdByThread: new Map(),
workerURLById: new Map(),
};
const warningsData: Trace.Handlers.ModelHandlers.Warnings.WarningsData = {
perWarning: new Map(),
perEvent: new Map(),
};
// This only includes data used in the thread appender
const mockParsedTrace = {
Renderer: rendererData,
Workers: workersData,
Warnings: warningsData,
AuctionWorklets: {worklets: new Map()},
Meta: {
traceIsGeneric: false,
navigationsByNavigationId: new Map(),
},
NetworkRequests:
{entityMappings: {entityByEvent: new Map(), eventsByEntity: new Map(), createdEntityCache: new Map()}},
ExtensionTraceData: {entryToNode: new Map(), extensionMarkers: [], extensionTrackData: []},
} as unknown as Trace.Handlers.Types.ParsedTrace;
// Add the script to ignore list and then append the flamechart data
ignoreListManager.ignoreListURL(SCRIPT_TO_IGNORE);
const {entryData, flameChartData, threadAppenders} = renderThreadAppendersFromParsedData(mockParsedTrace);
const entryDataNames = entryData.map(entry => {
if (Trace.Types.Events.isProfileCall(entry)) {
return entry.callFrame.functionName;
}
return entry.name;
});
assert.deepEqual(entryDataNames, ['A', 'IgnoreListedB', 'E', 'F']);
assert.deepEqual(flameChartData.entryLevels, [0, 1, 2, 1]);
assert.deepEqual(flameChartData.entryStartTimes, [0.1, 0.1, 0.1, 0.2]);
assert.deepEqual(flameChartData.entryTotalTimes, [0.2, 0.1, 0.025, 0.1]);
assert.lengthOf(threadAppenders, 1);
assert.strictEqual(threadAppenders[0].titleForEvent(callFrameB), 'On ignore list (\\\/bundled\\.js$)');
});
});
describe('showAllEvents', () => {
beforeEach(() => {
const targetManager = SDK.TargetManager.TargetManager.instance({forceNew: true});
const workspace = Workspace.Workspace.WorkspaceImpl.instance({forceNew: true});
const resourceMapping = new Bindings.ResourceMapping.ResourceMapping(targetManager, workspace);
const debuggerWorkspaceBinding = Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance({
forceNew: true,
resourceMapping,
targetManager,
});
Bindings.IgnoreListManager.IgnoreListManager.instance({
forceNew: true,
debuggerWorkspaceBinding,
});
});
afterEach(() => {
SDK.TargetManager.TargetManager.removeInstance();
Workspace.Workspace.WorkspaceImpl.removeInstance();
Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.removeInstance();
Bindings.IgnoreListManager.IgnoreListManager.removeInstance();
});
it('appends unknown events to the flame chart data only when the experiment is enabled', async function() {
const fileName = 'react-hello-world.json.gz';
const bizarreName = 'BackForwardCacheBufferLimitTracker::DidRemoveFrameOrWorkerFromBackForwardCache';
// Look up a trace event with an name we are not tracking anywhere and make sure it's not
// appended to the timeline data.
const initialTimelineData = await renderThreadAppendersFromTrace(this, fileName);
let unknownEventIndex = initialTimelineData.entryData.findIndex(entry => {
return entry.name === bizarreName;
});
assert.strictEqual(unknownEventIndex, -1);
// Now enable the experiment and make sure the event is appended to the timeline data this time
Root.Runtime.experiments.enableForTest('timeline-show-all-events');
const finalTimelineData = await renderThreadAppendersFromTrace(this, fileName);
const finalFlamechartData = finalTimelineData.flameChartData;
unknownEventIndex = finalTimelineData.entryData.findIndex(entry => {
return entry.name === bizarreName;
});
assert.isAbove(unknownEventIndex, -1);
assert.exists(finalFlamechartData.entryStartTimes);
assert.exists(finalFlamechartData.entryTotalTimes);
Root.Runtime.experiments.disableForTest('timeline-show-all-events');
});
});
describe('AuctionWorklet threads', () => {
// We have to set these up because the ThreadAppender includes logic for
// ignoring events that relies on the IgnoreListManager.
beforeEach(() => {
const targetManager = SDK.TargetManager.TargetManager.instance({forceNew: true});
const workspace = Workspace.Workspace.WorkspaceImpl.instance({forceNew: true});
const resourceMapping = new Bindings.ResourceMapping.ResourceMapping(targetManager, workspace);
const debuggerWorkspaceBinding = Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance({
forceNew: true,
resourceMapping,
targetManager,
});
Bindings.IgnoreListManager.IgnoreListManager.instance({
forceNew: true,
debuggerWorkspaceBinding,
});
});
afterEach(() => {
SDK.TargetManager.TargetManager.removeInstance();
Workspace.Workspace.WorkspaceImpl.removeInstance();
Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.removeInstance();
Bindings.IgnoreListManager.IgnoreListManager.removeInstance();
});
it('finds all the worklet threads', async function() {
const {threadAppenders} = await renderThreadAppendersFromTrace(this, 'fenced-frame-fledge.json.gz');
const workletAppenders = threadAppenders.filter(threadAppender => {
return threadAppender.trackName().includes('Worklet');
});
assert.lengthOf(workletAppenders, 6);
});
it('sets the title correctly for an Auction Worklet service', async function() {
const UTILITY_THREAD_PID = 776435 as Trace.Types.Events.ProcessID;
const UTILITY_THREAD_TID = 1 as Trace.Types.Events.ThreadID;
const {threadAppenders} = await renderThreadAppendersFromTrace(this, 'fenced-frame-fledge.json.gz');
const appender = threadAppenders.find(threadAppender => {
return threadAppender.processId() === UTILITY_THREAD_PID && threadAppender.threadId() === UTILITY_THREAD_TID;
});
if (!appender) {
throw new Error('Could not find expected thread appender');
}
assert.strictEqual(appender.trackName(), 'Auction Worklet service — https://ssp-fledge-demo.glitch.me');
});
it('sets the title correctly for an Auction Worklet seller service', async function() {
const SELLER_THREAD_PID = 776435 as Trace.Types.Events.ProcessID;
const SELLER_THREAD_TID = 6 as Trace.Types.Events.ThreadID;
const {threadAppenders} = await renderThreadAppendersFromTrace(this, 'fenced-frame-fledge.json.gz');
const appender = threadAppenders.find(threadAppender => {
return threadAppender.processId() === SELLER_THREAD_PID && threadAppender.threadId() === SELLER_THREAD_TID;
});
if (!appender) {
throw new Error('Could not find expected thread appender');
}
assert.strictEqual(appender.trackName(), 'Seller Worklet — https://ssp-fledge-demo.glitch.me');
});
it('sets the title correctly for an Auction Worklet bidder service', async function() {
const BIDDER_THREAD_PID = 776436 as Trace.Types.Events.ProcessID;
const BIDDER_THREAD_TID = 6 as Trace.Types.Events.ThreadID;
const {threadAppenders} = await renderThreadAppendersFromTrace(this, 'fenced-frame-fledge.json.gz');
const appender = threadAppenders.find(threadAppender => {
return threadAppender.processId() === BIDDER_THREAD_PID && threadAppender.threadId() === BIDDER_THREAD_TID;
});
if (!appender) {
throw new Error('Could not find expected thread appender');
}
assert.strictEqual(appender.trackName(), 'Bidder Worklet — https://dsp-fledge-demo.glitch.me');
});
});
});