11import json
22
33
4+ _SKIP_NAMES = {
5+ "torch::unwind::unwind()" ,
6+ "torch::CapturedTraceback::gather(bool, bool, bool)" ,
7+ }
8+
9+ _CPYTHON_MARKERS = (
10+ "/usr/local/src/conda/python" ,
11+ "/conda-bld/python" ,
12+ "/cpython/" ,
13+ )
14+
15+
16+ def _is_cpython_c_frame (fn : str , name : str ) -> bool :
17+ if any (m in fn for m in _CPYTHON_MARKERS ):
18+ return True
19+ if fn .endswith (".c" ) and name .startswith (("_Py" , "Py" , "pyrun" , "pymain" , "run_" )):
20+ return True
21+ return False
22+
23+
24+ def _shorten_path (path : str ) -> str :
25+ markers = ["/site-packages/" , "/lib/python" ]
26+ for marker in markers :
27+ idx = path .find (marker )
28+ if idx >= 0 :
29+ return path [idx + len (marker ) :]
30+ return path
31+
32+
433def _extract_frames (frames : list [dict ]) -> list [str ]:
534 result = []
635 for f in frames :
736 fn = f .get ("filename" , "" )
837 name = f .get ("name" , "" )
938 line = f .get ("line" , 0 )
10- if not name or name in (
11- "torch::unwind::unwind()" ,
12- "torch::CapturedTraceback::gather(bool, bool, bool)" ,
13- ):
39+ if not name or name in _SKIP_NAMES :
40+ continue
41+ if _is_cpython_c_frame (fn , name ):
1442 continue
1543 if fn and fn != "??" and fn != "" :
16- result .append (f"{ fn } :{ line } { name } " )
44+ result .append (f"{ _shorten_path ( fn ) } :{ line } { name } " )
1745 elif name :
1846 result .append (name )
1947 return result
@@ -175,20 +203,21 @@ def generate_memory_html(
175203<meta charset="utf-8">
176204<title>__TITLE__</title>
177205<style>
206+ @import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600&family=Inter:wght@400;500;600&display=swap');
178207 :root {
179- --bg: #0f1117 ;
180- --surface: #1a1d2e ;
181- --border: #2a2d3e ;
182- --text: #e2e4e9 ;
183- --text-muted: #8b8fa3 ;
184- --accent: #6366f1 ;
185- --accent-light: rgba(99, 102, 241 , 0.15 );
186- --accent-stroke: rgba(99, 102, 241 , 0.8 );
187- --hwm-color: #f59e0b ;
188- --grid: rgba(255, 255, 255, 0.04 );
189- --tooltip-bg: #1e2235 ;
190- --font: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Inter' , sans-serif;
191- --mono: 'SF Mono', 'Fira Code', 'Consolas ', monospace;
208+ --bg: #0E0E0E ;
209+ --surface: #1a1a1a ;
210+ --border: rgba(255, 255, 255, 0.10) ;
211+ --text: rgba(255, 255, 255, 0.92) ;
212+ --text-muted: rgba(255, 255, 255, 0.50) ;
213+ --accent: #3E93CC ;
214+ --accent-light: rgba(62, 147, 204 , 0.12 );
215+ --accent-stroke: rgba(62, 147, 204 , 0.7 );
216+ --hwm-color: rgba(255, 255, 255, 0.60) ;
217+ --grid: rgba(255, 255, 255, 0.03 );
218+ --tooltip-bg: #1f1f1f ;
219+ --font: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
220+ --mono: 'IBM Plex Mono', 'Fira Mono ', monospace;
192221 }
193222
194223 * { margin: 0; padding: 0; box-sizing: border-box; }
@@ -212,7 +241,7 @@ def generate_memory_html(
212241 flex-shrink: 0;
213242 }
214243
215- #header h1 { font-size: 16px ; font-weight: 600 ; }
244+ #header h1 { font-size: 14px ; font-weight: 500; font-family: var(--mono); letter-spacing: 0.03em; text-transform: uppercase ; }
216245
217246 #controls {
218247 display: flex;
@@ -240,15 +269,16 @@ def generate_memory_html(
240269 .toggle:hover { color: var(--text); }
241270
242271 .stat {
243- font-size: 12px;
272+ font-size: 11px;
273+ font-family: var(--mono);
244274 color: var(--text-muted);
245275 padding: 4px 10px;
246- background: var(--surface );
247- border-radius: 4px ;
276+ background: rgba(255,255,255,0.04 );
277+ border-radius: 3px ;
248278 border: 1px solid var(--border);
249279 }
250280
251- .stat strong { color: var(--text); font-weight: 600 ; }
281+ .stat strong { color: var(--text); font-weight: 500 ; }
252282
253283 #main {
254284 display: flex;
@@ -277,8 +307,11 @@ def generate_memory_html(
277307 #detail-header {
278308 padding: 12px 16px;
279309 border-bottom: 1px solid var(--border);
280- font-size: 13px;
281- font-weight: 600;
310+ font-size: 11px;
311+ font-weight: 500;
312+ font-family: var(--mono);
313+ text-transform: uppercase;
314+ letter-spacing: 0.05em;
282315 display: flex;
283316 justify-content: space-between;
284317 align-items: center;
@@ -301,13 +334,62 @@ def generate_memory_html(
301334 #detail-body::-webkit-scrollbar-track { background: transparent; }
302335 #detail-body::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
303336
337+ .stack-group {
338+ border-bottom: 1px solid rgba(255,255,255,0.05);
339+ }
340+
341+ .stack-group-header {
342+ display: flex;
343+ align-items: center;
344+ gap: 6px;
345+ padding: 6px 16px;
346+ font-family: var(--mono);
347+ font-size: 10px;
348+ font-weight: 500;
349+ text-transform: uppercase;
350+ letter-spacing: 0.05em;
351+ color: var(--text-muted);
352+ cursor: pointer;
353+ user-select: none;
354+ background: rgba(255,255,255,0.02);
355+ }
356+
357+ .stack-group-header:hover { background: rgba(255,255,255,0.04); }
358+
359+ .stack-group-header .chevron {
360+ display: inline-block;
361+ width: 12px;
362+ font-size: 8px;
363+ transition: transform 0.15s;
364+ color: rgba(255,255,255,0.3);
365+ }
366+
367+ .stack-group.collapsed .chevron { transform: rotate(-90deg); }
368+ .stack-group.collapsed .stack-group-frames { display: none; }
369+
370+ .stack-group-header .group-count {
371+ margin-left: auto;
372+ font-size: 9px;
373+ color: rgba(255,255,255,0.25);
374+ }
375+
376+ .stack-group-header .group-tag {
377+ padding: 1px 6px;
378+ border-radius: 2px;
379+ font-size: 9px;
380+ }
381+
382+ .group-tag.user { background: rgba(73, 201, 99, 0.15); color: #49C963; }
383+ .group-tag.torch { background: rgba(62, 147, 204, 0.15); color: #3E93CC; }
384+ .group-tag.cpp { background: rgba(189, 147, 249, 0.15); color: #bd93f9; }
385+ .group-tag.python { background: rgba(255, 255, 255, 0.06); color: var(--text-muted); }
386+
304387 .stack-frame {
305- padding: 4px 16px;
388+ padding: 3px 16px 3px 34px ;
306389 font-family: var(--mono);
307390 font-size: 11px;
308- line-height: 1.6 ;
391+ line-height: 1.5 ;
309392 color: var(--text-muted);
310- border-bottom: 1px solid rgba(255,255,255,0.02);
311393 cursor: pointer;
312394 overflow: hidden;
313395 }
@@ -326,16 +408,10 @@ def generate_memory_html(
326408
327409 .stack-frame:hover { background: rgba(255,255,255,0.03); color: var(--text); }
328410
329- .stack-frame .frame-idx {
330- display: inline-block; width: 24px;
331- color: rgba(255,255,255,0.15); text-align: right;
332- margin-right: 8px; font-size: 10px;
333- vertical-align: top;
334- }
335-
336- .stack-frame .frame-cpp { color: #f97316; }
337- .stack-frame .frame-file { color: var(--accent); }
338- .stack-frame .frame-func { color: var(--text); }
411+ .stack-frame .frame-cpp { color: #bd93f9; }
412+ .stack-frame .frame-file { color: #3E93CC; }
413+ .stack-frame .frame-func { color: #49C963; }
414+ .stack-frame .frame-basename { color: var(--text); }
339415
340416 .empty-detail {
341417 padding: 24px 16px;
@@ -349,19 +425,20 @@ def generate_memory_html(
349425 .grid line { stroke: var(--grid); }
350426 .grid path { stroke: none; }
351427
352- .hwm-line { stroke: var(--hwm-color); stroke-width: 1 ; stroke-dasharray: 6 4; }
353- .hwm-label { fill: var(--hwm-color); font-size: 11px; font-family: var(--font ); font-weight: 600 ; }
428+ .hwm-line { stroke: var(--hwm-color); stroke-width: 0.75 ; stroke-dasharray: 8 4; }
429+ .hwm-label { fill: var(--hwm-color); font-size: 11px; font-family: var(--mono ); font-weight: 500; letter-spacing: 0.02em ; }
354430
355- .alloc-poly { stroke: rgba(0,0,0,0.3 ); stroke-width: 0.5; cursor: pointer; }
356- .alloc-poly:hover { stroke: white ; stroke-width: 1.5 ; }
431+ .alloc-poly { stroke: rgba(0,0,0,0.5 ); stroke-width: 0.5; cursor: pointer; transition: opacity 0.1s ; }
432+ .alloc-poly:hover { stroke: rgba(255,255,255,0.8) ; stroke-width: 1; }
357433
358434 #tooltip {
359435 position: fixed; display: none;
360- background: var(--tooltip-bg) ; border: 1px solid var(--border );
361- border-radius: 6px ; padding: 10px 12px ;
362- font-size: 12px; line-height: 1.5 ;
436+ background: #1f1f1f ; border: 1px solid rgba(255,255,255,0.08 );
437+ border-radius: 4px ; padding: 10px 14px ;
438+ font-size: 12px; line-height: 1.6 ;
363439 pointer-events: none; z-index: 100; max-width: 500px;
364- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
440+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.6);
441+ font-family: var(--mono);
365442 }
366443
367444 #tooltip .tt-label { color: var(--text-muted); margin-right: 4px; }
@@ -465,13 +542,12 @@ def generate_memory_html(
465542document.getElementById('allocs-stat').textContent = META.num_allocs.toLocaleString();
466543
467544const PALETTE = [
468- '#6366f1', '#8b5cf6', '#a78bfa', '#c084fc',
469- '#ec4899', '#f43f5e', '#fb7185', '#f97316',
470- '#f59e0b', '#eab308', '#84cc16', '#22c55e',
471- '#14b8a6', '#06b6d4', '#0ea5e9', '#3b82f6',
472- '#818cf8', '#a5b4fc', '#c4b5fd', '#e879f9',
473- '#f472b6', '#fb923c', '#fbbf24', '#a3e635',
474- '#34d399', '#2dd4bf', '#22d3ee', '#38bdf8',
545+ '#3E93CC', '#2E7DB5', '#5BA8D9', '#78BBE3',
546+ '#49C963', '#3AA852', '#6DD883', '#8DE49D',
547+ '#bd93f9', '#a06eed', '#d4b5ff', '#9054e0',
548+ '#CC6B3E', '#E08A5B', '#B55A30', '#F0A478',
549+ '#3ECCC1', '#5BD9D0', '#2EB5AC', '#78E3DC',
550+ '#C9CC3E', '#D9DB5B', '#B5B72E', '#E3E478',
475551];
476552
477553function getColor(stackIdx) {
@@ -492,30 +568,71 @@ def generate_memory_html(
492568
493569function hideTooltip() { tooltipEl.style.display = 'none'; }
494570
571+ function classifyFrame(frame) {
572+ if (!frame.includes(':') && frame.includes('::')) return 'cpp';
573+ if (frame.includes('/site-packages/torch/') || frame.includes('/torch/')) return 'torch';
574+ if (frame.includes('/lib/python') || frame.includes('/conda/') || frame.includes('lib/python')) return 'torch';
575+ return 'user';
576+ }
577+
578+ function renderFrame(frame) {
579+ const hasColon = frame.includes(':');
580+ const isCpp = !hasColon && frame.includes('::');
581+ if (isCpp) {
582+ const parts = frame.split('::');
583+ const funcName = parts[parts.length - 1];
584+ const ns = parts.slice(0, -1).join('::');
585+ return `<span class="frame-cpp">${ns}::</span><span class="frame-basename">${funcName}</span>`;
586+ }
587+ if (hasColon) {
588+ const sp = frame.indexOf(' ', frame.lastIndexOf(':'));
589+ if (sp > 0) {
590+ const filePart = frame.substring(0, sp);
591+ const funcPart = frame.substring(sp + 1);
592+ const lastSlash = filePart.lastIndexOf('/');
593+ const basename = lastSlash >= 0 ? filePart.substring(lastSlash + 1) : filePart;
594+ const dir = lastSlash >= 0 ? filePart.substring(0, lastSlash + 1) : '';
595+ return `<span class="frame-file">${dir}</span><span class="frame-basename">${basename}</span> <span class="frame-func">${funcPart}</span>`;
596+ }
597+ return `<span class="frame-file">${frame}</span>`;
598+ }
599+ return frame;
600+ }
601+
602+ const GROUP_LABELS = { user: 'Your Code', torch: 'PyTorch / Python', cpp: 'C++ Runtime' };
603+
495604function renderStack(stackIdx, label) {
496605 const stack = STACKS[stackIdx] || [];
497606 detailStats.textContent = label;
498607 if (!stack.length) {
499608 detailBody.innerHTML = '<div class="empty-detail">No frames recorded</div>';
500609 return;
501610 }
502- detailBody.innerHTML = stack.map((frame, i) => {
503- const hasColon = frame.includes(':');
504- const isCpp = !hasColon && frame.includes('::');
505- let inner;
506- if (isCpp) {
507- inner = `<span class="frame-cpp">${frame}</span>`;
508- } else if (hasColon) {
509- const sp = frame.indexOf(' ', frame.lastIndexOf(':'));
510- if (sp > 0) {
511- inner = `<span class="frame-file">${frame.substring(0, sp)}</span> <span class="frame-func">${frame.substring(sp + 1)}</span>`;
512- } else {
513- inner = `<span class="frame-file">${frame}</span>`;
514- }
515- } else {
516- inner = frame;
611+
612+ const groups = [];
613+ let cur = null;
614+ for (const frame of stack) {
615+ const cls = classifyFrame(frame);
616+ if (!cur || cur.cls !== cls) {
617+ cur = { cls, frames: [] };
618+ groups.push(cur);
517619 }
518- return `<div class="stack-frame" onclick="this.classList.toggle('expanded')"><span class="frame-idx">${i}</span><span class="frame-text">${inner}</span></div>`;
620+ cur.frames.push(frame);
621+ }
622+
623+ detailBody.innerHTML = groups.map(g => {
624+ const collapsed = g.cls !== 'user' ? ' collapsed' : '';
625+ const framesHtml = g.frames.map(f =>
626+ `<div class="stack-frame" onclick="event.stopPropagation();this.classList.toggle('expanded')"><span class="frame-text">${renderFrame(f)}</span></div>`
627+ ).join('');
628+ return `<div class="stack-group${collapsed}">
629+ <div class="stack-group-header" onclick="this.parentElement.classList.toggle('collapsed')">
630+ <span class="chevron">▼</span>
631+ <span class="group-tag ${g.cls}">${GROUP_LABELS[g.cls]}</span>
632+ <span class="group-count">${g.frames.length}</span>
633+ </div>
634+ <div class="stack-group-frames">${framesHtml}</div>
635+ </div>`;
519636 }).join('');
520637}
521638
0 commit comments