Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Open multiple traces in an integrated scenario #14

Draft
wants to merge 5 commits into
base: reuse_timeline
Choose a base branch
from
Draft
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
10 changes: 5 additions & 5 deletions ui/src/base/logging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

import {SCM_REVISION, VERSION} from '../gen/perfetto_version';

export type ErrorHandler = (err: string) => void;
export type ErrorHandler = (globalsContext: string, err: string) => void;

let errorHandler: ErrorHandler = (_: string) => {};

Expand All @@ -39,9 +39,9 @@ export function setErrorHandler(handler: ErrorHandler) {
errorHandler = handler;
}

export function reportError(err: ErrorEvent|PromiseRejectionEvent|{}): void;
export function reportError(err: ErrorEvent|PromiseRejectionEvent|{}, handler: ErrorHandler): void;
export function reportError(err: ErrorEvent|PromiseRejectionEvent|{}, handler = errorHandler) {
export function reportError(globalsContext: string, err: ErrorEvent|PromiseRejectionEvent|{}): void;
export function reportError(globalsContext: string, err: ErrorEvent|PromiseRejectionEvent|{}, handler: ErrorHandler): void;
export function reportError(globalsContext: string, err: ErrorEvent|PromiseRejectionEvent|{}, handler = errorHandler) {
let errLog = '';
let errorObj = undefined;

Expand All @@ -64,7 +64,7 @@ export function reportError(err: ErrorEvent|PromiseRejectionEvent|{}, handler =
errLog += `UA: ${navigator.userAgent}\n`;

console.error(errLog, err);
handler?.(errLog);
handler?.(globalsContext, errLog);
}

// This function serves two purposes.
Expand Down
46 changes: 23 additions & 23 deletions ui/src/common/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ function clearTraceState(state: StateDraft) {
const chromeCategories = state.chromeCategories;
const newEngineMode = state.newEngineMode;

Object.assign(state, createEmptyState());
Object.assign(state, createEmptyState(state.globalsContext));
state.nextId = nextId;
state.recordConfig = recordConfig;
state.recordingTarget = recordingTarget;
Expand Down Expand Up @@ -174,8 +174,8 @@ function wasFiltered<T extends TrackState|TrackGroupState>(item: T): item is T &
return 'wasFiltered' in item && item.wasFiltered === true;
}

function isFilteredTrack(track: Partial<AddTrackArgs>): boolean {
return globals.trackFilteringEnabled && globals.filteredTracks
function isFilteredTrack(globalsContext: string, track: Partial<AddTrackArgs>): boolean {
return globals(globalsContext).trackFilteringEnabled && globals(globalsContext).filteredTracks
.some(filtered => isAddTrackArgs(filtered) && isSameTrack(filtered, track));
}

Expand All @@ -188,35 +188,35 @@ function isSameTrack(track: AddTrackArgs, other: Partial<AddTrackArgs>): boolean
&& track.name == other.name;
}

function isFilteredTrackGroup(trackGroup: Partial<AddTrackGroupArgs>): boolean {
return globals.trackFilteringEnabled && globals.filteredTracks
function isFilteredTrackGroup(globalsContext: string, trackGroup: Partial<AddTrackGroupArgs>): boolean {
return globals(globalsContext).trackFilteringEnabled && globals(globalsContext).filteredTracks
.some(filtered => isAddTrackGroupArgs(filtered) && filtered.id === trackGroup.id);
}

function unfilterTracklike(predicate: (tracklike: AddTrackLikeArgs) => boolean) {
const index = globals.filteredTracks.findIndex(predicate);
function unfilterTracklike(globalsContext: string, predicate: (tracklike: AddTrackLikeArgs) => boolean) {
const index = globals(globalsContext).filteredTracks.findIndex(predicate);
if (index >= 0) {
globals.filteredTracks.splice(index, 1);
globals(globalsContext).filteredTracks.splice(index, 1);
}
}

function unfilterTrack(track: TrackState) {
function unfilterTrack(globalsContext: string, track: TrackState) {
track.isRemovable = true;
(track as any).wasFiltered = true;
unfilterTracklike(filtered => isAddTrackArgs(filtered) && isSameTrack(filtered, track));
unfilterTracklike(globalsContext, filtered => isAddTrackArgs(filtered) && isSameTrack(filtered, track));
}

function unfilterTrackGroup(trackGroup: TrackGroupState) {
function unfilterTrackGroup(globalsContext: string, trackGroup: TrackGroupState) {
trackGroup.isRemovable = true;
(trackGroup as any).wasFiltered = true;
unfilterTracklike(filtered => isAddTrackGroupArgs(filtered) && filtered.id === trackGroup.id);
unfilterTracklike(globalsContext, filtered => isAddTrackGroupArgs(filtered) && filtered.id === trackGroup.id);
}

// A helper to delete the private tables and views created by a track.
// TODO: These should recorded by each track that creates them and cleaned up
// by an explicit disposable-track protocol.
async function dropTables(engineId: string, trackId: string) {
const engine = assertExists(globals.engines.get(engineId));
async function dropTables(globalsContext: string, engineId: string, trackId: string) {
const engine = assertExists(globals(globalsContext).engines.get(engineId));
const suffix = trackId.split('-').join('_');
const result = await engine.query(`
select name, type from sqlite_schema
Expand Down Expand Up @@ -383,8 +383,8 @@ export const StateActions = {
// or if it is currently filtered out of view
if (hasRemovable(args)) {
state.tracks[id].isRemovable = args.isRemovable;
} else if (isFilteredTrack(args)) {
unfilterTrack(state.tracks[id]);
} else if (isFilteredTrack(state.globalsContext, args)) {
unfilterTrack(state.globalsContext, state.tracks[id]);
}

this.fillUiTrackIdByTraceTrackId(state, state.tracks[id], id);
Expand Down Expand Up @@ -415,8 +415,8 @@ export const StateActions = {
// or if it is currently filtered out of view
if (hasRemovable(args)) {
state.trackGroups[args.id].isRemovable = args.isRemovable;
} else if (isFilteredTrackGroup(args)) {
unfilterTrackGroup(state.trackGroups[args.id]);
} else if (isFilteredTrackGroup(state.globalsContext, args)) {
unfilterTrackGroup(state.globalsContext, state.trackGroups[args.id]);
}
},

Expand Down Expand Up @@ -475,7 +475,7 @@ export const StateActions = {
? { id: track.id }
: {};

globals.filteredTracks.push({
globals(state.globalsContext).filteredTracks.push({
...id,
kind: track.kind,
engineId: track.engineId,
Expand All @@ -487,7 +487,7 @@ export const StateActions = {
});
}

dropTables(track.engineId, track.id);
dropTables(state.globalsContext, track.engineId, track.id);
},

// Remove a track group if it is removable as indicated by the
Expand All @@ -500,7 +500,7 @@ export const StateActions = {

if (wasFiltered(trackGroup)) {
delete trackGroup.wasFiltered;
globals.filteredTracks.push({
globals(state.globalsContext).filteredTracks.push({
id: trackGroup.id,
engineId: trackGroup.engineId,
name: trackGroup.name,
Expand Down Expand Up @@ -1271,10 +1271,10 @@ export const StateActions = {
}
},

togglePivotTable(state: StateDraft, args: {areaId: string|null}) {
togglePivotTable(state: StateDraft, args: {globalsContext: string, areaId: string|null}) {
state.nonSerializableState.pivotTable.selectionArea = args.areaId === null ?
undefined :
{areaId: args.areaId, tracks: globals.state.areas[args.areaId].tracks};
{areaId: args.areaId, tracks: globals(args.globalsContext).state.areas[args.areaId].tracks};
if (args.areaId !==
state.nonSerializableState.pivotTable.selectionArea?.areaId) {
state.nonSerializableState.pivotTable.queryResult = null;
Expand Down
8 changes: 4 additions & 4 deletions ui/src/common/cache_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ async function cacheKeys(): Promise<readonly Request[]> {
}

export async function cacheTrace(
traceSource: TraceSource, traceUuid: string): Promise<boolean> {
globalsContext: string, traceSource: TraceSource, traceUuid: string): Promise<boolean> {
let trace;
let title = '';
let fileName = '';
Expand Down Expand Up @@ -122,14 +122,14 @@ export async function cacheTrace(
]);
await deleteStaleEntries();
await cachePut(
`${globals.cachePrefix}/_${TRACE_CACHE_NAME}/${traceUuid}`, new Response(trace, {headers}));
`${globals(globalsContext).cachePrefix}/_${TRACE_CACHE_NAME}/${traceUuid}`, new Response(trace, {headers}));
return true;
}

export async function tryGetTrace(traceUuid: string):
export async function tryGetTrace(globalsContext: string, traceUuid: string):
Promise<TraceArrayBufferSource|undefined> {
await deleteStaleEntries();
const response = await cacheMatch(`${globals.cachePrefix}/_${TRACE_CACHE_NAME}/${traceUuid}`);
const response = await cacheMatch(`${globals(globalsContext).cachePrefix}/_${TRACE_CACHE_NAME}/${traceUuid}`);

if (!response) return undefined;
return {
Expand Down
2 changes: 1 addition & 1 deletion ui/src/common/channels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,5 @@ export function setChannel(channel: string): void {
getCurrentChannel(); // Cache the current channel before mangling next one.
nextChannel = channel;
localStorage.setItem(CHANNEL_KEY, channel);
globals.rafScheduler.scheduleFullRedraw();
globals().rafScheduler.scheduleFullRedraw();
}
3 changes: 2 additions & 1 deletion ui/src/common/empty_state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,11 @@ export function createEmptyNonSerializableState(): NonSerializableState {
};
}

export function createEmptyState(): State {
export function createEmptyState(globalsContext = ''): State {
return {
version: STATE_VERSION,
nextId: '-1',
globalsContext,
newEngineMode: 'USE_HTTP_RPC_IF_AVAILABLE',
traceTime: {...defaultTraceTime},
tracks: {},
Expand Down
12 changes: 7 additions & 5 deletions ui/src/common/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ import {Span} from '../common/time';
import {BigintMath} from '../base/bigint_math';

export interface LoadingTracker {
beginLoading(): void;
endLoading(): void;
beginLoading(globalsContext: string): void;
endLoading(globalsContext: string): void;
}

export class NullLoadingTracker implements LoadingTracker {
Expand Down Expand Up @@ -75,6 +75,7 @@ export interface TraceProcessorConfig {
// 2. Call onRpcResponseBytes() when response data is received.
export abstract class Engine {
abstract readonly id: string;
private globalsContext: string;
private _cpus?: number[];
private _numGpus?: number;
private loadingTracker: LoadingTracker;
Expand All @@ -90,7 +91,8 @@ export abstract class Engine {
private pendingReadMetatrace?: Deferred<DisableAndReadMetatraceResult>;
private _isMetatracingEnabled = false;

constructor(tracker?: LoadingTracker) {
constructor(globalsContext: string, tracker?: LoadingTracker) {
this.globalsContext = globalsContext;
this.loadingTracker = tracker ? tracker : new NullLoadingTracker();
}

Expand Down Expand Up @@ -223,7 +225,7 @@ export abstract class Engine {
} // switch(rpc.response);

if (isFinalResponse) {
this.loadingTracker.endLoading();
this.loadingTracker.endLoading(this.globalsContext);
}
}

Expand Down Expand Up @@ -375,7 +377,7 @@ export abstract class Engine {
const outerProto = TraceProcessorRpcStream.create();
outerProto.msg.push(rpc);
const buf = TraceProcessorRpcStream.encode(outerProto).finish();
this.loadingTracker.beginLoading();
this.loadingTracker.beginLoading(this.globalsContext);
this.rpcSendRequestBytes(buf);
}

Expand Down
25 changes: 16 additions & 9 deletions ui/src/common/http_rpc_engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,16 @@ import {StatusResult} from '../common/protos';

import {Engine, LoadingTracker} from './engine';

export const RPC_URL = 'http://127.0.0.1:9001/';
export const WS_URL = 'ws://127.0.0.1:9001/websocket';

const RPC_CONNECT_TIMEOUT_MS = 2000;

export function getRPC_URL(port: number) {
return `http://127.0.0.1:${port}/`;
}

export function getWS_URL(port: number) {
return `ws://127.0.0.1:${port}/websocket`;
}

export interface HttpRpcState {
connected: boolean;
status?: StatusResult;
Expand All @@ -34,20 +39,22 @@ export type HttpRcpEngineCustomizer = (engine: HttpRpcEngine) => unknown;

export class HttpRpcEngine extends Engine {
readonly id: string;
readonly port: number;
errorHandler: (err: string) => void = () => {};
closeHandler: (code: number, reason: string) => void = (code, reason) => this.errorHandler(`Websocket closed (${code}: ${reason})`);
private requestQueue = new Array<Uint8Array>();
private websocket?: WebSocket;
private connected = false;

constructor(id: string, loadingTracker?: LoadingTracker) {
super(loadingTracker);
constructor(globalsContext: string, id: string, loadingTracker?: LoadingTracker, port = 9001) {
super(globalsContext, loadingTracker);
this.id = id;
this.port = port;
}

rpcSendRequestBytes(data: Uint8Array): void {
if (this.websocket === undefined) {
this.websocket = new WebSocket(WS_URL);
this.websocket = new WebSocket(getWS_URL(this.port));
this.websocket.onopen = () => this.onWebsocketConnected();
this.websocket.onmessage = (e) => this.onWebsocketMessage(e);
this.websocket.onclose = (e) =>
Expand Down Expand Up @@ -78,15 +85,15 @@ export class HttpRpcEngine extends Engine {
});
}

static async checkConnection(): Promise<HttpRpcState> {
static async checkConnection(port: number): Promise<HttpRpcState> {
const httpRpcState: HttpRpcState = {connected: false};
console.info(
`It's safe to ignore the ERR_CONNECTION_REFUSED on ${RPC_URL} below. ` +
`It's safe to ignore the ERR_CONNECTION_REFUSED on ${getRPC_URL(port)} below. ` +
`That might happen while probing the external native accelerator. The ` +
`error is non-fatal and unlikely to be the culprit for any UI bug.`);
try {
const resp = await fetchWithTimeout(
RPC_URL + 'status',
getRPC_URL(port) + 'status',
{method: 'post', cache: 'no-cache'},
RPC_CONNECT_TIMEOUT_MS);
if (resp.status !== 200) {
Expand Down
8 changes: 4 additions & 4 deletions ui/src/common/plugin_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,11 @@ export interface PluginContext {
// selection changes for tracks in this plugin.
//
// Params:
// @onDetailsPanelSelectionChange a function that takes a Selection as its
// parameter and performs whatever must happen on the details panel when the
// selection is invoked.
// @onDetailsPanelSelectionChange a function that takes the trace globals
// context and a Selection as its parameters and performs whatever must
// happen on the details panel when the selection is invoked.
registerOnDetailsPanelSelectionChange(
onDetailsPanelSelectionChange: (newSelection?: Selection) => void): void;
onDetailsPanelSelectionChange: (globalsContext: string,newSelection?: Selection) => void): void;
}

export interface PluginInfo {
Expand Down
8 changes: 4 additions & 4 deletions ui/src/common/plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import {Selection} from './state';
// plugins.
export class PluginContextImpl implements PluginContext {
readonly pluginId: string;
onDetailsPanelSelectionChange?: (newSelection?: Selection) => void;
onDetailsPanelSelectionChange?: (globalsContext: string, newSelection?: Selection) => void;
private trackProviders: TrackProvider[];

constructor(pluginId: string) {
Expand All @@ -62,7 +62,7 @@ export class PluginContextImpl implements PluginContext {
}

registerOnDetailsPanelSelectionChange(
onDetailsPanelSelectionChange: (newSelection?: Selection) => void) {
onDetailsPanelSelectionChange: (globalsContext: string, newSelection?: Selection) => void) {
this.onDetailsPanelSelectionChange = onDetailsPanelSelectionChange;
}
// ==================================================================
Expand Down Expand Up @@ -136,11 +136,11 @@ export class PluginManager {
return promises;
}

onDetailsPanelSelectionChange(pluginId: string, newSelection?: Selection) {
onDetailsPanelSelectionChange(pluginId: string, globalsContext: string, newSelection?: Selection) {
const pluginContext = this.getPluginContext(pluginId);
if (pluginContext === undefined) return;
if (pluginContext.onDetailsPanelSelectionChange) {
pluginContext.onDetailsPanelSelectionChange(newSelection);
pluginContext.onDetailsPanelSelectionChange(globalsContext, newSelection);
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions ui/src/common/recordingV2/adb_connection_over_webusb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export class AdbConnectionOverWebusb extends AdbConnectionImpl {
// Once we've sent the public key, for future recordings we only need to
// sign with the private key, so the user doesn't need to give permissions
// again.
constructor(private device: USBDevice, private keyManager: AdbKeyManager) {
constructor(private globalsContext: string, private device: USBDevice, private keyManager: AdbKeyManager) {
super();
}

Expand Down Expand Up @@ -351,7 +351,7 @@ export class AdbConnectionOverWebusb extends AdbConnectionImpl {
await this.sendMessage(
'AUTH', AuthCmd.RSAPUBLICKEY, 0, key.getPublicKey() + '\0');
this.onStatus(ALLOW_USB_DEBUGGING);
await maybeStoreKey(key);
await maybeStoreKey(this.globalsContext, key);
}
} else if (msg.cmd === 'CNXN') {
assertTrue(
Expand Down
Loading