@@ -64,6 +64,9 @@ function resolveStringIndices(node) {
6464 if ( typeof resolved . funcname === 'number' ) {
6565 resolved . funcname = resolveString ( resolved . funcname ) ;
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) {
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+
8189// ============================================================================
8290// Theme & UI Controls
8391// ============================================================================
@@ -201,6 +209,7 @@ function setupLogos() {
201209function updateStatusBar ( nodeData , rootValue ) {
202210 const funcname = resolveString ( nodeData . funcname ) || resolveString ( nodeData . name ) || "--" ;
203211 const filename = resolveString ( nodeData . filename ) || "" ;
212+ const moduleName = resolveString ( nodeData . module_name ) || "" ;
204213 const lineno = nodeData . lineno ;
205214 const timeMs = ( nodeData . value / 1000 ) . toFixed ( 2 ) ;
206215 const percent = rootValue > 0 ? ( ( nodeData . value / rootValue ) * 100 ) . toFixed ( 1 ) : "0.0" ;
@@ -222,8 +231,7 @@ function updateStatusBar(nodeData, rootValue) {
222231
223232 const fileEl = document . getElementById ( 'status-file' ) ;
224233 if ( fileEl && filename && filename !== "~" ) {
225- const basename = filename . split ( '/' ) . pop ( ) ;
226- fileEl . textContent = lineno ? `${ basename } :${ lineno } ` : basename ;
234+ fileEl . textContent = lineno ? `${ moduleName } :${ lineno } ` : moduleName ;
227235 }
228236
229237 const funcEl = document . getElementById ( 'status-func' ) ;
@@ -272,6 +280,7 @@ function createPythonTooltip(data) {
272280
273281 const funcname = resolveString ( d . data . funcname ) || resolveString ( d . data . name ) ;
274282 const filename = resolveString ( d . data . filename ) || "" ;
283+ const moduleName = escapeHtml ( resolveString ( d . data . module_name ) || "" ) ;
275284 const isSpecialFrame = filename === "~" ;
276285
277286 // Build source section
@@ -280,7 +289,7 @@ function createPythonTooltip(data) {
280289 const sourceLines = source
281290 . map ( ( line ) => {
282291 const isCurrent = line . startsWith ( "→" ) ;
283- const escaped = line . replace ( / & / g , "&" ) . replace ( / < / g , "<" ) . replace ( / > / g , ">" ) ;
292+ const escaped = escapeHtml ( line ) ;
284293 return `<div class="tooltip-source-line${ isCurrent ? ' current' : '' } ">${ escaped } </div>` ;
285294 } )
286295 . join ( "" ) ;
@@ -340,7 +349,7 @@ function createPythonTooltip(data) {
340349 }
341350
342351 const fileLocationHTML = isSpecialFrame ? "" : `
343- <div class="tooltip-location">${ filename } ${ d . data . lineno ? ":" + d . data . lineno : "" } </div>` ;
352+ <div class="tooltip-location">${ moduleName } ${ d . data . lineno ? ":" + d . data . lineno : "" } </div>` ;
344353
345354 const tooltipHTML = `
346355 <div class="tooltip-header">
@@ -509,24 +518,24 @@ function updateSearchHighlight(searchTerm, searchInput) {
509518 const name = resolveString ( d . data . name ) || "" ;
510519 const funcname = resolveString ( d . data . funcname ) || "" ;
511520 const filename = resolveString ( d . data . filename ) || "" ;
521+ const moduleName = resolveString ( d . data . module_name ) || "" ;
512522 const lineno = d . data . lineno ;
513523 const term = searchTerm . toLowerCase ( ) ;
514524
515- // Check if search term looks like file :line pattern
525+ // Check if search term looks like module :line pattern
516526 const fileLineMatch = term . match ( / ^ ( .+ ) : ( \d + ) $ / ) ;
517527 let matches = false ;
518528
519529 if ( fileLineMatch ) {
520- // Exact file:line matching
521530 const searchFile = fileLineMatch [ 1 ] ;
522531 const searchLine = parseInt ( fileLineMatch [ 2 ] , 10 ) ;
523- const basename = filename . split ( '/' ) . pop ( ) . toLowerCase ( ) ;
524- matches = basename . includes ( searchFile ) && lineno === searchLine ;
532+ matches = moduleName . toLowerCase ( ) . includes ( searchFile ) && lineno === searchLine ;
525533 } else {
526534 // Regular substring search
527535 matches =
528536 name . toLowerCase ( ) . includes ( term ) ||
529537 funcname . toLowerCase ( ) . includes ( term ) ||
538+ moduleName . toLowerCase ( ) . includes ( term ) ||
530539 filename . toLowerCase ( ) . includes ( term ) ;
531540 }
532541
@@ -894,6 +903,7 @@ function populateStats(data) {
894903
895904 let filename = resolveString ( node . filename ) ;
896905 let funcname = resolveString ( node . funcname ) ;
906+ let moduleName = resolveString ( node . module_name ) ;
897907
898908 if ( ! filename || ! funcname ) {
899909 const nameStr = resolveString ( node . name ) ;
@@ -908,6 +918,7 @@ function populateStats(data) {
908918
909919 filename = filename || 'unknown' ;
910920 funcname = funcname || 'unknown' ;
921+ moduleName = moduleName || 'unknown' ;
911922
912923 if ( filename !== 'unknown' && funcname !== 'unknown' && node . value > 0 ) {
913924 let childrenValue = 0 ;
@@ -924,12 +935,14 @@ function populateStats(data) {
924935 existing . directPercent = ( existing . directSamples / totalSamples ) * 100 ;
925936 if ( directSamples > existing . maxSingleSamples ) {
926937 existing . filename = filename ;
938+ existing . module_name = moduleName ;
927939 existing . lineno = node . lineno || '?' ;
928940 existing . maxSingleSamples = directSamples ;
929941 }
930942 } else {
931943 functionMap . set ( funcKey , {
932944 filename : filename ,
945+ module_name : moduleName ,
933946 lineno : node . lineno || '?' ,
934947 funcname : funcname ,
935948 directSamples,
@@ -964,6 +977,7 @@ function populateStats(data) {
964977 const h = hotSpots [ i ] ;
965978 const filename = h . filename || 'unknown' ;
966979 const lineno = h . lineno ?? '?' ;
980+ const moduleName = h . module_name || 'unknown' ;
967981 const isSpecialFrame = filename === '~' && ( lineno === 0 || lineno === '?' ) ;
968982
969983 let funcDisplay = h . funcname || 'unknown' ;
@@ -974,8 +988,7 @@ function populateStats(data) {
974988 if ( isSpecialFrame ) {
975989 fileEl . textContent = '--' ;
976990 } else {
977- const basename = filename !== 'unknown' ? filename . split ( '/' ) . pop ( ) : 'unknown' ;
978- fileEl . textContent = `${ basename } :${ lineno } ` ;
991+ fileEl . textContent = `${ moduleName } :${ lineno } ` ;
979992 }
980993 }
981994 if ( percentEl ) percentEl . textContent = `${ h . directPercent . toFixed ( 1 ) } %` ;
@@ -991,8 +1004,9 @@ function populateStats(data) {
9911004 if ( card ) {
9921005 if ( i < hotSpots . length && hotSpots [ i ] ) {
9931006 const h = hotSpots [ i ] ;
994- const basename = h . filename !== 'unknown' ? h . filename . split ( '/' ) . pop ( ) : '' ;
995- const searchTerm = basename && h . lineno !== '?' ? `${ basename } :${ h . lineno } ` : h . funcname ;
1007+ const moduleName = h . module_name || 'unknown' ;
1008+ const hasValidLocation = moduleName !== 'unknown' && h . lineno !== '?' ;
1009+ const searchTerm = hasValidLocation ? `${ moduleName } :${ h . lineno } ` : h . funcname ;
9961010 card . dataset . searchterm = searchTerm ;
9971011 card . onclick = ( ) => searchForHotspot ( searchTerm ) ;
9981012 card . style . cursor = 'pointer' ;
@@ -1147,6 +1161,7 @@ function accumulateInvertedNode(parent, stackFrame, leaf) {
11471161 value : 0 ,
11481162 children : { } ,
11491163 filename : stackFrame . filename ,
1164+ module_name : stackFrame . module_name ,
11501165 lineno : stackFrame . lineno ,
11511166 funcname : stackFrame . funcname ,
11521167 source : stackFrame . source ,
0 commit comments