Skip to content
Open
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
1 change: 1 addition & 0 deletions frontend/app/components/trace_viewer/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ xprof_ng_module(
deps = [
"@npm//@angular/common",
"@npm//@angular/core",
"@npm//@angular/core_rxjs_interop",
"@npm//@angular/forms",
"@npm//@angular/router",
"@npm//@ngrx/store",
Expand Down
27 changes: 14 additions & 13 deletions frontend/app/components/trace_viewer/trace_viewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import {
AfterViewInit,
ChangeDetectionStrategy,
Component,
DestroyRef,
inject,
Injector,
OnDestroy,
OnInit,
TemplateRef,
ViewChild,
} from '@angular/core';
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
import {MatDialog, MatDialogConfig} from '@angular/material/dialog';
import {ActivatedRoute, Router} from '@angular/router';
import {Store} from '@ngrx/store';
Expand Down Expand Up @@ -55,7 +57,6 @@ import {
distinctUntilChanged,
finalize,
switchMap,
takeUntil,
tap,
} from 'rxjs/operators';
import {
Expand Down Expand Up @@ -160,8 +161,8 @@ function loadFeatureFlagsFromStorage(): FeatureFlagWithValue[] {
styleUrls: ['./trace_viewer.css'],
})
export class TraceViewer implements OnInit, AfterViewInit, OnDestroy {
private readonly destroyed = new ReplaySubject<void>(1);
private isDestroyed = false;
private readonly destroyRef = inject(DestroyRef);
isDestroyed = false;
private isInitializing = false;
private navigationEvent: NavigationEvent = {};
private readonly injector = inject(Injector);
Expand Down Expand Up @@ -404,7 +405,7 @@ export class TraceViewer implements OnInit, AfterViewInit, OnDestroy {
this.route.queryParams,
this.store.select(getHostsState),
])
.pipe(takeUntil(this.destroyed))
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(([params, queryParams, hostsMetadata]) => {
if (hostsMetadata && hostsMetadata.length > 0) {
this.hostList = hostsMetadata.map(
Expand Down Expand Up @@ -440,7 +441,7 @@ export class TraceViewer implements OnInit, AfterViewInit, OnDestroy {
// UI accordingly.
sourceCodeService
?.isAvailable()
.pipe(takeUntil(this.destroyed))
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((isAvailable) => {
this.sourceCodeServiceIsAvailable = isAvailable;
});
Expand All @@ -449,7 +450,7 @@ export class TraceViewer implements OnInit, AfterViewInit, OnDestroy {
ngOnInit(): void {
this.searchQuery
.pipe(
takeUntil(this.destroyed),
takeUntilDestroyed(this.destroyRef),
debounceTime(300),
distinctUntilChanged(),
tap<string>(() => {
Expand Down Expand Up @@ -482,6 +483,7 @@ export class TraceViewer implements OnInit, AfterViewInit, OnDestroy {
}),
)
.subscribe((data) => {
if (this.isDestroyed) return;
this.searching = false;
if (this.traceViewerModule && data) {
// Type contract is guaranteed by the backend response structure.
Expand Down Expand Up @@ -525,7 +527,8 @@ export class TraceViewer implements OnInit, AfterViewInit, OnDestroy {
this.traceViewerModule &&
this.traceViewerModule.getAllFlowCategories
) {
this.allFlowCategories = this.traceViewerModule.getAllFlowCategories();
this.allFlowCategories =
this.traceViewerModule.getAllFlowCategories() as FlowCategory[];
this.selectAllFlowCategories();
}
this.update(this.navigationEvent);
Expand Down Expand Up @@ -599,6 +602,7 @@ export class TraceViewer implements OnInit, AfterViewInit, OnDestroy {
if (this.useTraceViewerV2) {
if (this.traceViewerModule && this.traceViewerModule.loadTraceData) {
this.traceViewerModule.loadTraceData(traceDataUrl).then(() => {
if (this.isDestroyed) return;
this.updateFlowCategories();
this.updateWasmFlowCategories();
this.updateWasmProcessMappings();
Expand All @@ -623,9 +627,6 @@ export class TraceViewer implements OnInit, AfterViewInit, OnDestroy {
this.traceViewerModule = null;
}
} finally {
// Unsubscribes all pending subscriptions.
this.destroyed.next();
this.destroyed.complete();
window.removeEventListener(
DETAILS_RECEIVED_EVENT_NAME,
this.detailsReceivedEventListener,
Expand Down Expand Up @@ -872,7 +873,7 @@ export class TraceViewer implements OnInit, AfterViewInit, OnDestroy {
host,
params,
)
.pipe(takeUntil(this.destroyed))
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((data) => {
const traceData = data as TraceData;
if (
Expand Down Expand Up @@ -944,7 +945,7 @@ export class TraceViewer implements OnInit, AfterViewInit, OnDestroy {
params,
)
.pipe(
takeUntil(this.destroyed),
takeUntilDestroyed(this.destroyRef),
finalize(() => {
this.pendingAdjacentNodesFetches.delete(key);
}),
Expand Down Expand Up @@ -1243,7 +1244,7 @@ export class TraceViewer implements OnInit, AfterViewInit, OnDestroy {
customEvent.detail.status === TraceViewerV2LoadingStatus.IDLE
) {
setTimeout(() => {
if (!this.destroyed.isStopped) {
if (!this.isDestroyed) {
this.showColorOnboarding = true;
}
}, 2000); // Delay 2 seconds after load complete
Expand Down
1 change: 1 addition & 0 deletions frontend/app/components/trace_viewer_container/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ xprof_ng_module(
deps = [
"@npm//@angular/common",
"@npm//@angular/core",
"@npm//@angular/core_rxjs_interop",
"@npm//@angular/forms",
"@npm//angular-split",
"@npm//rxjs",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import {
AfterViewInit,
ChangeDetectionStrategy,
Component,
DestroyRef,
ElementRef,
EventEmitter,
inject,
Input,
OnChanges,
OnDestroy,
Expand All @@ -15,6 +17,7 @@ import {
SimpleChanges,
ViewChild,
} from '@angular/core';
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
import {FormsModule} from '@angular/forms';
import {MatButtonModule} from '@angular/material/button';
import {MatFormFieldModule} from '@angular/material/form-field';
Expand All @@ -35,8 +38,8 @@ import {
type TraceViewerV2Module,
} from 'org_xprof/frontend/app/components/trace_viewer_v2/main';
import {PipesModule} from 'org_xprof/frontend/app/pipes/pipes_module';
import {interval, ReplaySubject, Subject, Subscription} from 'rxjs';
import {debounceTime, takeUntil} from 'rxjs/operators';
import {interval, Subject, Subscription} from 'rxjs';
import {debounceTime} from 'rxjs/operators';

const DEPRECATED_STORAGE_KEYS = ['trace_viewer_timing_prompted'];

Expand Down Expand Up @@ -379,12 +382,11 @@ export class TraceViewerContainer
timelineHeightPercent = 100;
detailHeightPercent = 0;

/** Handles on-destroy Subject, used to unsubscribe. */
private readonly destroyed = new ReplaySubject<void>(1);
private readonly destroyRef = inject(DestroyRef);

constructor() {
this.search$
.pipe(debounceTime(300), takeUntil(this.destroyed))
.pipe(debounceTime(300), takeUntilDestroyed(this.destroyRef))
.subscribe((query) => {
this.currentSearchQuery = query;
this.searchEvents.emit({events_query: query});
Expand Down Expand Up @@ -456,9 +458,6 @@ export class TraceViewerContainer
window.removeEventListener('mouseup', this.mouseUpEventListener);
window.removeEventListener('keydown', this.keyDownEventListener);
}
// Unsubscribes all pending subscriptions.
this.destroyed.next();
this.destroyed.complete();
this.stopTutorialRotation();
}

Expand Down Expand Up @@ -622,7 +621,7 @@ export class TraceViewerContainer
if (this.tutorialSubscription) return;

this.tutorialSubscription = interval(TUTORIAL_ROTATION_INTERVAL_MS)
.pipe(takeUntil(this.destroyed))
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => {
this.currentTutorialIndex =
(this.currentTutorialIndex + 1) % this.tutorials.length;
Expand Down
14 changes: 10 additions & 4 deletions frontend/app/components/trace_viewer_v2/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,8 +320,7 @@ async function getWebGpuDevice(): Promise<GPUDevice> {
'WebGPU cannot be initialized - failed to get WebGPU device.',
);
}
// tslint:disable-next-line:no-any
(device as any).lost
void (device as unknown as {lost: Promise<void>}).lost
.then(() => {
throw new Error('WebGPU Cannot be initialized - Device has been lost');
})
Expand Down Expand Up @@ -512,6 +511,13 @@ async function decompressGzip(data: Uint8Array): Promise<Uint8Array | null> {
}
}

/**
* Checks if the URL represents a compressed protobuf file.
*/
export function isCompressedProto(urlObj: URL): boolean {
return urlObj.pathname.endsWith('.pb');
}

function isPerfettoTrace(data: Uint8Array, fileName: string): boolean {
return (
fileName.endsWith('.pftrace') ||
Expand Down Expand Up @@ -799,7 +805,7 @@ async function fetchAndProcessTraceData({
updateUrlWithResolution(urlObj, traceviewerModule.canvas);

try {
if (urlObj.pathname.endsWith('.pb')) {
if (isCompressedProto(urlObj)) {
const buffer = await loadCompressedTraceDataInternal(urlObj.toString());
if (isAbortRequested()) return;

Expand Down Expand Up @@ -1104,7 +1110,7 @@ export async function traceViewerV2Main(
return;
}

if (urlObj.pathname.endsWith('.pb')) {
if (isCompressedProto(urlObj)) {
const buffer = await loadCompressedTraceDataInternal(urlObj.toString());
traceviewerModule.setCompressedSearchResultsInWasm(
new Uint8Array(buffer),
Expand Down