Skip to content

Commit efc84b3

Browse files
committed
feat(symbolizer): Symbolize thread start address
Resolve the thread start address to its respective symbol name and the module from which the function call is originated.
1 parent 4d6027d commit efc84b3

File tree

2 files changed

+74
-0
lines changed

2 files changed

+74
-0
lines changed

pkg/kevent/kparams/fields_windows.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ const (
7878
UstackLimit = "ustack_limit"
7979
// StartAddress field is the thread start address.
8080
StartAddress = "start_address"
81+
// StartAddressSymbol field is the symbol associated with the thread start address.
82+
StartAddressSymbol = "start_address_symbol"
83+
// StartAddressModule field is the module where the thread start address is mapped.
84+
StartAddressModule = "start_address_module"
8185
// TEB field is the address of the Thread Environment Block (TEB)
8286
TEB = "teb"
8387

pkg/symbolize/symbolizer.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,9 +298,25 @@ func (s *Symbolizer) processCallstack(e *kevent.Kevent) error {
298298
defer s.mu.Unlock()
299299

300300
if e.PS != nil {
301+
// symbolize thread start address
302+
if e.IsCreateThread() && !e.IsSystemPid() {
303+
addr := e.Kparams.TryGetAddress(kparams.StartAddress)
304+
305+
mod := e.PS.FindModuleByVa(addr)
306+
symbol := s.symbolizeAddress(e.Kparams.MustGetPid(), addr, mod)
307+
308+
if symbol != "" {
309+
e.Kparams.Append(kparams.StartAddressSymbol, kparams.UnicodeString, symbol)
310+
}
311+
if mod != nil {
312+
e.Kparams.Append(kparams.StartAddressModule, kparams.UnicodeString, mod.Name)
313+
}
314+
}
315+
301316
// try to resolve addresses from process
302317
// state and PE export directory data
303318
s.pushFrames(addrs, e, false, true)
319+
304320
return nil
305321
}
306322

@@ -504,6 +520,60 @@ func (s *Symbolizer) resolveSymbolFromExportDirectory(addr va.Address, mod *psty
504520
return symbolFromRVA(rva, px.Exports)
505521
}
506522

523+
// symbolizeAddress resolves the given address to a symbol. If the symbol
524+
// for this address was resolved previously, we fetch it from the cache.
525+
// On the contrary, the symbol is first consulted in the export directory.
526+
// If not found, the Debug Help API is used to symbolize the address.
527+
func (s *Symbolizer) symbolizeAddress(pid uint32, addr va.Address, mod *pstypes.Module) string {
528+
if addr.InSystemRange() {
529+
return ""
530+
}
531+
532+
symbol, ok := s.symbols[pid][addr]
533+
if !ok && mod != nil {
534+
// resolve symbol from the export directory
535+
symbol = s.resolveSymbolFromExportDirectory(addr, mod)
536+
}
537+
538+
// try to get the symbol via Debug Help API
539+
if symbol == "" {
540+
proc, ok := s.procs[pid]
541+
if !ok {
542+
handle, err := windows.OpenProcess(windows.SYNCHRONIZE|windows.PROCESS_QUERY_INFORMATION, false, pid)
543+
if err != nil {
544+
return ""
545+
}
546+
547+
// initialize symbol handler
548+
opts := uint32(sys.SymUndname | sys.SymCaseInsensitive | sys.SymAutoPublics | sys.SymOmapFindNearest | sys.SymDeferredLoads)
549+
err = s.r.Initialize(handle, opts)
550+
if err != nil {
551+
return ""
552+
}
553+
554+
proc = &process{pid, handle, time.Now(), 1}
555+
s.procs[pid] = proc
556+
557+
// resolve address to symbol
558+
symbol, _ = s.r.GetSymbolNameAndOffset(handle, addr)
559+
} else {
560+
symbol, _ = s.r.GetSymbolNameAndOffset(proc.handle, addr)
561+
proc.keepalive()
562+
}
563+
}
564+
565+
// cache the resolved symbol
566+
if addrs, ok := s.symbols[pid]; ok {
567+
if _, ok := addrs[addr]; !ok {
568+
s.symbols[pid][addr] = symbol
569+
}
570+
} else {
571+
s.symbols[pid] = map[va.Address]string{addr: symbol}
572+
}
573+
574+
return symbol
575+
}
576+
507577
// symbolFromRVA finds the closest export address before RVA.
508578
func symbolFromRVA(rva va.Address, exports map[uint32]string) string {
509579
var exp uint32

0 commit comments

Comments
 (0)