11const EMBEDDED_DATA = { { FLAMEGRAPH_DATA } } ;
22
3+ // Global string table for resolving string indices
4+ let stringTable = [ ] ;
5+
6+ // Function to resolve string indices to actual strings
7+ function resolveString ( index ) {
8+ if ( typeof index === 'number' && index >= 0 && index < stringTable . length ) {
9+ return stringTable [ index ] ;
10+ }
11+ // Fallback for non-indexed strings or invalid indices
12+ return String ( index ) ;
13+ }
14+
15+ // Function to recursively resolve all string indices in flamegraph data
16+ function resolveStringIndices ( node ) {
17+ if ( ! node ) return node ;
18+
19+ // Create a copy to avoid mutating the original
20+ const resolved = { ...node } ;
21+
22+ // Resolve string fields
23+ if ( typeof resolved . name === 'number' ) {
24+ resolved . name = resolveString ( resolved . name ) ;
25+ }
26+ if ( typeof resolved . filename === 'number' ) {
27+ resolved . filename = resolveString ( resolved . filename ) ;
28+ }
29+ if ( typeof resolved . funcname === 'number' ) {
30+ resolved . funcname = resolveString ( resolved . funcname ) ;
31+ }
32+
33+ // Resolve source lines if present
34+ if ( Array . isArray ( resolved . source ) ) {
35+ resolved . source = resolved . source . map ( index =>
36+ typeof index === 'number' ? resolveString ( index ) : index
37+ ) ;
38+ }
39+
40+ // Recursively resolve children
41+ if ( Array . isArray ( resolved . children ) ) {
42+ resolved . children = resolved . children . map ( child => resolveStringIndices ( child ) ) ;
43+ }
44+
45+ return resolved ;
46+ }
47+
348// Python color palette - cold to hot
449const pythonColors = [
550 "#fff4bf" , // Coldest - light yellow (<1%)
@@ -100,6 +145,10 @@ function createPythonTooltip(data) {
100145 </div>` ;
101146 }
102147
148+ // Resolve strings for display
149+ const funcname = resolveString ( d . data . funcname ) || resolveString ( d . data . name ) ;
150+ const filename = resolveString ( d . data . filename ) || "" ;
151+
103152 const tooltipHTML = `
104153 <div>
105154 <div style="color: #3776ab; font-weight: 600; font-size: 16px;
@@ -257,9 +306,9 @@ function updateSearchHighlight(searchTerm, searchInput) {
257306 let matchCount = 0 ;
258307 d3 . selectAll ( "#chart rect" ) . each ( function ( d ) {
259308 if ( d && d . data ) {
260- const name = d . data . name || "" ;
261- const funcname = d . data . funcname || "" ;
262- const filename = d . data . filename || "" ;
309+ const name = resolveString ( d . data . name ) || "" ;
310+ const funcname = resolveString ( d . data . funcname ) || "" ;
311+ const filename = resolveString ( d . data . filename ) || "" ;
263312 const term = searchTerm . toLowerCase ( ) ;
264313 const matches =
265314 name . toLowerCase ( ) . includes ( term ) ||
@@ -317,12 +366,20 @@ function handleResize(chart, data) {
317366
318367function initFlamegraph ( ) {
319368 ensureLibraryLoaded ( ) ;
320- const tooltip = createPythonTooltip ( EMBEDDED_DATA ) ;
321- const chart = createFlamegraph ( tooltip , EMBEDDED_DATA . value ) ;
322- renderFlamegraph ( chart , EMBEDDED_DATA ) ;
369+
370+ // Extract string table if present and resolve string indices
371+ let processedData = EMBEDDED_DATA ;
372+ if ( EMBEDDED_DATA . strings ) {
373+ stringTable = EMBEDDED_DATA . strings ;
374+ processedData = resolveStringIndices ( EMBEDDED_DATA ) ;
375+ }
376+
377+ const tooltip = createPythonTooltip ( processedData ) ;
378+ const chart = createFlamegraph ( tooltip , processedData . value ) ;
379+ renderFlamegraph ( chart , processedData ) ;
323380 attachPanelControls ( ) ;
324381 initSearchHandlers ( ) ;
325- handleResize ( chart , EMBEDDED_DATA ) ;
382+ handleResize ( chart , processedData ) ;
326383}
327384
328385if ( document . readyState === "loading" ) {
@@ -338,7 +395,10 @@ function populateStats(data) {
338395 const functionMap = new Map ( ) ;
339396
340397 function collectFunctions ( node ) {
341- if ( node . filename && node . funcname ) {
398+ const filename = resolveString ( node . filename ) ;
399+ const funcname = resolveString ( node . funcname ) ;
400+
401+ if ( filename && funcname ) {
342402 // Calculate direct samples (this node's value minus children's values)
343403 let childrenValue = 0 ;
344404 if ( node . children ) {
@@ -347,23 +407,23 @@ function populateStats(data) {
347407 const directSamples = Math . max ( 0 , node . value - childrenValue ) ;
348408
349409 // Use file:line:funcname as key to ensure uniqueness
350- const funcKey = `${ node . filename } :${ node . lineno || '?' } :${ node . funcname } ` ;
410+ const funcKey = `${ filename } :${ node . lineno || '?' } :${ funcname } ` ;
351411
352412 if ( functionMap . has ( funcKey ) ) {
353413 const existing = functionMap . get ( funcKey ) ;
354414 existing . directSamples += directSamples ;
355415 existing . directPercent = ( existing . directSamples / totalSamples ) * 100 ;
356416 // Keep the most representative file/line (the one with more samples)
357417 if ( directSamples > existing . maxSingleSamples ) {
358- existing . filename = node . filename ;
418+ existing . filename = filename ;
359419 existing . lineno = node . lineno || '?' ;
360420 existing . maxSingleSamples = directSamples ;
361421 }
362422 } else {
363423 functionMap . set ( funcKey , {
364- filename : node . filename ,
424+ filename : filename ,
365425 lineno : node . lineno || '?' ,
366- funcname : node . funcname ,
426+ funcname : funcname ,
367427 directSamples,
368428 directPercent : ( directSamples / totalSamples ) * 100 ,
369429 maxSingleSamples : directSamples
0 commit comments