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:
Dominik Inführ
2026-07-09 12:33:34 +02:00
committed by GitHub
parent 9bc61b43d6
commit 15a6b789da
6 changed files with 53 additions and 29 deletions
+18 -7
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+3 -6
View File
@@ -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[]>;
+4
View File
@@ -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
+2 -2
View File
@@ -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);