Skip to content

Commit 6633f98

Browse files
committed
fix(symbolizer): Lookup live modules
When the module in process state can't be derived from the VA, we enumerate the modules via API call
1 parent 3df8f30 commit 6633f98

2 files changed

Lines changed: 75 additions & 0 deletions

File tree

pkg/symbolize/symbolizer.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ var (
6565
// symModulesCount counts the number of loaded module exports
6666
symModulesCount = expvar.NewInt("symbolizer.modules.count")
6767

68+
// symEnumModulesHits counts the number of hits from enumerated modules
69+
symEnumModulesHits = expvar.NewInt("symbolizer.enum.modules.hits")
70+
6871
// debugHelpFallbacks counts how many times we Debug Help API was called
6972
// to resolve symbol information since we fail to do this from process
7073
// modules and PE export directory data
@@ -462,6 +465,23 @@ func (s *Symbolizer) produceFrame(addr va.Address, e *kevent.Kevent) callstack.F
462465
if mod == nil && e.PS.Parent != nil {
463466
mod = e.PS.Parent.FindModuleByVa(addr)
464467
}
468+
if mod == nil {
469+
// our last resort is to enumerate process modules
470+
modules := sys.EnumProcessModules(e.PID)
471+
for _, m := range modules {
472+
b := va.Address(m.BaseOfDll)
473+
size := uint64(m.SizeOfImage)
474+
if addr >= b && addr <= b.Inc(size) {
475+
mod = &pstypes.Module{
476+
Name: m.Name,
477+
BaseAddress: b,
478+
Size: size,
479+
}
480+
symEnumModulesHits.Add(1)
481+
break
482+
}
483+
}
484+
}
465485
if mod != nil {
466486
frame.Module = mod.Name
467487
frame.ModuleAddress = mod.BaseAddress

pkg/sys/process.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,3 +117,58 @@ func IsWindowsService() bool {
117117
isSvc, err := svc.IsWindowsService()
118118
return isSvc && err == nil
119119
}
120+
121+
// ProcessModule describes the process loaded module.
122+
type ProcessModule struct {
123+
windows.ModuleInfo
124+
Name string
125+
}
126+
127+
// EnumProcessModules returns all loaded modules in the process address space.
128+
func EnumProcessModules(pid uint32) []ProcessModule {
129+
n := uint32(1024)
130+
mods := make([]windows.Handle, n)
131+
proc, err := windows.OpenProcess(windows.PROCESS_QUERY_INFORMATION|windows.PROCESS_VM_READ, false, pid)
132+
if err != nil {
133+
return nil
134+
}
135+
defer windows.Close(proc)
136+
if err := windows.EnumProcessModules(proc, &mods[0], 1024, &n); err != nil {
137+
// need bigger buffer
138+
if n > 1024 {
139+
mods = make([]windows.Handle, n)
140+
if err := windows.EnumProcessModules(proc, &mods[0], 1024, &n); err != nil {
141+
return nil
142+
}
143+
}
144+
return nil
145+
}
146+
147+
modules := make([]ProcessModule, 0)
148+
for _, mod := range mods {
149+
if mod == 0 {
150+
continue
151+
}
152+
var moduleInfo windows.ModuleInfo
153+
if err := windows.GetModuleInformation(proc, mod, &moduleInfo, uint32(unsafe.Sizeof(moduleInfo))); err != nil {
154+
continue
155+
}
156+
module := ProcessModule{ModuleInfo: moduleInfo}
157+
if name, err := getModuleFileName(proc, mod); err == nil {
158+
module.Name = name
159+
}
160+
modules = append(modules, module)
161+
}
162+
163+
return modules
164+
}
165+
166+
func getModuleFileName(proc, mod windows.Handle) (string, error) {
167+
n := uint32(1024)
168+
buf := make([]uint16, n)
169+
err := windows.GetModuleFileNameEx(proc, mod, &buf[0], n)
170+
if err != nil {
171+
return "", err
172+
}
173+
return windows.UTF16ToString(buf), nil
174+
}

0 commit comments

Comments
 (0)