@@ -64,6 +64,9 @@ function resolveStringIndices(node, table) {
6464 if ( typeof resolved . funcname === 'number' ) {
6565 resolved . funcname = resolveString ( resolved . funcname , table ) ;
6666 }
67+ if ( typeof resolved . module_name === 'number' ) {
68+ resolved . module_name = resolveString ( resolved . module_name ) ;
69+ }
6770
6871 if ( Array . isArray ( resolved . source ) ) {
6972 resolved . source = resolved . source . map ( index =>
@@ -78,6 +81,11 @@ function resolveStringIndices(node, table) {
7881 return resolved ;
7982}
8083
84+ // Escape HTML special characters
85+ function escapeHtml ( str ) {
86+ return str . replace ( / & / g, "&" ) . replace ( / < / g, "<" ) . replace ( / > / g, ">" ) ;
87+ }
88+
8189function selectFlamegraphData ( ) {
8290 const baseData = isShowingElided ? elidedFlamegraphData : normalData ;
8391
@@ -228,6 +236,7 @@ function setupLogos() {
228236function updateStatusBar ( nodeData , rootValue ) {
229237 const funcname = resolveString ( nodeData . funcname ) || resolveString ( nodeData . name ) || "--" ;
230238 const filename = resolveString ( nodeData . filename ) || "" ;
239+ const moduleName = resolveString ( nodeData . module_name ) || "" ;
231240 const lineno = nodeData . lineno ;
232241 const timeMs = ( nodeData . value / 1000 ) . toFixed ( 2 ) ;
233242 const percent = rootValue > 0 ? ( ( nodeData . value / rootValue ) * 100 ) . toFixed ( 1 ) : "0.0" ;
@@ -249,8 +258,7 @@ function updateStatusBar(nodeData, rootValue) {
249258
250259 const fileEl = document . getElementById ( 'status-file' ) ;
251260 if ( fileEl && filename && filename !== "~" ) {
252- const basename = filename . split ( '/' ) . pop ( ) ;
253- fileEl . textContent = lineno ? `${ basename } :${ lineno } ` : basename ;
261+ fileEl . textContent = lineno ? `${ moduleName } :${ lineno } ` : moduleName ;
254262 }
255263
256264 const funcEl = document . getElementById ( 'status-func' ) ;
@@ -299,6 +307,7 @@ function createPythonTooltip(data) {
299307
300308 const funcname = resolveString ( d . data . funcname ) || resolveString ( d . data . name ) ;
301309 const filename = resolveString ( d . data . filename ) || "" ;
310+ const moduleName = escapeHtml ( resolveString ( d . data . module_name ) || "" ) ;
302311 const isSpecialFrame = filename === "~" ;
303312
304313 // Build source section
@@ -307,7 +316,7 @@ function createPythonTooltip(data) {
307316 const sourceLines = source
308317 . map ( ( line ) => {
309318 const isCurrent = line . startsWith ( "→" ) ;
310- const escaped = line . replace ( / & / g , "&" ) . replace ( / < / g , "<" ) . replace ( / > / g , ">" ) ;
319+ const escaped = escapeHtml ( line ) ;
311320 return `<div class="tooltip-source-line${ isCurrent ? ' current' : '' } ">${ escaped } </div>` ;
312321 } )
313322 . join ( "" ) ;
@@ -367,7 +376,7 @@ function createPythonTooltip(data) {
367376 }
368377
369378 const fileLocationHTML = isSpecialFrame ? "" : `
370- <div class="tooltip-location">${ filename } ${ d . data . lineno ? ":" + d . data . lineno : "" } </div>` ;
379+ <div class="tooltip-location">${ moduleName } ${ d . data . lineno ? ":" + d . data . lineno : "" } </div>` ;
371380
372381 // Differential stats section
373382 let diffSection = "" ;
@@ -621,24 +630,24 @@ function updateSearchHighlight(searchTerm, searchInput) {
621630 const name = resolveString ( d . data . name ) || "" ;
622631 const funcname = resolveString ( d . data . funcname ) || "" ;
623632 const filename = resolveString ( d . data . filename ) || "" ;
633+ const moduleName = resolveString ( d . data . module_name ) || "" ;
624634 const lineno = d . data . lineno ;
625635 const term = searchTerm . toLowerCase ( ) ;
626636
627- // Check if search term looks like file :line pattern
637+ // Check if search term looks like module :line pattern
628638 const fileLineMatch = term . match ( / ^ ( .+ ) : ( \d + ) $ / ) ;
629639 let matches = false ;
630640
631641 if ( fileLineMatch ) {
632- // Exact file:line matching
633642 const searchFile = fileLineMatch [ 1 ] ;
634643 const searchLine = parseInt ( fileLineMatch [ 2 ] , 10 ) ;
635- const basename = filename . split ( '/' ) . pop ( ) . toLowerCase ( ) ;
636- matches = basename . includes ( searchFile ) && lineno === searchLine ;
644+ matches = moduleName . toLowerCase ( ) . includes ( searchFile ) && lineno === searchLine ;
637645 } else {
638646 // Regular substring search
639647 matches =
640648 name . toLowerCase ( ) . includes ( term ) ||
641649 funcname . toLowerCase ( ) . includes ( term ) ||
650+ moduleName . toLowerCase ( ) . includes ( term ) ||
642651 filename . toLowerCase ( ) . includes ( term ) ;
643652 }
644653
@@ -1040,6 +1049,7 @@ function populateStats(data) {
10401049
10411050 let filename = resolveString ( node . filename ) ;
10421051 let funcname = resolveString ( node . funcname ) ;
1052+ let moduleName = resolveString ( node . module_name ) ;
10431053
10441054 if ( ! filename || ! funcname ) {
10451055 const nameStr = resolveString ( node . name ) ;
@@ -1054,6 +1064,7 @@ function populateStats(data) {
10541064
10551065 filename = filename || 'unknown' ;
10561066 funcname = funcname || 'unknown' ;
1067+ moduleName = moduleName || 'unknown' ;
10571068
10581069 if ( filename !== 'unknown' && funcname !== 'unknown' && node . value > 0 ) {
10591070 let childrenValue = 0 ;
@@ -1070,12 +1081,14 @@ function populateStats(data) {
10701081 existing . directPercent = ( existing . directSamples / totalSamples ) * 100 ;
10711082 if ( directSamples > existing . maxSingleSamples ) {
10721083 existing . filename = filename ;
1084+ existing . module_name = moduleName ;
10731085 existing . lineno = node . lineno || '?' ;
10741086 existing . maxSingleSamples = directSamples ;
10751087 }
10761088 } else {
10771089 functionMap . set ( funcKey , {
10781090 filename : filename ,
1091+ module_name : moduleName ,
10791092 lineno : node . lineno || '?' ,
10801093 funcname : funcname ,
10811094 directSamples,
@@ -1110,6 +1123,7 @@ function populateStats(data) {
11101123 const h = hotSpots [ i ] ;
11111124 const filename = h . filename || 'unknown' ;
11121125 const lineno = h . lineno ?? '?' ;
1126+ const moduleName = h . module_name || 'unknown' ;
11131127 const isSpecialFrame = filename === '~' && ( lineno === 0 || lineno === '?' ) ;
11141128
11151129 let funcDisplay = h . funcname || 'unknown' ;
@@ -1120,8 +1134,7 @@ function populateStats(data) {
11201134 if ( isSpecialFrame ) {
11211135 fileEl . textContent = '--' ;
11221136 } else {
1123- const basename = filename !== 'unknown' ? filename . split ( '/' ) . pop ( ) : 'unknown' ;
1124- fileEl . textContent = `${ basename } :${ lineno } ` ;
1137+ fileEl . textContent = `${ moduleName } :${ lineno } ` ;
11251138 }
11261139 }
11271140 if ( percentEl ) percentEl . textContent = `${ h . directPercent . toFixed ( 1 ) } %` ;
@@ -1137,8 +1150,9 @@ function populateStats(data) {
11371150 if ( card ) {
11381151 if ( i < hotSpots . length && hotSpots [ i ] ) {
11391152 const h = hotSpots [ i ] ;
1140- const basename = h . filename !== 'unknown' ? h . filename . split ( '/' ) . pop ( ) : '' ;
1141- const searchTerm = basename && h . lineno !== '?' ? `${ basename } :${ h . lineno } ` : h . funcname ;
1153+ const moduleName = h . module_name || 'unknown' ;
1154+ const hasValidLocation = moduleName !== 'unknown' && h . lineno !== '?' ;
1155+ const searchTerm = hasValidLocation ? `${ moduleName } :${ h . lineno } ` : h . funcname ;
11421156 card . dataset . searchterm = searchTerm ;
11431157 card . onclick = ( ) => searchForHotspot ( searchTerm ) ;
11441158 card . style . cursor = 'pointer' ;
@@ -1273,6 +1287,7 @@ function accumulateInvertedNode(parent, stackFrame, leaf, isDifferential) {
12731287 value : 0 ,
12741288 children : { } ,
12751289 filename : stackFrame . filename ,
1290+ module_name : stackFrame . module_name ,
12761291 lineno : stackFrame . lineno ,
12771292 funcname : stackFrame . funcname ,
12781293 source : stackFrame . source ,
0 commit comments