feat: Print object count and total sizes in get_heapsnapshot_details (#2325)
This emits object count and total shallow sizes for get_heapsnapshot_details.
This commit is contained in:
@@ -17,6 +17,12 @@ import {
|
||||
export type AggregatedInfoWithId =
|
||||
WithSymbolId<DevTools.HeapSnapshotModel.HeapSnapshotModel.AggregatedInfo>;
|
||||
|
||||
export interface HeapSnapshotAggregateData {
|
||||
aggregates: Record<string, AggregatedInfoWithId>;
|
||||
objectCount: number;
|
||||
totalSelfSize: number;
|
||||
}
|
||||
|
||||
export interface HeapSnapshotClassDiff {
|
||||
className: string;
|
||||
addedCount: number;
|
||||
@@ -74,7 +80,7 @@ export class HeapSnapshotManager {
|
||||
async getAggregates(
|
||||
filePath: string,
|
||||
filterName?: string,
|
||||
): Promise<Record<string, AggregatedInfoWithId>> {
|
||||
): Promise<HeapSnapshotAggregateData> {
|
||||
const snapshot = await this.getSnapshot(filePath);
|
||||
const filter =
|
||||
new DevTools.HeapSnapshotModel.HeapSnapshotModel.NodeFilter();
|
||||
@@ -83,16 +89,21 @@ export class HeapSnapshotManager {
|
||||
}
|
||||
const aggregates: Record<string, AggregatedInfoWithId> =
|
||||
await snapshot.aggregatesWithFilter(filter);
|
||||
let objectCount = 0;
|
||||
let totalSelfSize = 0;
|
||||
|
||||
for (const key of Object.keys(aggregates)) {
|
||||
for (const [key, aggregate] of Object.entries(aggregates)) {
|
||||
const id = await this.getOrCreateIdForClassKey(filePath, key);
|
||||
const aggregate = aggregates[key];
|
||||
if (aggregate) {
|
||||
aggregate[stableIdSymbol] = id;
|
||||
}
|
||||
aggregate[stableIdSymbol] = id;
|
||||
objectCount += aggregate.count;
|
||||
totalSelfSize += aggregate.self;
|
||||
}
|
||||
|
||||
return aggregates;
|
||||
return {
|
||||
aggregates,
|
||||
objectCount,
|
||||
totalSelfSize,
|
||||
};
|
||||
}
|
||||
|
||||
async getStats(
|
||||
|
||||
+2
-2
@@ -17,7 +17,7 @@ import {
|
||||
} from './devtools/DevtoolsUtils.js';
|
||||
import {HeapSnapshotManager} from './HeapSnapshotManager.js';
|
||||
import type {
|
||||
AggregatedInfoWithId,
|
||||
HeapSnapshotAggregateData,
|
||||
HeapSnapshotClassDiff,
|
||||
HeapSnapshotDetailedClassDiff,
|
||||
DuplicateStringGroup,
|
||||
@@ -972,7 +972,7 @@ export class McpContext implements Context {
|
||||
async getHeapSnapshotAggregates(
|
||||
filePath: string,
|
||||
filterName?: string,
|
||||
): Promise<Record<string, AggregatedInfoWithId>> {
|
||||
): Promise<HeapSnapshotAggregateData> {
|
||||
return await this.#heapSnapshotManager.getAggregates(filePath, filterName);
|
||||
}
|
||||
|
||||
|
||||
+24
-12
@@ -17,6 +17,7 @@ import {IssueFormatter} from './formatters/IssueFormatter.js';
|
||||
import {NetworkFormatter} from './formatters/NetworkFormatter.js';
|
||||
import {SnapshotFormatter} from './formatters/SnapshotFormatter.js';
|
||||
import type {
|
||||
HeapSnapshotAggregateData,
|
||||
HeapSnapshotClassDiff,
|
||||
HeapSnapshotDetailedClassDiff,
|
||||
DuplicateStringGroup,
|
||||
@@ -230,10 +231,7 @@ export class McpResponse implements Response {
|
||||
#images: ImageContentData[] = [];
|
||||
#heapSnapshotOptions?: {
|
||||
include: boolean;
|
||||
aggregates?: Record<
|
||||
string,
|
||||
DevTools.HeapSnapshotModel.HeapSnapshotModel.AggregatedInfo
|
||||
>;
|
||||
aggregateData?: HeapSnapshotAggregateData;
|
||||
pagination?: PaginationOptions;
|
||||
stats?: DevTools.HeapSnapshotModel.HeapSnapshotModel.Statistics;
|
||||
staticData?: DevTools.HeapSnapshotModel.HeapSnapshotModel.StaticData | null;
|
||||
@@ -461,16 +459,13 @@ export class McpResponse implements Response {
|
||||
}
|
||||
|
||||
setHeapSnapshotAggregates(
|
||||
aggregates: Record<
|
||||
string,
|
||||
DevTools.HeapSnapshotModel.HeapSnapshotModel.AggregatedInfo
|
||||
>,
|
||||
aggregateData: HeapSnapshotAggregateData,
|
||||
options?: PaginationOptions,
|
||||
) {
|
||||
this.#heapSnapshotOptions = {
|
||||
...this.#heapSnapshotOptions,
|
||||
include: true,
|
||||
aggregates,
|
||||
aggregateData,
|
||||
pagination: options,
|
||||
};
|
||||
}
|
||||
@@ -877,6 +872,10 @@ export class McpResponse implements Response {
|
||||
heapSnapshot?: {
|
||||
stats?: object;
|
||||
staticData?: object;
|
||||
aggregateStats?: {
|
||||
objectCount: number;
|
||||
totalSelfSize: number;
|
||||
};
|
||||
};
|
||||
heapSnapshotData?: object[];
|
||||
heapSnapshotNodes?: readonly object[];
|
||||
@@ -1169,15 +1168,28 @@ Call ${handleDialog.name} to handle it before continuing.`);
|
||||
structuredContent.heapSnapshot = structuredContent.heapSnapshot || {};
|
||||
structuredContent.heapSnapshot.staticData = staticData;
|
||||
}
|
||||
const aggregates = this.#heapSnapshotOptions.aggregates;
|
||||
if (aggregates) {
|
||||
const sortedEntries = HeapSnapshotFormatter.sort(aggregates);
|
||||
const aggregateData = this.#heapSnapshotOptions.aggregateData;
|
||||
if (aggregateData) {
|
||||
const sortedEntries = HeapSnapshotFormatter.sort(
|
||||
aggregateData.aggregates,
|
||||
);
|
||||
|
||||
const paginationData = this.#dataWithPagination(
|
||||
sortedEntries,
|
||||
this.#heapSnapshotOptions.pagination,
|
||||
);
|
||||
|
||||
response.push(`Objects: ${aggregateData.objectCount}`);
|
||||
response.push(
|
||||
`Total shallow size: ${DevTools.I18n.ByteUtilities.formatBytesToKb(
|
||||
aggregateData.totalSelfSize,
|
||||
)}`,
|
||||
);
|
||||
structuredContent.heapSnapshot = structuredContent.heapSnapshot || {};
|
||||
structuredContent.heapSnapshot.aggregateStats = {
|
||||
objectCount: aggregateData.objectCount,
|
||||
totalSelfSize: aggregateData.totalSelfSize,
|
||||
};
|
||||
structuredContent.pagination = paginationData.pagination;
|
||||
response.push(...paginationData.info);
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import type {ParsedArguments} from '../bin/chrome-devtools-mcp-cli-options.js';
|
||||
import type {
|
||||
AggregatedInfoWithId,
|
||||
HeapSnapshotAggregateData,
|
||||
HeapSnapshotClassDiff,
|
||||
HeapSnapshotDetailedClassDiff,
|
||||
DuplicateStringGroup,
|
||||
@@ -107,10 +107,7 @@ export interface DevToolsData {
|
||||
export interface Response {
|
||||
appendResponseLine(value: string): void;
|
||||
setHeapSnapshotAggregates(
|
||||
aggregates: Record<
|
||||
string,
|
||||
DevTools.HeapSnapshotModel.HeapSnapshotModel.AggregatedInfo
|
||||
>,
|
||||
aggregateData: HeapSnapshotAggregateData,
|
||||
options?: PaginationOptions,
|
||||
): void;
|
||||
setHeapSnapshotStats(
|
||||
@@ -258,7 +255,7 @@ export type Context = Readonly<{
|
||||
getHeapSnapshotAggregates(
|
||||
filePath: string,
|
||||
filterName?: string,
|
||||
): Promise<Record<string, AggregatedInfoWithId>>;
|
||||
): Promise<HeapSnapshotAggregateData>;
|
||||
getHeapSnapshotDuplicateStrings(
|
||||
filePath: string,
|
||||
): Promise<DuplicateStringGroup[]>;
|
||||
|
||||
@@ -63,6 +63,8 @@ Showing 1-6 of 6 (Page 1 of 1).
|
||||
|
||||
exports[`memory > get_heapsnapshot_details > with default options 1`] = `
|
||||
## Heap Snapshot Data
|
||||
Objects: 11940
|
||||
Total shallow size: 802 kB
|
||||
Showing 1-157 of 157 (Page 1 of 1).
|
||||
id,name,count,selfSize,maxRetainedSize
|
||||
2,(system),3205,199 kB,655 kB
|
||||
@@ -226,6 +228,8 @@ id,name,count,selfSize,maxRetainedSize
|
||||
|
||||
exports[`memory > get_heapsnapshot_details > with objectsRetainedByContexts filterName 1`] = `
|
||||
## Heap Snapshot Data
|
||||
Objects: 148
|
||||
Total shallow size: 3.5 kB
|
||||
Showing 1-4 of 4 (Page 1 of 1).
|
||||
id,name,count,selfSize,maxRetainedSize
|
||||
1,system / Context,116,2.5 kB,3.5 kB
|
||||
|
||||
@@ -168,11 +168,11 @@ describe('memory', () => {
|
||||
'tests/fixtures/example.heapsnapshot',
|
||||
);
|
||||
|
||||
const aggregates = await context.getHeapSnapshotAggregates(
|
||||
const aggregateData = await context.getHeapSnapshotAggregates(
|
||||
filePath,
|
||||
'objectsRetainedByContexts',
|
||||
);
|
||||
const aggregate = Object.values(aggregates).find(
|
||||
const aggregate = Object.values(aggregateData.aggregates).find(
|
||||
a => a.name === 'Function',
|
||||
);
|
||||
assert.ok(aggregate);
|
||||
|
||||
Reference in New Issue
Block a user