Skip to content

Commit b204f85

Browse files
committed
Use ranges with human readable sizes
1 parent fadbf41 commit b204f85

8 files changed

Lines changed: 376 additions & 81 deletions

File tree

docs/tool-reference.md

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -546,9 +546,9 @@ in the DevTools Elements panel (if any).
546546
- **filePath** (string) **(required)**: A path to a .heapsnapshot file to read.
547547
- **nodeId** (number) **(required)**: The node ID to get outgoing edges for.
548548
- **excludePrimitives** (boolean) _(optional)_: Whether to exclude primitive target nodes. Default is true.
549-
- **minRetainedSize** (number) _(optional)_: Minimum retained size in bytes for target nodes.
550549
- **pageIdx** (number) _(optional)_: The page index for pagination.
551550
- **pageSize** (number) _(optional)_: The page size for pagination.
551+
- **retainedSize** (string) _(optional)_: Inclusive retained size range (e.g. "1MB-2MB", "-1MB", or "1MB-") for target nodes. A single value is treated as a minimum. Currently, only the lower bound is applied.
552552
- **sortBy** (enum: "retainedSize", "selfSize", "name") _(optional)_: Sort order for edges. Default is retainedSize.
553553

554554
---
@@ -603,21 +603,19 @@ in the DevTools Elements panel (if any).
603603

604604
### `query_heapsnapshot_objects`
605605

606-
**Description:** Loads a memory heapsnapshot and queries objects matching specific filters (className, propertyName, nodeType, minRetainedSize, maxRetainedSize, minSelfSize, isDetached, sortBy). (requires flag: --memoryDebugging=true)
606+
**Description:** Loads a memory heapsnapshot and queries objects matching specific filters (className, propertyName, nodeType, retainedSize, selfSize, isDetached, sortBy). (requires flag: --memoryDebugging=true)
607607

608608
**Parameters:**
609609

610610
- **filePath** (string) **(required)**: A path to a .heapsnapshot file to read.
611611
- **className** (string) _(optional)_: Optional regex or text matching object class name.
612612
- **isDetached** (boolean) _(optional)_: Whether to filter for detached DOM nodes.
613-
- **maxRetainedSize** (number) _(optional)_: Maximum retained size in bytes.
614-
- **maxSelfSize** (number) _(optional)_: Maximum self size in bytes.
615-
- **minRetainedSize** (number) _(optional)_: Minimum retained size in bytes.
616-
- **minSelfSize** (number) _(optional)_: Minimum self size in bytes.
617613
- **nodeType** (string) _(optional)_: Optional V8 node type filter (e.g. object, closure, string, array, code).
618614
- **pageIdx** (number) _(optional)_: The page index for pagination.
619615
- **pageSize** (number) _(optional)_: The page size for pagination.
620616
- **propertyName** (string) _(optional)_: Optional property name filter for outgoing reference edges.
617+
- **retainedSize** (string) _(optional)_: Inclusive retained size range (e.g. "1MB-2MB", "-1MB", or "1MB-"). A single value is treated as a minimum.
618+
- **selfSize** (string) _(optional)_: Inclusive self size range (e.g. "1MB-2MB", "-1MB", or "1MB-"). A single value is treated as a minimum.
621619
- **sortBy** (enum: "retainedSize", "selfSize", "id") _(optional)_: Sort order for results. Default is retainedSize.
622620

623621
---

