@@ -59,20 +59,29 @@ def _categorize_stack(frames: list[str]) -> str:
5959
6060def process_snapshot (
6161 snapshot : dict , device : int = 0
62- ) -> tuple [list [dict ], list [dict ], list [list [str ]], list [str ], int ]:
62+ ) -> tuple [list [dict ], list [dict ], list [str ], list [list [ int ]], list [str ], int ]:
6363 traces = snapshot .get ("device_traces" , [])
6464 if device >= len (traces ):
65- return [], [], [], [], 0
66-
67- stack_to_idx : dict [tuple [str , ...], int ] = {}
68- stacks : list [list [str ]] = []
69-
70- def get_stack_idx (frames : list [dict ]) -> int :
71- extracted = _extract_frames (frames )
72- key = tuple (extracted )
65+ return [], [], [], [], [], 0
66+
67+ frame_to_idx : dict [str , int ] = {}
68+ frames : list [str ] = []
69+ stack_to_idx : dict [tuple [int , ...], int ] = {}
70+ stacks : list [list [int ]] = []
71+
72+ def intern_frame (f : str ) -> int :
73+ if f not in frame_to_idx :
74+ frame_to_idx [f ] = len (frames )
75+ frames .append (f )
76+ return frame_to_idx [f ]
77+
78+ def get_stack_idx (raw_frames : list [dict ]) -> int :
79+ extracted = _extract_frames (raw_frames )
80+ frame_indices = [intern_frame (f ) for f in extracted ]
81+ key = tuple (frame_indices )
7382 if key not in stack_to_idx :
7483 stack_to_idx [key ] = len (stacks )
75- stacks .append (extracted )
84+ stacks .append (frame_indices )
7685 return stack_to_idx [key ]
7786
7887 allocated = 0
@@ -159,16 +168,18 @@ def get_stack_idx(frames: list[dict]) -> int:
159168 poly ["ts" ].append (timestep )
160169 poly ["offsets" ].append (poly ["offsets" ][- 1 ])
161170
162- categories = [_categorize_stack (stack ) for stack in stacks ]
163- return timeline , alloc_polys , stacks , categories , timestep
171+ categories = [_categorize_stack ([ frames [ fi ] for fi in stack ] ) for stack in stacks ]
172+ return timeline , alloc_polys , frames , stacks , categories , timestep
164173
165174
166175def generate_memory_html (
167176 snapshot : dict ,
168177 device : int = 0 ,
169178 title : str = "Memory Timeline" ,
170179) -> str :
171- timeline , alloc_polys , stacks , categories , max_ts = process_snapshot (snapshot , device )
180+ timeline , alloc_polys , frames , stacks , categories , max_ts = process_snapshot (
181+ snapshot , device
182+ )
172183 hwm = max ((p ["h" ] for p in timeline ), default = 0 )
173184 hwm_timestep = next ((i for i , p in enumerate (timeline ) if p ["a" ] == hwm ), 0 )
174185
@@ -193,6 +204,7 @@ def generate_memory_html(
193204 _MEMORY_VIZ_TEMPLATE .replace ("__TITLE__" , title )
194205 .replace ("__TIMELINE__" , json .dumps (timeline ))
195206 .replace ("__ALLOCS__" , json .dumps (alloc_polys ))
207+ .replace ("__FRAMES__" , json .dumps (frames ))
196208 .replace ("__STACKS__" , json .dumps (stacks ))
197209 .replace ("__CATEGORIES__" , json .dumps (cat_indices ))
198210 .replace ("__META__" , json .dumps (meta ))
@@ -683,10 +695,16 @@ def generate_memory_html(
683695<script>
684696const TIMELINE = __TIMELINE__;
685697const ALLOCS = __ALLOCS__;
698+ const FRAMES = __FRAMES__;
686699const STACKS = __STACKS__;
687700const CATEGORIES = __CATEGORIES__;
688701const META = __META__;
689702
703+ function resolveStack(stackIdx) {
704+ const indices = STACKS[stackIdx] || [];
705+ return indices.map(i => FRAMES[i]);
706+ }
707+
690708function formatBytes(b) {
691709 if (Math.abs(b) >= 1024**3) return (b / 1024**3).toFixed(2) + ' GiB';
692710 if (Math.abs(b) >= 1024**2) return (b / 1024**2).toFixed(1) + ' MiB';
@@ -743,7 +761,7 @@ def generate_memory_html(
743761}
744762
745763function bestFrame(stackIdx) {
746- const stack = STACKS[ stackIdx] || [] ;
764+ const stack = resolveStack( stackIdx) ;
747765 for (const f of stack) {
748766 if (classifyFrame(f) === 'user') return f;
749767 }
@@ -782,7 +800,7 @@ def generate_memory_html(
782800function renderStack(stackIdx, label) {
783801 lastStackIdx = stackIdx;
784802 lastStackLabel = label;
785- const stack = STACKS[ stackIdx] || [] ;
803+ const stack = resolveStack( stackIdx) ;
786804 detailStats.textContent = label;
787805 if (!stack.length) {
788806 detailBody.innerHTML = '<div class="empty-detail">No frames recorded</div>';
@@ -1025,7 +1043,7 @@ def generate_memory_html(
10251043 el.classed('dimmed', false).classed('highlighted', false);
10261044 return;
10271045 }
1028- const stack = STACKS[ d.si] || [];
1046+ const stack = resolveStack( d.si) || [];
10291047 const match = stack.some(f => matcher.test(f));
10301048 el.classed('dimmed', !match).classed('highlighted', match);
10311049 });
0 commit comments