5858 // symCacheHits counts the number of cache hits in the symbols cache
5959 symCacheHits = expvar .NewInt ("symbolizer.cache.hits" )
6060
61+ // symCachedSymbols counts the number of cached symbol infos
62+ symCachedSymbols = expvar .NewInt ("symbolizer.cached.symbols" )
63+
6164 // symModulesCount counts the number of loaded module exports
6265 symModulesCount = expvar .NewInt ("symbolizer.modules.count" )
6366
@@ -101,6 +104,11 @@ type module struct {
101104 hasExports bool
102105}
103106
107+ type syminfo struct {
108+ module string
109+ symbol string
110+ }
111+
104112func (m * module ) keepalive () {
105113 m .accessed = time .Now ()
106114}
@@ -125,11 +133,10 @@ type Symbolizer struct {
125133 // return address and the symbol information
126134 // identifying the originated call. It is populated
127135 // by the Debug Help API function when the module
128- // doesn't exist in process state. Subsequent
129- // calls to the produceFrame method will inspect
130- // this cache whenever the module is not located
131- // in process state
132- symbols map [uint32 ]map [va.Address ]string
136+ // doesn't exist in process state, and in addition
137+ // it is populated by each export directory symbol
138+ // resolution
139+ symbols map [uint32 ]map [va.Address ]syminfo
133140
134141 r Resolver
135142 psnap ps.Snapshotter
@@ -150,7 +157,7 @@ func NewSymbolizer(r Resolver, psnap ps.Snapshotter, config *config.Config, enqu
150157 config : config ,
151158 procs : make (map [uint32 ]* process ),
152159 mods : make (map [va.Address ]* module ),
153- symbols : make (map [uint32 ]map [va.Address ]string ),
160+ symbols : make (map [uint32 ]map [va.Address ]syminfo ),
154161 cleaner : time .NewTicker (time .Second * 2 ),
155162 purger : time .NewTicker (time .Minute * 5 ),
156163 quit : make (chan struct {}, 1 ),
@@ -196,6 +203,10 @@ func (s *Symbolizer) ProcessEvent(e *kevent.Kevent) (bool, error) {
196203 pid := e .Kparams .MustGetPid ()
197204 s .mu .Lock ()
198205 defer s .mu .Unlock ()
206+ if _ , ok := s .symbols [pid ]; ! ok {
207+ return true , nil
208+ }
209+ symCachedSymbols .Add (- int64 (len (s .symbols [pid ])))
199210 delete (s .symbols , pid )
200211 proc , ok := s .procs [pid ]
201212 if ! ok {
@@ -304,6 +315,9 @@ func (s *Symbolizer) processCallstack(e *kevent.Kevent) error {
304315 return nil
305316 }
306317
318+ s .mu .Lock ()
319+ defer s .mu .Unlock ()
320+
307321 if e .IsCreateFile () && e .IsOpenDisposition () {
308322 // for high-volume events decorating
309323 // the frames with symbol information
@@ -313,8 +327,6 @@ func (s *Symbolizer) processCallstack(e *kevent.Kevent) error {
313327 s .pushFrames (addrs , e , true , false )
314328 return nil
315329 }
316- s .mu .Lock ()
317- defer s .mu .Unlock ()
318330
319331 if e .PS != nil {
320332 var (
@@ -438,7 +450,7 @@ func (s *Symbolizer) pushFrames(addrs []va.Address, e *kevent.Kevent, fast, look
438450// state. All symbols are resolved from the
439451// PE export directory entries. If either the
440452// symbol or module are not resolved, then we
441- // fallback to Debug API.
453+ // fall back to Debug API.
442454func (s * Symbolizer ) produceFrame (addr va.Address , e * kevent.Kevent , fast , lookupExport bool ) kevent.Frame {
443455 frame := kevent.Frame {PID : e .PID , Addr : addr }
444456 if addr .InSystemRange () {
@@ -448,6 +460,16 @@ func (s *Symbolizer) produceFrame(addr va.Address, e *kevent.Kevent, fast, looku
448460 }
449461 return frame
450462 }
463+
464+ // did we hit this address previously?
465+ if sym , ok := s .symbols [e .PID ]; ok {
466+ if symbol , ok := sym [addr ]; ok {
467+ symCacheHits .Add (1 )
468+ frame .Module , frame .Symbol = symbol .module , symbol .symbol
469+ return frame
470+ }
471+ }
472+
451473 if fast {
452474 if e .PS != nil {
453475 mod := e .PS .FindModuleByVa (addr )
@@ -511,22 +533,12 @@ func (s *Symbolizer) produceFrame(addr va.Address, e *kevent.Kevent, fast, looku
511533 m .keepalive ()
512534 }
513535 if frame .Module != "" && frame .Symbol != "" {
536+ // store resolved symbol information in cache
537+ s .cacheSymbol (e .PID , addr , & frame )
514538 return frame
515539 }
516540 }
517541
518- // did we hit this address previously?
519- if sym , ok := s .symbols [e .PID ]; ok && sym [addr ] != "" {
520- symCacheHits .Add (1 )
521- n := strings .Split (sym [addr ], "!" )
522- if len (n ) > 1 {
523- frame .Module , frame .Symbol = n [0 ], n [1 ]
524- }
525- }
526- if frame .Module != "" && frame .Symbol != "" {
527- return frame
528- }
529-
530542 debugHelpFallbacks .Add (1 )
531543
532544 // fallback to Debug Help API
@@ -560,16 +572,21 @@ func (s *Symbolizer) produceFrame(addr va.Address, e *kevent.Kevent, fast, looku
560572 }
561573
562574 // store resolved symbol information in cache
563- sym := frame .Module + "!" + frame .Symbol
564- if mod , ok := s .symbols [e .PID ]; ok {
565- if _ , ok := mod [addr ]; ! ok {
566- s.symbols [e.PID ][addr ] = sym
575+ s .cacheSymbol (e .PID , addr , & frame )
576+
577+ return frame
578+ }
579+
580+ func (s * Symbolizer ) cacheSymbol (pid uint32 , addr va.Address , frame * kevent.Frame ) {
581+ if sym , ok := s .symbols [pid ]; ok {
582+ if _ , ok := sym [addr ]; ! ok {
583+ symCachedSymbols .Add (1 )
584+ s.symbols [pid ][addr ] = syminfo {module : frame .Module , symbol : frame .Symbol }
567585 }
568586 } else {
569- s .symbols [e .PID ] = map [va.Address ]string {addr : sym }
587+ symCachedSymbols .Add (1 )
588+ s .symbols [pid ] = map [va.Address ]syminfo {addr : {module : frame .Module , symbol : frame .Symbol }}
570589 }
571-
572- return frame
573590}
574591
575592// resolveSymbolFromExportDirectory parses the module PE
@@ -599,11 +616,11 @@ func (s *Symbolizer) symbolizeAddress(pid uint32, addr va.Address, mod *pstypes.
599616 symbol , ok := s.symbols [pid ][addr ]
600617 if ! ok && mod != nil {
601618 // resolve symbol from the export directory
602- symbol = s .resolveSymbolFromExportDirectory (addr , mod )
619+ symbol . symbol = s .resolveSymbolFromExportDirectory (addr , mod )
603620 }
604621
605622 // try to get the symbol via Debug Help API
606- if symbol == "" {
623+ if symbol . symbol == "" {
607624 proc , ok := s .procs [pid ]
608625 if ! ok {
609626 handle , err := windows .OpenProcess (windows .SYNCHRONIZE | windows .PROCESS_QUERY_INFORMATION , false , pid )
@@ -622,23 +639,29 @@ func (s *Symbolizer) symbolizeAddress(pid uint32, addr va.Address, mod *pstypes.
622639 s .procs [pid ] = proc
623640
624641 // resolve address to symbol
625- symbol , _ = s .r .GetSymbolNameAndOffset (handle , addr )
642+ symbol .symbol , _ = s .r .GetSymbolNameAndOffset (handle , addr )
643+ symbol .module = s .r .GetModuleName (handle , addr )
626644 } else {
627- symbol , _ = s .r .GetSymbolNameAndOffset (proc .handle , addr )
645+ symbol .symbol , _ = s .r .GetSymbolNameAndOffset (proc .handle , addr )
646+ symbol .module = s .r .GetModuleName (proc .handle , addr )
628647 proc .keepalive ()
629648 }
630649 }
631650
651+ if symbol .module == "" && mod != nil {
652+ symbol .module = mod .Name
653+ }
654+
632655 // cache the resolved symbol
633- if addrs , ok := s .symbols [pid ]; ok {
634- if _ , ok := addrs [addr ]; ! ok {
656+ if sym , ok := s .symbols [pid ]; ok {
657+ if _ , ok := sym [addr ]; ! ok {
635658 s.symbols [pid ][addr ] = symbol
636659 }
637660 } else {
638- s .symbols [pid ] = map [va.Address ]string {addr : symbol }
661+ s .symbols [pid ] = map [va.Address ]syminfo {addr : symbol }
639662 }
640663
641- return symbol
664+ return symbol . symbol
642665}
643666
644667// symbolFromRVA finds the closest export address before RVA.
0 commit comments