mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-06 20:11:38 +08:00
Bug: 1299773 Change-Id: I54609104283c074d0f3e03917dc9ba2a77544290 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/3548478 Reviewed-by: Eric Leese <leese@chromium.org> Reviewed-by: Jack Franklin <jacktfranklin@chromium.org> Commit-Queue: Kateryna Prokopenko <kprokopenko@chromium.org>
229 lines
7.7 KiB
TypeScript
229 lines
7.7 KiB
TypeScript
// Copyright 2020 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 ProtocolProxyApi from '../../generated/protocol-proxy-api.js';
|
|
import type * as Protocol from '../../generated/protocol.js';
|
|
import type * as Platform from '../platform/platform.js';
|
|
|
|
import type {DebuggerModel} from './DebuggerModel.js';
|
|
import type {RemoteObject} from './RemoteObject.js';
|
|
import {RuntimeModel} from './RuntimeModel.js';
|
|
import type {Target} from './Target.js';
|
|
import {Capability} from './Target.js';
|
|
import {SDKModel} from './SDKModel.js';
|
|
|
|
export class HeapProfilerModel extends SDKModel<EventTypes> {
|
|
#enabled: boolean;
|
|
readonly #heapProfilerAgent: ProtocolProxyApi.HeapProfilerApi;
|
|
readonly #runtimeModelInternal: RuntimeModel;
|
|
#samplingProfilerDepth: number;
|
|
|
|
constructor(target: Target) {
|
|
super(target);
|
|
target.registerHeapProfilerDispatcher(new HeapProfilerDispatcher(this));
|
|
this.#enabled = false;
|
|
this.#heapProfilerAgent = target.heapProfilerAgent();
|
|
this.#runtimeModelInternal = (target.model(RuntimeModel) as RuntimeModel);
|
|
this.#samplingProfilerDepth = 0;
|
|
}
|
|
|
|
debuggerModel(): DebuggerModel {
|
|
return this.#runtimeModelInternal.debuggerModel();
|
|
}
|
|
|
|
runtimeModel(): RuntimeModel {
|
|
return this.#runtimeModelInternal;
|
|
}
|
|
|
|
async enable(): Promise<void> {
|
|
if (this.#enabled) {
|
|
return;
|
|
}
|
|
|
|
this.#enabled = true;
|
|
await this.#heapProfilerAgent.invoke_enable();
|
|
}
|
|
|
|
async startSampling(samplingRateInBytes?: number): Promise<boolean> {
|
|
if (this.#samplingProfilerDepth++) {
|
|
return false;
|
|
}
|
|
const defaultSamplingIntervalInBytes = 16384;
|
|
const response = await this.#heapProfilerAgent.invoke_startSampling(
|
|
{samplingInterval: samplingRateInBytes || defaultSamplingIntervalInBytes});
|
|
return Boolean(response.getError());
|
|
}
|
|
|
|
async stopSampling(): Promise<Protocol.HeapProfiler.SamplingHeapProfile|null> {
|
|
if (!this.#samplingProfilerDepth) {
|
|
throw new Error('Sampling profiler is not running.');
|
|
}
|
|
if (--this.#samplingProfilerDepth) {
|
|
return this.getSamplingProfile();
|
|
}
|
|
const response = await this.#heapProfilerAgent.invoke_stopSampling();
|
|
if (response.getError()) {
|
|
return null;
|
|
}
|
|
return response.profile;
|
|
}
|
|
|
|
async getSamplingProfile(): Promise<Protocol.HeapProfiler.SamplingHeapProfile|null> {
|
|
const response = await this.#heapProfilerAgent.invoke_getSamplingProfile();
|
|
if (response.getError()) {
|
|
return null;
|
|
}
|
|
return response.profile;
|
|
}
|
|
|
|
async collectGarbage(): Promise<boolean> {
|
|
const response = await this.#heapProfilerAgent.invoke_collectGarbage();
|
|
return Boolean(response.getError());
|
|
}
|
|
|
|
async snapshotObjectIdForObjectId(objectId: Protocol.Runtime.RemoteObjectId): Promise<string|null> {
|
|
const response = await this.#heapProfilerAgent.invoke_getHeapObjectId({objectId});
|
|
if (response.getError()) {
|
|
return null;
|
|
}
|
|
return response.heapSnapshotObjectId;
|
|
}
|
|
|
|
async objectForSnapshotObjectId(
|
|
snapshotObjectId: Protocol.HeapProfiler.HeapSnapshotObjectId,
|
|
objectGroupName: string): Promise<RemoteObject|null> {
|
|
const result = await this.#heapProfilerAgent.invoke_getObjectByHeapObjectId(
|
|
{objectId: snapshotObjectId, objectGroup: objectGroupName});
|
|
if (result.getError()) {
|
|
return null;
|
|
}
|
|
return this.#runtimeModelInternal.createRemoteObject(result.result);
|
|
}
|
|
|
|
async addInspectedHeapObject(snapshotObjectId: Protocol.HeapProfiler.HeapSnapshotObjectId): Promise<boolean> {
|
|
const response = await this.#heapProfilerAgent.invoke_addInspectedHeapObject({heapObjectId: snapshotObjectId});
|
|
return Boolean(response.getError());
|
|
}
|
|
|
|
async takeHeapSnapshot(heapSnapshotOptions: Protocol.HeapProfiler.TakeHeapSnapshotRequest): Promise<void> {
|
|
await this.#heapProfilerAgent.invoke_takeHeapSnapshot(heapSnapshotOptions);
|
|
}
|
|
|
|
async startTrackingHeapObjects(recordAllocationStacks: boolean): Promise<boolean> {
|
|
const response =
|
|
await this.#heapProfilerAgent.invoke_startTrackingHeapObjects({trackAllocations: recordAllocationStacks});
|
|
return Boolean(response.getError());
|
|
}
|
|
|
|
async stopTrackingHeapObjects(reportProgress: boolean): Promise<boolean> {
|
|
const response = await this.#heapProfilerAgent.invoke_stopTrackingHeapObjects({reportProgress});
|
|
return Boolean(response.getError());
|
|
}
|
|
|
|
heapStatsUpdate(samples: number[]): void {
|
|
this.dispatchEventToListeners(Events.HeapStatsUpdate, samples);
|
|
}
|
|
|
|
lastSeenObjectId(lastSeenObjectId: number, timestamp: number): void {
|
|
this.dispatchEventToListeners(Events.LastSeenObjectId, {lastSeenObjectId: lastSeenObjectId, timestamp: timestamp});
|
|
}
|
|
|
|
addHeapSnapshotChunk(chunk: string): void {
|
|
this.dispatchEventToListeners(Events.AddHeapSnapshotChunk, chunk);
|
|
}
|
|
|
|
reportHeapSnapshotProgress(done: number, total: number, finished?: boolean): void {
|
|
this.dispatchEventToListeners(Events.ReportHeapSnapshotProgress, {done: done, total: total, finished: finished});
|
|
}
|
|
|
|
resetProfiles(): void {
|
|
this.dispatchEventToListeners(Events.ResetProfiles, this);
|
|
}
|
|
}
|
|
|
|
// TODO(crbug.com/1167717): Make this a const enum again
|
|
// eslint-disable-next-line rulesdir/const_enum
|
|
export enum Events {
|
|
HeapStatsUpdate = 'HeapStatsUpdate',
|
|
LastSeenObjectId = 'LastSeenObjectId',
|
|
AddHeapSnapshotChunk = 'AddHeapSnapshotChunk',
|
|
ReportHeapSnapshotProgress = 'ReportHeapSnapshotProgress',
|
|
ResetProfiles = 'ResetProfiles',
|
|
}
|
|
|
|
/**
|
|
* An array of triplets. Each triplet describes a fragment. The first number is the fragment
|
|
* index, the second number is a total count of objects for the fragment, the third number is
|
|
* a total size of the objects for the fragment.
|
|
*/
|
|
export type HeapStatsUpdateSamples = number[];
|
|
|
|
export interface LastSeenObjectId {
|
|
lastSeenObjectId: number;
|
|
timestamp: number;
|
|
}
|
|
|
|
export interface HeapSnapshotProgress {
|
|
done: number;
|
|
total: number;
|
|
finished?: boolean;
|
|
}
|
|
|
|
export type EventTypes = {
|
|
[Events.HeapStatsUpdate]: HeapStatsUpdateSamples,
|
|
[Events.LastSeenObjectId]: LastSeenObjectId,
|
|
[Events.AddHeapSnapshotChunk]: string,
|
|
[Events.ReportHeapSnapshotProgress]: HeapSnapshotProgress,
|
|
[Events.ResetProfiles]: HeapProfilerModel,
|
|
};
|
|
|
|
export interface NativeProfilerCallFrame {
|
|
functionName: string;
|
|
url: Platform.DevToolsPath.UrlString;
|
|
scriptId?: string;
|
|
lineNumber?: number;
|
|
columnNumber?: number;
|
|
}
|
|
|
|
export interface CommonHeapProfileNode {
|
|
callFrame: NativeProfilerCallFrame;
|
|
selfSize: number;
|
|
id?: number;
|
|
children: CommonHeapProfileNode[];
|
|
}
|
|
|
|
export interface CommonHeapProfile {
|
|
head: CommonHeapProfileNode;
|
|
modules: Protocol.Memory.Module[];
|
|
}
|
|
|
|
class HeapProfilerDispatcher implements ProtocolProxyApi.HeapProfilerDispatcher {
|
|
readonly #heapProfilerModel: HeapProfilerModel;
|
|
constructor(model: HeapProfilerModel) {
|
|
this.#heapProfilerModel = model;
|
|
}
|
|
|
|
heapStatsUpdate({statsUpdate}: Protocol.HeapProfiler.HeapStatsUpdateEvent): void {
|
|
this.#heapProfilerModel.heapStatsUpdate(statsUpdate);
|
|
}
|
|
|
|
lastSeenObjectId({lastSeenObjectId, timestamp}: Protocol.HeapProfiler.LastSeenObjectIdEvent): void {
|
|
this.#heapProfilerModel.lastSeenObjectId(lastSeenObjectId, timestamp);
|
|
}
|
|
|
|
addHeapSnapshotChunk({chunk}: Protocol.HeapProfiler.AddHeapSnapshotChunkEvent): void {
|
|
this.#heapProfilerModel.addHeapSnapshotChunk(chunk);
|
|
}
|
|
|
|
reportHeapSnapshotProgress({done, total, finished}: Protocol.HeapProfiler.ReportHeapSnapshotProgressEvent): void {
|
|
this.#heapProfilerModel.reportHeapSnapshotProgress(done, total, finished);
|
|
}
|
|
|
|
resetProfiles(): void {
|
|
this.#heapProfilerModel.resetProfiles();
|
|
}
|
|
}
|
|
|
|
SDKModel.register(HeapProfilerModel, {capabilities: Capability.JS, autostart: false});
|