MCPcopy Create free account
hub / github.com/garrytan/gstack / getMemorySnapshot

Method getMemorySnapshot

browse/src/browser-manager.ts:1059–1150  ·  view source on GitHub ↗

* Diagnostic for `$B memory` and the /memory endpoint. * * Collects: * - Bun process memory (cross-platform, accurate, no shelling). * - Per-tab JS heap via CDP Performance.getMetrics — the most portable * per-tab signal CDP exposes. Misses native/GPU/Skia/cache memory *

(structures: MemoryStructureStats)

Source from the content-addressed store, hash-verified

1057 * browser-manager doesn't take a hard dep on every buffer-owning module.
1058 */
1059 async getMemorySnapshot(structures: MemoryStructureStats): Promise<MemorySnapshot> {
1060 const bunMem = process.memoryUsage();
1061 const notes: string[] = [];
1062
1063 // Per-tab JS heap. Lazy: only the pages we already track. A target
1064 // that died mid-snapshot is omitted, never throws.
1065 const tabs: MemoryTabSnapshot[] = [];
1066 for (const [id, page] of this.pages) {
1067 try {
1068 const url = (() => { try { return page.url(); } catch { return ''; } })();
1069 const title = await page.title().catch(() => '');
1070 const metrics = await withCdpSession(page, async (session) => {
1071 await session.send('Performance.enable').catch(() => undefined);
1072 const result = await session.send('Performance.getMetrics');
1073 return ((result as { metrics?: Array<{ name: string; value: number }> }).metrics) ?? [];
1074 });
1075 const mm: Record<string, number> = {};
1076 for (const m of metrics) mm[m.name] = m.value;
1077 tabs.push({
1078 id,
1079 url,
1080 title,
1081 jsHeapUsed: mm.JSHeapUsedSize ?? 0,
1082 jsHeapTotal: mm.JSHeapTotalSize ?? 0,
1083 documents: mm.Documents ?? 0,
1084 nodes: mm.Nodes ?? 0,
1085 listeners: mm.JSEventListeners ?? 0,
1086 });
1087 } catch {
1088 // Target died or CDP unavailable mid-snapshot — skip this tab.
1089 }
1090 }
1091
1092 // Chromium process tree. Browser handle may be on the `browser` field
1093 // (launched mode) or accessible via `context.browser()` (persistent
1094 // context / headed mode); try both.
1095 let processes: MemoryProcess[] | null = null;
1096 const browser: Browser | null = this.browser ?? (this.context ? this.context.browser() : null);
1097 if (browser) {
1098 try {
1099 // `newBrowserCDPSession` is browser-wide. Not exposed on every
1100 // Playwright TypeScript surface, but present at runtime on the
1101 // Browser instance — use a typed cast to avoid the @ts-expect-error.
1102 type BrowserWithCDP = Browser & {
1103 newBrowserCDPSession?: () => Promise<{
1104 send: (method: string, params?: unknown) => Promise<unknown>;
1105 detach: () => Promise<void>;
1106 }>;
1107 };
1108 const maybeFactory = (browser as BrowserWithCDP).newBrowserCDPSession;
1109 if (typeof maybeFactory === 'function') {
1110 const browserSession = await maybeFactory.call(browser);
1111 try {
1112 const info = (await browserSession.send('SystemInfo.getProcessInfo')) as {
1113 processInfo?: Array<{ id: number; type: string; cpuTime: number }>;
1114 };
1115 processes = (info.processInfo ?? []).map((p) => ({
1116 id: p.id,

Callers 2

handleMemoryCommandFunction · 0.80
buildMemorySnapshotJsonFunction · 0.80

Calls 4

withCdpSessionFunction · 0.90
sendMethod · 0.80
callMethod · 0.80
pushMethod · 0.45

Tested by

no test coverage detected