chrome-devtools-frontend
Version:
Chrome DevTools UI
140 lines (128 loc) • 5.16 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 type * as Helpers from '../helpers/helpers.js';
import type * as Types from '../types/types.js';
import type {AuctionWorkletsData} from './AuctionWorkletsHandler.js';
import type * as Renderer from './RendererHandler.js';
import type {ParsedTrace} from './types.js';
export interface ThreadData {
pid: Types.Events.ProcessID;
tid: Types.Events.ThreadID;
entries: readonly Types.Events.Event[];
processIsOnMainFrame: boolean;
tree: Helpers.TreeHelpers.TraceEntryTree;
type: ThreadType;
name: string|null;
entryToNode: Map<Types.Events.Event, Helpers.TreeHelpers.TraceEntryNode>;
}
export const enum ThreadType {
MAIN_THREAD = 'MAIN_THREAD',
WORKER = 'WORKER',
RASTERIZER = 'RASTERIZER',
AUCTION_WORKLET = 'AUCTION_WORKLET',
OTHER = 'OTHER',
CPU_PROFILE = 'CPU_PROFILE',
THREAD_POOL = 'THREAD_POOL',
}
function getThreadTypeForRendererThread(
pid: Types.Events.ProcessID, thread: Renderer.RendererThread,
auctionWorkletsData: AuctionWorkletsData): ThreadType {
let threadType = ThreadType.OTHER;
if (thread.name === 'CrRendererMain') {
threadType = ThreadType.MAIN_THREAD;
} else if (thread.name === 'DedicatedWorker thread') {
threadType = ThreadType.WORKER;
} else if (thread.name?.startsWith('CompositorTileWorker')) {
threadType = ThreadType.RASTERIZER;
} else if (auctionWorkletsData.worklets.has(pid)) {
threadType = ThreadType.AUCTION_WORKLET;
} else if (thread.name?.startsWith('ThreadPool')) {
// TODO(paulirish): perhaps exclude ThreadPoolServiceThread entirely
threadType = ThreadType.THREAD_POOL;
}
return threadType;
}
export function threadsInRenderer(
rendererData: Renderer.RendererHandlerData, auctionWorkletsData: AuctionWorkletsData): readonly ThreadData[] {
const foundThreads: ThreadData[] = [];
// If we have Renderer threads, we prefer to use those. In the event that a
// trace is a CPU Profile trace, we will never have Renderer threads, so we
// know if there are no Renderer threads that we can fallback to using the
// data from the SamplesHandler.
if (rendererData.processes.size) {
for (const [pid, process] of rendererData.processes) {
for (const [tid, thread] of process.threads) {
if (!thread.tree) {
// Drop threads where we could not create the tree; this indicates
// unexpected data and we won't be able to support all the UI
// filtering we need.
continue;
}
const threadType = getThreadTypeForRendererThread(pid, thread, auctionWorkletsData);
foundThreads.push({
name: thread.name,
pid,
tid,
processIsOnMainFrame: process.isOnMainFrame,
entries: thread.entries,
tree: thread.tree,
type: threadType,
entryToNode: rendererData.entryToNode,
});
}
}
}
return foundThreads;
}
const threadsInTraceCache = new WeakMap<ParsedTrace, readonly ThreadData[]>();
/**
* Given trace parsed data, this helper will return a high level array of
* ThreadData. This is useful because it allows you to get a list of threads
* regardless of if the trace is a CPU Profile or a Tracing profile. Thus you
* can use this helper to iterate over threads in confidence that it will work
* for both trace types.
* The resulting data is cached per-trace, so you can safely call this multiple times.
*/
export function threadsInTrace(parsedTrace: ParsedTrace): readonly ThreadData[] {
const cached = threadsInTraceCache.get(parsedTrace);
if (cached) {
return cached;
}
// If we have Renderer threads, we prefer to use those.
const threadsFromRenderer = threadsInRenderer(parsedTrace.Renderer, parsedTrace.AuctionWorklets);
if (threadsFromRenderer.length) {
threadsInTraceCache.set(parsedTrace, threadsFromRenderer);
return threadsFromRenderer;
}
// If it's a CPU Profile trace, there will be no Renderer threads.
// We can fallback to using the data from the SamplesHandler.
const foundThreads: ThreadData[] = [];
if (parsedTrace.Samples.profilesInProcess.size) {
for (const [pid, process] of parsedTrace.Samples.profilesInProcess) {
for (const [tid, thread] of process) {
if (!thread.profileTree) {
// Drop threads where we could not create the tree; this indicates
// unexpected data and we won't be able to support all the UI
// filtering we need.
continue;
}
foundThreads.push({
pid,
tid,
// CPU Profile threads do not have a name.
name: null,
entries: thread.profileCalls,
// There is no concept of a "Main Frame" in a CPU profile.
processIsOnMainFrame: false,
tree: thread.profileTree,
type: ThreadType.CPU_PROFILE,
entryToNode: parsedTrace.Samples.entryToNode,
});
}
}
}
threadsInTraceCache.set(parsedTrace, foundThreads);
return foundThreads;
}