@@ -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' ) ;
@@ -301,6 +309,7 @@ function createPythonTooltip(data) {
301309
302310 const funcname = resolveString ( d . data . funcname ) || resolveString ( d . data . name ) ;
303311 const filename = resolveString ( d . data . filename ) || "" ;
312+ const moduleName = escapeHtml ( resolveString ( d . data . module_name ) || "" ) ;
304313 const isSpecialFrame = filename === "~" ;
305314
306315 // Build source section
@@ -309,7 +318,7 @@ function createPythonTooltip(data) {
309318 const sourceLines = source
310319 . map ( ( line ) => {
311320 const isCurrent = line . startsWith ( "→" ) ;
312- const escaped = line . replace ( / & / g , "&" ) . replace ( / < / g , "<" ) . replace ( / > / g , ">" ) ;
321+ const escaped = escapeHtml ( line ) ;
313322 return `<div class="tooltip-source-line${ isCurrent ? ' current' : '' } ">${ escaped } </div>` ;
314323 } )
315324 . join ( "" ) ;
@@ -369,7 +378,7 @@ function createPythonTooltip(data) {
369378 }
370379
371380 const fileLocationHTML = isSpecialFrame ? "" : `
372- <div class="tooltip-location">${ filename } ${ d . data . lineno ? ":" + d . data . lineno : "" } </div>` ;
381+ <div class="tooltip-location">${ moduleName } ${ d . data . lineno ? ":" + d . data . lineno : "" } </div>` ;
373382
374383 // Differential stats section
375384 let diffSection = "" ;
@@ -628,24 +637,24 @@ function updateSearchHighlight(searchTerm, searchInput) {
628637 const name = resolveString ( d . data . name ) || "" ;
629638 const funcname = resolveString ( d . data . funcname ) || "" ;
630639 const filename = resolveString ( d . data . filename ) || "" ;
640+ const moduleName = resolveString ( d . data . module_name ) || "" ;
631641 const lineno = d . data . lineno ;
632642 const term = searchTerm . toLowerCase ( ) ;
633643
634- // Check if search term looks like file :line pattern
644+ // Check if search term looks like module :line pattern
635645 const fileLineMatch = term . match ( / ^ ( .+ ) : ( \d + ) $ / ) ;
636646 let matches = false ;
637647
638648 if ( fileLineMatch ) {
639- // Exact file:line matching
640649 const searchFile = fileLineMatch [ 1 ] ;
641650 const searchLine = parseInt ( fileLineMatch [ 2 ] , 10 ) ;
642- const basename = filename . split ( '/' ) . pop ( ) . toLowerCase ( ) ;
643- matches = basename . includes ( searchFile ) && lineno === searchLine ;
651+ matches = moduleName . toLowerCase ( ) . includes ( searchFile ) && lineno === searchLine ;
644652 } else {
645653 // Regular substring search
646654 matches =
647655 name . toLowerCase ( ) . includes ( term ) ||
648656 funcname . toLowerCase ( ) . includes ( term ) ||
657+ moduleName . toLowerCase ( ) . includes ( term ) ||
649658 filename . toLowerCase ( ) . includes ( term ) ;
650659 }
651660
@@ -1047,6 +1056,7 @@ function populateStats(data) {
10471056
10481057 let filename = resolveString ( node . filename ) ;
10491058 let funcname = resolveString ( node . funcname ) ;
1059+ let moduleName = resolveString ( node . module_name ) ;
10501060
10511061 if ( ! filename || ! funcname ) {
10521062 const nameStr = resolveString ( node . name ) ;
@@ -1061,6 +1071,7 @@ function populateStats(data) {
10611071
10621072 filename = filename || 'unknown' ;
10631073 funcname = funcname || 'unknown' ;
1074+ moduleName = moduleName || 'unknown' ;
10641075
10651076 if ( filename !== 'unknown' && funcname !== 'unknown' && node . value > 0 ) {
10661077 let childrenValue = 0 ;
@@ -1077,12 +1088,14 @@ function populateStats(data) {
10771088 existing . directPercent = ( existing . directSamples / totalSamples ) * 100 ;
10781089 if ( directSamples > existing . maxSingleSamples ) {
10791090 existing . filename = filename ;
1091+ existing . module_name = moduleName ;
10801092 existing . lineno = node . lineno || '?' ;
10811093 existing . maxSingleSamples = directSamples ;
10821094 }
10831095 } else {
10841096 functionMap . set ( funcKey , {
10851097 filename : filename ,
1098+ module_name : moduleName ,
10861099 lineno : node . lineno || '?' ,
10871100 funcname : funcname ,
10881101 directSamples,
@@ -1117,6 +1130,7 @@ function populateStats(data) {
11171130 const h = hotSpots [ i ] ;
11181131 const filename = h . filename || 'unknown' ;
11191132 const lineno = h . lineno ?? '?' ;
1133+ const moduleName = h . module_name || 'unknown' ;
11201134 const isSpecialFrame = filename === '~' && ( lineno === 0 || lineno === '?' ) ;
11211135
11221136 let funcDisplay = h . funcname || 'unknown' ;
@@ -1127,8 +1141,7 @@ function populateStats(data) {
11271141 if ( isSpecialFrame ) {
11281142 fileEl . textContent = '--' ;
11291143 } else {
1130- const basename = filename !== 'unknown' ? filename . split ( '/' ) . pop ( ) : 'unknown' ;
1131- fileEl . textContent = `${ basename } :${ lineno } ` ;
1144+ fileEl . textContent = `${ moduleName } :${ lineno } ` ;
11321145 }
11331146 }
11341147 if ( percentEl ) percentEl . textContent = `${ h . directPercent . toFixed ( 1 ) } %` ;
@@ -1144,8 +1157,9 @@ function populateStats(data) {
11441157 if ( card ) {
11451158 if ( i < hotSpots . length && hotSpots [ i ] ) {
11461159 const h = hotSpots [ i ] ;
1147- const basename = h . filename !== 'unknown' ? h . filename . split ( '/' ) . pop ( ) : '' ;
1148- const searchTerm = basename && h . lineno !== '?' ? `${ basename } :${ h . lineno } ` : h . funcname ;
1160+ const moduleName = h . module_name || 'unknown' ;
1161+ const hasValidLocation = moduleName !== 'unknown' && h . lineno !== '?' ;
1162+ const searchTerm = hasValidLocation ? `${ moduleName } :${ h . lineno } ` : h . funcname ;
11491163 card . dataset . searchterm = searchTerm ;
11501164 card . onclick = ( ) => searchForHotspot ( searchTerm ) ;
11511165 card . style . cursor = 'pointer' ;
@@ -1281,6 +1295,7 @@ function accumulateInvertedNode(parent, stackFrame, leaf, isDifferential) {
12811295 self : 0 ,
12821296 children : { } ,
12831297 filename : stackFrame . filename ,
1298+ module_name : stackFrame . module_name ,
12841299 lineno : stackFrame . lineno ,
12851300 funcname : stackFrame . funcname ,
12861301 source : stackFrame . source ,
0 commit comments