mirror of
https://github.com/go-delve/delve.git
synced 2025-10-29 09:46:56 +08:00
service,terminal,cmd/dlv: automatically guessing substitute-path config (#3781)
Add command, API calls and launch.json option to automatically guess substitute-path configuration.
This commit is contained in:
committed by
GitHub
parent
ac14553fda
commit
822014b8e8
@ -12,6 +12,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
@ -2551,3 +2552,157 @@ func (d *Debugger) maybePrintUnattendedBreakpointWarning(stopReason proc.StopRea
|
||||
}
|
||||
api.PrintStack(formatPathFunc, os.Stderr, apiFrames, "", false, api.StackTraceColors{}, includeFunc)
|
||||
}
|
||||
|
||||
// GuessSubstitutePath returns a substitute-path configuration that maps
|
||||
// server paths to client paths by examining the executable file and a map
|
||||
// of module paths to client directories (clientMod2Dir) passed as input.
|
||||
func (d *Debugger) GuessSubstitutePath(args *api.GuessSubstitutePathIn) map[string]string {
|
||||
bis := []*proc.BinaryInfo{}
|
||||
bins := [][]proc.Function{}
|
||||
tgt := proc.ValidTargets{Group: d.target}
|
||||
for tgt.Next() {
|
||||
bi := tgt.BinInfo()
|
||||
bis = append(bis, bi)
|
||||
bins = append(bins, bi.Functions)
|
||||
}
|
||||
return guessSubstitutePath(args, bins, func(biIdx int, fn *proc.Function) string {
|
||||
file, _ := bis[biIdx].EntryLineForFunc(fn)
|
||||
return file
|
||||
})
|
||||
}
|
||||
|
||||
func guessSubstitutePath(args *api.GuessSubstitutePathIn, bins [][]proc.Function, fileForFunc func(int, *proc.Function) string) map[string]string {
|
||||
serverMod2Dir := map[string]string{}
|
||||
serverMod2DirCandidate := map[string]map[string]int{}
|
||||
pkg2mod := map[string]string{}
|
||||
|
||||
for mod := range args.ClientModuleDirectories {
|
||||
serverMod2DirCandidate[mod] = make(map[string]int)
|
||||
}
|
||||
|
||||
const minEvidence = 10
|
||||
const decisionThreshold = 0.8
|
||||
|
||||
totCandidates := func(mod string) int {
|
||||
r := 0
|
||||
for _, cnt := range serverMod2DirCandidate[mod] {
|
||||
r += cnt
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
bestCandidate := func(mod string) string {
|
||||
best := ""
|
||||
for dir, cnt := range serverMod2DirCandidate[mod] {
|
||||
if cnt > serverMod2DirCandidate[mod][best] {
|
||||
best = dir
|
||||
}
|
||||
}
|
||||
return best
|
||||
}
|
||||
|
||||
slashes := func(s string) int {
|
||||
r := 0
|
||||
for _, ch := range s {
|
||||
if ch == '/' {
|
||||
r++
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
serverGoroot := ""
|
||||
|
||||
logger := logflags.DebuggerLogger()
|
||||
|
||||
for binIdx, bin := range bins {
|
||||
for i := range bin {
|
||||
fn := &bin[i]
|
||||
|
||||
if fn.Name == "runtime.main" && serverGoroot == "" {
|
||||
file := fileForFunc(binIdx, fn)
|
||||
serverGoroot = path.Dir(path.Dir(path.Dir(file)))
|
||||
continue
|
||||
}
|
||||
|
||||
fnpkg := fn.PackageName()
|
||||
if fn.CompilationUnitName() != "" && strings.ReplaceAll(fn.CompilationUnitName(), "\\", "/") != fnpkg {
|
||||
// inlined
|
||||
continue
|
||||
}
|
||||
|
||||
if fnpkg == "main" && binIdx == 0 && args.ImportPathOfMainPackage != "" {
|
||||
fnpkg = args.ImportPathOfMainPackage
|
||||
}
|
||||
|
||||
fnmod := ""
|
||||
|
||||
if mod, ok := pkg2mod[fnpkg]; ok {
|
||||
fnmod = mod
|
||||
} else {
|
||||
for mod := range args.ClientModuleDirectories {
|
||||
if strings.HasPrefix(fnpkg, mod) {
|
||||
fnmod = mod
|
||||
break
|
||||
}
|
||||
}
|
||||
pkg2mod[fnpkg] = fnmod
|
||||
if fnmod == "" {
|
||||
logger.Debugf("No module detected for server package %q", fnpkg)
|
||||
}
|
||||
}
|
||||
|
||||
if fnmod == "" {
|
||||
// not in any module we are interested in
|
||||
continue
|
||||
}
|
||||
if serverMod2Dir[fnmod] != "" {
|
||||
// already decided
|
||||
continue
|
||||
}
|
||||
|
||||
elems := slashes(fnpkg[len(fnmod):])
|
||||
|
||||
file := fileForFunc(binIdx, fn)
|
||||
if file == "" || file == "<autogenerated>" {
|
||||
continue
|
||||
}
|
||||
logger.Debugf("considering %s pkg:%s compile unit:%s file:%s", fn.Name, fnpkg, fn.CompilationUnitName(), file)
|
||||
dir := path.Dir(file) // note: paths are normalized to always use '/' as a separator by pkg/dwarf/line
|
||||
if slashes(dir) < elems {
|
||||
continue
|
||||
}
|
||||
for i := 0; i < elems; i++ {
|
||||
dir = path.Dir(dir)
|
||||
}
|
||||
|
||||
serverMod2DirCandidate[fnmod][dir]++
|
||||
|
||||
n := totCandidates(fnmod)
|
||||
best := bestCandidate(fnmod)
|
||||
if n > minEvidence && float64(serverMod2DirCandidate[fnmod][best])/float64(n) > decisionThreshold {
|
||||
serverMod2Dir[fnmod] = best
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for mod := range args.ClientModuleDirectories {
|
||||
if serverMod2Dir[mod] == "" {
|
||||
serverMod2Dir[mod] = bestCandidate(mod)
|
||||
}
|
||||
}
|
||||
|
||||
server2Client := make(map[string]string)
|
||||
|
||||
for mod, clientDir := range args.ClientModuleDirectories {
|
||||
if serverMod2Dir[mod] != "" {
|
||||
server2Client[serverMod2Dir[mod]] = clientDir
|
||||
}
|
||||
}
|
||||
|
||||
if serverGoroot != "" && args.ClientGOROOT != "" {
|
||||
server2Client[serverGoroot] = args.ClientGOROOT
|
||||
}
|
||||
|
||||
return server2Client
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user