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%)
@@ -42,6 +87,8 @@ function createPythonTooltip(data) {
4287 . style ( "font-weight" , "400" )
4388 . style ( "line-height" , "1.5" )
4489 . style ( "max-width" , "500px" )
90+ . style ( "word-wrap" , "break-word" )
91+ . style ( "overflow-wrap" , "break-word" )
4592 . style ( "font-family" , "'Source Sans Pro', sans-serif" )
4693 . style ( "opacity" , 0 ) ;
4794 }
@@ -61,7 +108,7 @@ function createPythonTooltip(data) {
61108 `<div style="font-family: 'SF Mono', 'Monaco', 'Consolas', ` +
62109 `monospace; font-size: 12px; color: ${
63110 line . startsWith ( "→" ) ? "#3776ab" : "#5a6c7d"
64- } ; white-space: pre; line-height: 1.4; padding: 2px 0;">${ line
111+ } ; white-space: pre-wrap; word-break: break-all; overflow-wrap: break-word ; line-height: 1.4; padding: 2px 0;">${ line
65112 . replace ( / & / g, "&" )
66113 . replace ( / < / g, "<" )
67114 . replace ( / > / g, ">" ) } </div>`,
@@ -77,7 +124,7 @@ function createPythonTooltip(data) {
77124 </div>
78125 <div style="background: #f8f9fa; border: 1px solid #e9ecef;
79126 border-radius: 6px; padding: 12px; max-height: 150px;
80- overflow-y: auto;">
127+ overflow-y: auto; overflow-x: hidden; ">
81128 ${ sourceLines }
82129 </div>
83130 </div>` ;
@@ -92,22 +139,26 @@ function createPythonTooltip(data) {
92139 </div>
93140 <div style="background: #f8f9fa; border: 1px solid #e9ecef;
94141 border-radius: 6px; padding: 12px; max-height: 150px;
95- overflow-y: auto; font-family: monospace; font-size: 11px;">
142+ overflow-y: auto; overflow-x: hidden; font-family: monospace; font-size: 11px; word-break: break-all; overflow-wrap: break-word ;">
96143 ${ JSON . stringify ( source , null , 2 ) }
97144 </div>
98145 </div>` ;
99146 }
100147
148+ // Resolve strings for display
149+ const funcname = resolveString ( d . data . funcname ) || resolveString ( d . data . name ) ;
150+ const filename = resolveString ( d . data . filename ) || "" ;
151+
101152 const tooltipHTML = `
102153 <div>
103154 <div style="color: #3776ab; font-weight: 600; font-size: 16px;
104- margin-bottom: 8px; line-height: 1.3;">
105- ${ d . data . funcname || d . data . name }
155+ margin-bottom: 8px; line-height: 1.3; word-break: break-word; overflow-wrap: break-word; ">
156+ ${ funcname }
106157 </div>
107158 <div style="color: #5a6c7d; font-size: 13px; margin-bottom: 12px;
108159 font-family: monospace; background: #f8f9fa;
109- padding: 4px 8px; border-radius: 4px;">
110- ${ d . data . filename || "" } ${ d . data . lineno ? ":" + d . data . lineno : "" }
160+ padding: 4px 8px; border-radius: 4px; word-break: break-all; overflow-wrap: break-word; ">
161+ ${ filename } ${ d . data . lineno ? ":" + d . data . lineno : "" }
111162 </div>
112163 <div style="display: grid; grid-template-columns: auto 1fr;
113164 gap: 8px 16px; font-size: 14px;">
@@ -255,9 +306,9 @@ function updateSearchHighlight(searchTerm, searchInput) {
255306 let matchCount = 0 ;
256307 d3 . selectAll ( "#chart rect" ) . each ( function ( d ) {
257308 if ( d && d . data ) {
258- const name = d . data . name || "" ;
259- const funcname = d . data . funcname || "" ;
260- 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 ) || "" ;
261312 const term = searchTerm . toLowerCase ( ) ;
262313 const matches =
263314 name . toLowerCase ( ) . includes ( term ) ||
@@ -315,12 +366,20 @@ function handleResize(chart, data) {
315366
316367function initFlamegraph ( ) {
317368 ensureLibraryLoaded ( ) ;
318- const tooltip = createPythonTooltip ( EMBEDDED_DATA ) ;
319- const chart = createFlamegraph ( tooltip , EMBEDDED_DATA . value ) ;
320- 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 ) ;
321380 attachPanelControls ( ) ;
322381 initSearchHandlers ( ) ;
323- handleResize ( chart , EMBEDDED_DATA ) ;
382+ handleResize ( chart , processedData ) ;
324383}
325384
326385if ( document . readyState === "loading" ) {
@@ -336,7 +395,10 @@ function populateStats(data) {
336395 const functionMap = new Map ( ) ;
337396
338397 function collectFunctions ( node ) {
339- if ( node . filename && node . funcname ) {
398+ const filename = resolveString ( node . filename ) ;
399+ const funcname = resolveString ( node . funcname ) ;
400+
401+ if ( filename && funcname ) {
340402 // Calculate direct samples (this node's value minus children's values)
341403 let childrenValue = 0 ;
342404 if ( node . children ) {
@@ -345,23 +407,23 @@ function populateStats(data) {
345407 const directSamples = Math . max ( 0 , node . value - childrenValue ) ;
346408
347409 // Use file:line:funcname as key to ensure uniqueness
348- const funcKey = `${ node . filename } :${ node . lineno || '?' } :${ node . funcname } ` ;
410+ const funcKey = `${ filename } :${ node . lineno || '?' } :${ funcname } ` ;
349411
350412 if ( functionMap . has ( funcKey ) ) {
351413 const existing = functionMap . get ( funcKey ) ;
352414 existing . directSamples += directSamples ;
353415 existing . directPercent = ( existing . directSamples / totalSamples ) * 100 ;
354416 // Keep the most representative file/line (the one with more samples)
355417 if ( directSamples > existing . maxSingleSamples ) {
356- existing . filename = node . filename ;
418+ existing . filename = filename ;
357419 existing . lineno = node . lineno || '?' ;
358420 existing . maxSingleSamples = directSamples ;
359421 }
360422 } else {
361423 functionMap . set ( funcKey , {
362- filename : node . filename ,
424+ filename : filename ,
363425 lineno : node . lineno || '?' ,
364- funcname : node . funcname ,
426+ funcname : funcname ,
365427 directSamples,
366428 directPercent : ( directSamples / totalSamples ) * 100 ,
367429 maxSingleSamples : directSamples
0 commit comments