src/config/cli-options.ts

Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -518,10 +518,11 @@ export const commands: Commands = {
518518
required: false,
519519
enum: ['retainedSize', 'selfSize', 'name'],
520520
},
521-
minRetainedSize: {
522-
name: 'minRetainedSize',
523-
type: 'number',
524-
description: 'Minimum retained size in bytes for target nodes.',
521+
retainedSize: {
522+
name: 'retainedSize',
523+
type: 'string',
524+
description:
525+
'Inclusive retained size range (e.g. "1MB-2MB", "-1MB", or "1MB-") for target nodes. A single value is treated as a minimum. Currently, only the lower bound is applied.',
525526
required: false,
526527
},
527528
excludePrimitives: {
@@ -1102,7 +1103,7 @@ export const commands: Commands = {
11021103
},
11031104
query_heapsnapshot_objects: {
11041105
description:
1105-
'Loads a memory heapsnapshot and queries objects matching specific filters (className, propertyName, nodeType, minRetainedSize, maxRetainedSize, minSelfSize, isDetached, sortBy). (requires flag: --memoryDebugging=true)',
1106+
'Loads a memory heapsnapshot and queries objects matching specific filters (className, propertyName, nodeType, retainedSize, selfSize, isDetached, sortBy). (requires flag: --memoryDebugging=true)',
11061107
category: 'Memory',
11071108
args: {
11081109
filePath: {
@@ -1131,28 +1132,18 @@ export const commands: Commands = {
11311132
'Optional V8 node type filter (e.g. object, closure, string, array, code).',
11321133
required: false,
11331134
},
1134-
minRetainedSize: {
1135-
name: 'minRetainedSize',
1136-
type: 'number',
1137-
description: 'Minimum retained size in bytes.',
1138-
required: false,
1139-
},
1140-
maxRetainedSize: {
1141-
name: 'maxRetainedSize',
1142-
type: 'number',
1143-
description: 'Maximum retained size in bytes.',
1144-
required: false,
1145-
},
1146-
minSelfSize: {
1147-
name: 'minSelfSize',
1148-
type: 'number',
1149-
description: 'Minimum self size in bytes.',
1135+
retainedSize: {
1136+
name: 'retainedSize',
1137+
type: 'string',
1138+
description:
1139+
'Inclusive retained size range (e.g. "1MB-2MB", "-1MB", or "1MB-"). A single value is treated as a minimum.',
11501140
required: false,
11511141
},
1152-
maxSelfSize: {
1153-
name: 'maxSelfSize',
1154-
type: 'number',
1155-
description: 'Maximum self size in bytes.',
1142+
selfSize: {
1143+
name: 'selfSize',
1144+
type: 'string',
1145+
description:
1146+
'Inclusive self size range (e.g. "1MB-2MB", "-1MB", or "1MB-"). A single value is treated as a minimum.',
11561147
required: false,
11571148
},
11581149
isDetached: {

src/telemetry/tool_call_metrics.json

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -831,11 +831,16 @@
831831
},
832832
{
833833
"name": "min_retained_size",
834-
"argType": "number"
834+
"argType": "number",
835+
"isDeprecated": true
835836
},
836837
{
837838
"name": "exclude_primitives",
838839
"argType": "boolean"
840+
},
841+
{
842+
"name": "retained_size_length",
843+
"argType": "number"
839844
}
840845
]
841846
},
@@ -1000,19 +1005,23 @@
10001005
},
10011006
{
10021007
"name": "min_retained_size",
1003-
"argType": "number"
1008+
"argType": "number",
1009+
"isDeprecated": true
10041010
},
10051011
{
10061012
"name": "max_retained_size",
1007-
"argType": "number"
1013+
"argType": "number",
1014+
"isDeprecated": true
10081015
},
10091016
{
10101017
"name": "min_self_size",
1011-
"argType": "number"
1018+
"argType": "number",
1019+
"isDeprecated": true
10121020
},
10131021
{
10141022
"name": "max_self_size",
1015-
"argType": "number"
1023+
"argType": "number",
1024+
"isDeprecated": true
10161025
},
10171026
{
10181027
"name": "is_detached",
@@ -1029,6 +1038,14 @@
10291038
{
10301039
"name": "page_size",
10311040
"argType": "number"
1041+
},
1042+
{
1043+
"name": "retained_size_length",
1044+
"argType": "number"
1045+
},
1046+
{
1047+
"name": "self_size_length",
1048+
"argType": "number"
10321049
}
10331050
]
10341051
}

src/tools/memory.ts

Lines changed: 46 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66

77
import {zod} from '../third_party/index.js';
8+
import {parseByteSizeRange} from '../utils/bytes.js';
89

910
import {ToolCategory} from './categories.js';
1011
import {definePageTool, defineTool} from './ToolDefinition.js';
@@ -19,6 +20,23 @@ const HEAP_SNAPSHOT_FILTERS: readonly [string, ...string[]] = [
1920
'attributedToSpecificNativeContext',
2021
];
2122

23+
function byteSizeRangeSchema(description: string) {
24+
return zod
25+
.string()
26+
.superRefine((value, context) => {
27+
try {
28+
parseByteSizeRange(value);
29+
} catch (error) {
30+
context.addIssue({
31+
code: zod.ZodIssueCode.custom,
32+
message:
33+
error instanceof Error ? error.message : 'Invalid byte size range',
34+
});
35+
}
36+
})
37+
.describe(description);
38+
}
39+
2240
export const takeHeapSnapshot = definePageTool({
2341
name: 'take_heapsnapshot',
2442
description: `Capture a heap snapshot of the currently selected page. Use to analyze the memory distribution of JavaScript objects and debug memory leaks.`,
@@ -305,10 +323,9 @@ export const getHeapSnapshotEdges = defineTool({
305323
.enum(['retainedSize', 'selfSize', 'name'])
306324
.optional()
307325
.describe('Sort order for edges. Default is retainedSize.'),
308-
minRetainedSize: zod
309-
.number()
310-
.optional()
311-
.describe('Minimum retained size in bytes for target nodes.'),
326+
retainedSize: byteSizeRangeSchema(
327+
'Inclusive retained size range (e.g. "1MB-2MB", "-1MB", or "1MB-") for target nodes. A single value is treated as a minimum. Currently, only the lower bound is applied.',
328+
).optional(),
312329
excludePrimitives: zod
313330
.boolean()
314331
.optional()
@@ -317,12 +334,17 @@ export const getHeapSnapshotEdges = defineTool({
317334
pageSize: zod.number().optional().describe('The page size for pagination.'),
318335
},
319336
handler: async (request, response, context) => {
337+
const retainedSize =
338+
request.params.retainedSize === undefined
339+
? undefined
340+
: parseByteSizeRange(request.params.retainedSize);
320341
const edges = await context.getHeapSnapshotEdges(
321342
request.params.filePath,
322343
request.params.nodeId,
323344
{
324345
sortBy: request.params.sortBy ?? 'retainedSize',
325-
minRetainedSize: request.params.minRetainedSize,
346+
// DevTools currently only supports a lower retained-size bound here.
347+
minRetainedSize: retainedSize?.min,
326348
excludePrimitives: request.params.excludePrimitives ?? true,
327349
},
328350
);
@@ -469,7 +491,7 @@ export const getHeapSnapshotObjectDetails = defineTool({
469491
export const queryHeapSnapshotObjects = defineTool({
470492
name: 'query_heapsnapshot_objects',
471493
description:
472-
'Loads a memory heapsnapshot and queries objects matching specific filters (className, propertyName, nodeType, minRetainedSize, maxRetainedSize, minSelfSize, isDetached, sortBy).',
494+
'Loads a memory heapsnapshot and queries objects matching specific filters (className, propertyName, nodeType, retainedSize, selfSize, isDetached, sortBy).',
473495
annotations: {
474496
category: ToolCategory.MEMORY,
475497
readOnlyHint: true,
@@ -493,22 +515,12 @@ export const queryHeapSnapshotObjects = defineTool({
493515
.describe(
494516
'Optional V8 node type filter (e.g. object, closure, string, array, code).',
495517
),
496-
minRetainedSize: zod
497-
.number()
498-
.optional()
499-
.describe('Minimum retained size in bytes.'),
500-
maxRetainedSize: zod
501-
.number()
502-
.optional()
503-
.describe('Maximum retained size in bytes.'),
504-
minSelfSize: zod
505-
.number()
506-
.optional()
507-
.describe('Minimum self size in bytes.'),
508-
maxSelfSize: zod
509-
.number()
510-
.optional()
511-
.describe('Maximum self size in bytes.'),
518+
retainedSize: byteSizeRangeSchema(
519+
'Inclusive retained size range (e.g. "1MB-2MB", "-1MB", or "1MB-"). A single value is treated as a minimum.',
520+
).optional(),
521+
selfSize: byteSizeRangeSchema(
522+
'Inclusive self size range (e.g. "1MB-2MB", "-1MB", or "1MB-"). A single value is treated as a minimum.',
523+
).optional(),
512524
isDetached: zod
513525
.boolean()
514526
.optional()
@@ -521,16 +533,24 @@ export const queryHeapSnapshotObjects = defineTool({
521533
pageSize: zod.number().optional().describe('The page size for pagination.'),
522534
},
523535
handler: async (request, response, context) => {
536+
const retainedSize =
537+
request.params.retainedSize === undefined
538+
? undefined
539+
: parseByteSizeRange(request.params.retainedSize);
540+
const selfSize =
541+
request.params.selfSize === undefined
542+
? undefined
543+
: parseByteSizeRange(request.params.selfSize);
524544
const range = await context.queryHeapSnapshotObjects(
525545
request.params.filePath,
526546
{
527547
className: request.params.className,
528548
propertyName: request.params.propertyName,
529549
nodeType: request.params.nodeType,
530-
minRetainedSize: request.params.minRetainedSize,
531-
maxRetainedSize: request.params.maxRetainedSize,
532-
minSelfSize: request.params.minSelfSize,
533-
maxSelfSize: request.params.maxSelfSize,
550+
minRetainedSize: retainedSize?.min,
551+
maxRetainedSize: retainedSize?.max,
552+
minSelfSize: selfSize?.min,
553+
maxSelfSize: selfSize?.max,
534554
isDetached: request.params.isDetached,
535555
sortBy: request.params.sortBy,
536556
},

0 commit comments

Comments
 (0)