2020-05-13 06:38:10 +00:00
|
|
|
package locspec
|
2015-08-07 16:50:14 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2016-01-10 13:08:16 +00:00
|
|
|
"go/constant"
|
2019-02-21 19:21:41 +00:00
|
|
|
"path"
|
2015-08-07 16:50:14 +00:00
|
|
|
"path/filepath"
|
2016-01-10 13:08:16 +00:00
|
|
|
"reflect"
|
2020-05-13 06:38:10 +00:00
|
|
|
"regexp"
|
2016-01-26 21:08:05 +00:00
|
|
|
"runtime"
|
2015-08-07 16:50:14 +00:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
|
2019-01-04 18:39:25 +00:00
|
|
|
"github.com/go-delve/delve/pkg/proc"
|
|
|
|
"github.com/go-delve/delve/service/api"
|
2015-08-07 16:50:14 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const maxFindLocationCandidates = 5
|
|
|
|
|
2020-05-13 06:38:10 +00:00
|
|
|
// LocationSpec is an interface that represents a parsed location spec string.
|
2015-08-07 16:50:14 +00:00
|
|
|
type LocationSpec interface {
|
2020-05-13 06:38:10 +00:00
|
|
|
// Find returns all locations that match the location spec.
|
2023-07-20 10:29:59 +00:00
|
|
|
Find(t *proc.Target, processArgs []string, scope *proc.EvalScope, locStr string, includeNonExecutableLines bool, substitutePathRules [][2]string) ([]api.Location, string, error)
|
2015-08-07 16:50:14 +00:00
|
|
|
}
|
|
|
|
|
2020-05-13 06:38:10 +00:00
|
|
|
// NormalLocationSpec represents a basic location spec.
|
|
|
|
// This can be a file:line or func:line.
|
2015-08-07 16:50:14 +00:00
|
|
|
type NormalLocationSpec struct {
|
|
|
|
Base string
|
|
|
|
FuncBase *FuncLocationSpec
|
|
|
|
LineOffset int
|
|
|
|
}
|
|
|
|
|
2020-05-13 06:38:10 +00:00
|
|
|
// RegexLocationSpec represents a regular expression
|
|
|
|
// location expression such as /^myfunc$/.
|
2015-08-07 16:50:14 +00:00
|
|
|
type RegexLocationSpec struct {
|
|
|
|
FuncRegex string
|
|
|
|
}
|
|
|
|
|
2020-05-13 06:38:10 +00:00
|
|
|
// AddrLocationSpec represents an address when used
|
|
|
|
// as a location spec.
|
2015-08-07 16:50:14 +00:00
|
|
|
type AddrLocationSpec struct {
|
2016-01-10 13:08:16 +00:00
|
|
|
AddrExpr string
|
2015-08-07 16:50:14 +00:00
|
|
|
}
|
|
|
|
|
2020-05-13 06:38:10 +00:00
|
|
|
// OffsetLocationSpec represents a location spec that
|
|
|
|
// is an offset of the current location (file:line).
|
2015-08-07 16:50:14 +00:00
|
|
|
type OffsetLocationSpec struct {
|
|
|
|
Offset int
|
|
|
|
}
|
|
|
|
|
2020-05-13 06:38:10 +00:00
|
|
|
// LineLocationSpec represents a line number in the current file.
|
2015-08-07 16:50:14 +00:00
|
|
|
type LineLocationSpec struct {
|
|
|
|
Line int
|
|
|
|
}
|
|
|
|
|
2020-05-13 06:38:10 +00:00
|
|
|
// FuncLocationSpec represents a function in the target program.
|
2015-08-07 16:50:14 +00:00
|
|
|
type FuncLocationSpec struct {
|
|
|
|
PackageName string
|
2015-12-12 14:01:35 +00:00
|
|
|
AbsolutePackage bool
|
2015-08-07 16:50:14 +00:00
|
|
|
ReceiverName string
|
|
|
|
PackageOrReceiverName string
|
|
|
|
BaseName string
|
|
|
|
}
|
|
|
|
|
2020-05-13 06:38:10 +00:00
|
|
|
// Parse will turn locStr into a parsed LocationSpec.
|
|
|
|
func Parse(locStr string) (LocationSpec, error) {
|
2015-08-07 16:50:14 +00:00
|
|
|
rest := locStr
|
|
|
|
|
|
|
|
malformed := func(reason string) error {
|
2021-09-28 19:07:42 +00:00
|
|
|
//lint:ignore ST1005 backwards compatibility
|
2023-07-05 15:49:08 +00:00
|
|
|
return fmt.Errorf("Malformed breakpoint location %q at %d: %s", locStr, len(locStr)-len(rest), reason)
|
2015-08-07 16:50:14 +00:00
|
|
|
}
|
|
|
|
|
2023-07-13 18:30:32 +00:00
|
|
|
if len(rest) == 0 {
|
2015-08-07 16:50:14 +00:00
|
|
|
return nil, malformed("empty string")
|
|
|
|
}
|
|
|
|
|
|
|
|
switch rest[0] {
|
|
|
|
case '+', '-':
|
|
|
|
offset, err := strconv.Atoi(rest)
|
|
|
|
if err != nil {
|
|
|
|
return nil, malformed(err.Error())
|
|
|
|
}
|
|
|
|
return &OffsetLocationSpec{offset}, nil
|
|
|
|
|
|
|
|
case '/':
|
2015-09-08 20:00:38 +00:00
|
|
|
if rest[len(rest)-1] == '/' {
|
|
|
|
rx, rest := readRegex(rest[1:])
|
2019-07-01 18:10:09 +00:00
|
|
|
if len(rest) == 0 {
|
2015-09-08 20:00:38 +00:00
|
|
|
return nil, malformed("non-terminated regular expression")
|
|
|
|
}
|
|
|
|
if len(rest) > 1 {
|
|
|
|
return nil, malformed("no line offset can be specified for regular expression locations")
|
|
|
|
}
|
|
|
|
return &RegexLocationSpec{rx}, nil
|
|
|
|
} else {
|
|
|
|
return parseLocationSpecDefault(locStr, rest)
|
2015-08-07 16:50:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
case '*':
|
2020-05-13 06:38:10 +00:00
|
|
|
return &AddrLocationSpec{AddrExpr: rest[1:]}, nil
|
2015-08-07 16:50:14 +00:00
|
|
|
|
|
|
|
default:
|
2015-09-08 20:00:38 +00:00
|
|
|
return parseLocationSpecDefault(locStr, rest)
|
|
|
|
}
|
|
|
|
}
|
2015-08-07 16:50:14 +00:00
|
|
|
|
2015-09-08 20:00:38 +00:00
|
|
|
func parseLocationSpecDefault(locStr, rest string) (LocationSpec, error) {
|
|
|
|
malformed := func(reason string) error {
|
2021-09-28 19:07:42 +00:00
|
|
|
//lint:ignore ST1005 backwards compatibility
|
2023-07-05 15:49:08 +00:00
|
|
|
return fmt.Errorf("Malformed breakpoint location %q at %d: %s", locStr, len(locStr)-len(rest), reason)
|
2015-09-08 20:00:38 +00:00
|
|
|
}
|
2015-08-07 16:50:14 +00:00
|
|
|
|
2016-01-15 05:26:54 +00:00
|
|
|
v := strings.Split(rest, ":")
|
|
|
|
if len(v) > 2 {
|
|
|
|
// On Windows, path may contain ":", so split only on last ":"
|
2016-01-26 21:08:05 +00:00
|
|
|
v = []string{strings.Join(v[0:len(v)-1], ":"), v[len(v)-1]}
|
2016-01-15 05:26:54 +00:00
|
|
|
}
|
2015-08-07 16:50:14 +00:00
|
|
|
|
2015-09-08 20:00:38 +00:00
|
|
|
if len(v) == 1 {
|
|
|
|
n, err := strconv.ParseInt(v[0], 0, 64)
|
|
|
|
if err == nil {
|
|
|
|
return &LineLocationSpec{int(n)}, nil
|
2015-08-07 16:50:14 +00:00
|
|
|
}
|
2015-09-08 20:00:38 +00:00
|
|
|
}
|
2015-08-07 16:50:14 +00:00
|
|
|
|
2015-09-08 20:00:38 +00:00
|
|
|
spec := &NormalLocationSpec{}
|
2015-08-07 16:50:14 +00:00
|
|
|
|
2015-09-08 20:00:38 +00:00
|
|
|
spec.Base = v[0]
|
|
|
|
spec.FuncBase = parseFuncLocationSpec(spec.Base)
|
2015-08-07 16:50:14 +00:00
|
|
|
|
2015-09-08 20:00:38 +00:00
|
|
|
if len(v) < 2 {
|
|
|
|
spec.LineOffset = -1
|
2015-08-07 16:50:14 +00:00
|
|
|
return spec, nil
|
|
|
|
}
|
2015-09-08 20:00:38 +00:00
|
|
|
|
|
|
|
rest = v[1]
|
|
|
|
|
|
|
|
var err error
|
|
|
|
spec.LineOffset, err = strconv.Atoi(rest)
|
|
|
|
if err != nil || spec.LineOffset < 0 {
|
|
|
|
return nil, malformed("line offset negative or not a number")
|
|
|
|
}
|
|
|
|
|
|
|
|
return spec, nil
|
2015-08-07 16:50:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func readRegex(in string) (rx string, rest string) {
|
|
|
|
out := make([]rune, 0, len(in))
|
|
|
|
escaped := false
|
|
|
|
for i, ch := range in {
|
|
|
|
if escaped {
|
|
|
|
if ch == '/' {
|
|
|
|
out = append(out, '/')
|
|
|
|
} else {
|
2020-03-18 22:45:29 +00:00
|
|
|
out = append(out, '\\', ch)
|
2015-08-07 16:50:14 +00:00
|
|
|
}
|
|
|
|
escaped = false
|
|
|
|
} else {
|
|
|
|
switch ch {
|
|
|
|
case '\\':
|
|
|
|
escaped = true
|
|
|
|
case '/':
|
|
|
|
return string(out), in[i:]
|
|
|
|
default:
|
|
|
|
out = append(out, ch)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return string(out), ""
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseFuncLocationSpec(in string) *FuncLocationSpec {
|
2016-07-05 19:13:21 +00:00
|
|
|
var v []string
|
|
|
|
pathend := strings.LastIndex(in, "/")
|
|
|
|
if pathend < 0 {
|
|
|
|
v = strings.Split(in, ".")
|
|
|
|
} else {
|
|
|
|
v = strings.Split(in[pathend:], ".")
|
|
|
|
if len(v) > 0 {
|
|
|
|
v[0] = in[:pathend] + v[0]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-07 16:50:14 +00:00
|
|
|
var spec FuncLocationSpec
|
|
|
|
switch len(v) {
|
|
|
|
case 1:
|
|
|
|
spec.BaseName = v[0]
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
spec.BaseName = v[1]
|
|
|
|
r := stripReceiverDecoration(v[0])
|
|
|
|
if r != v[0] {
|
|
|
|
spec.ReceiverName = r
|
2017-06-29 18:15:59 +00:00
|
|
|
} else if strings.Contains(r, "/") {
|
2015-10-17 07:31:07 +00:00
|
|
|
spec.PackageName = r
|
2015-08-07 16:50:14 +00:00
|
|
|
} else {
|
|
|
|
spec.PackageOrReceiverName = r
|
|
|
|
}
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
spec.BaseName = v[2]
|
|
|
|
spec.ReceiverName = stripReceiverDecoration(v[1])
|
2015-12-12 14:01:35 +00:00
|
|
|
spec.PackageName = v[0]
|
2015-08-07 16:50:14 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
return nil
|
2015-10-17 07:31:07 +00:00
|
|
|
}
|
|
|
|
|
2015-12-12 14:01:35 +00:00
|
|
|
if strings.HasPrefix(spec.PackageName, "/") {
|
|
|
|
spec.PackageName = spec.PackageName[1:]
|
|
|
|
spec.AbsolutePackage = true
|
|
|
|
}
|
|
|
|
|
2017-06-29 18:15:59 +00:00
|
|
|
if strings.Contains(spec.BaseName, "/") || strings.Contains(spec.ReceiverName, "/") {
|
2015-10-17 07:31:07 +00:00
|
|
|
return nil
|
2015-08-07 16:50:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return &spec
|
|
|
|
}
|
|
|
|
|
|
|
|
func stripReceiverDecoration(in string) string {
|
|
|
|
if len(in) < 3 {
|
|
|
|
return in
|
|
|
|
}
|
|
|
|
if (in[0] != '(') || (in[1] != '*') || (in[len(in)-1] != ')') {
|
|
|
|
return in
|
|
|
|
}
|
|
|
|
|
|
|
|
return in[2 : len(in)-1]
|
|
|
|
}
|
|
|
|
|
2020-05-13 06:38:10 +00:00
|
|
|
// Match will return whether the provided function matches the location spec.
|
2021-10-30 18:52:26 +00:00
|
|
|
func (spec *FuncLocationSpec) Match(sym *proc.Function, packageMap map[string][]string) bool {
|
2015-08-07 16:50:14 +00:00
|
|
|
if spec.BaseName != sym.BaseName() {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
recv := stripReceiverDecoration(sym.ReceiverName())
|
|
|
|
if spec.ReceiverName != "" && spec.ReceiverName != recv {
|
|
|
|
return false
|
|
|
|
}
|
2015-12-12 14:01:35 +00:00
|
|
|
if spec.PackageName != "" {
|
|
|
|
if spec.AbsolutePackage {
|
|
|
|
if spec.PackageName != sym.PackageName() {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
} else {
|
2019-11-25 17:10:18 +00:00
|
|
|
if !packageMatch(spec.PackageName, sym.PackageName(), packageMap) {
|
2015-12-12 14:01:35 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
2015-08-07 16:50:14 +00:00
|
|
|
}
|
2019-11-25 17:10:18 +00:00
|
|
|
if spec.PackageOrReceiverName != "" && !packageMatch(spec.PackageOrReceiverName, sym.PackageName(), packageMap) && spec.PackageOrReceiverName != recv {
|
2015-08-07 16:50:14 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2019-11-25 17:10:18 +00:00
|
|
|
func packageMatch(specPkg, symPkg string, packageMap map[string][]string) bool {
|
|
|
|
for _, pkg := range packageMap[specPkg] {
|
|
|
|
if partialPackageMatch(pkg, symPkg) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return partialPackageMatch(specPkg, symPkg)
|
|
|
|
}
|
|
|
|
|
2020-05-13 06:38:10 +00:00
|
|
|
// Find will search all functions in the target program and filter them via the
|
|
|
|
// regex location spec. Only functions matching the regex will be returned.
|
2023-07-20 10:29:59 +00:00
|
|
|
func (loc *RegexLocationSpec) Find(t *proc.Target, _ []string, scope *proc.EvalScope, locStr string, includeNonExecutableLines bool, _ [][2]string) ([]api.Location, string, error) {
|
2022-10-04 15:07:05 +00:00
|
|
|
if scope == nil {
|
|
|
|
//TODO(aarzilli): this needs only the list of function we should make it work
|
2023-07-20 10:29:59 +00:00
|
|
|
return nil, "", fmt.Errorf("could not determine location (scope is nil)")
|
2022-10-04 15:07:05 +00:00
|
|
|
}
|
2020-05-13 06:38:10 +00:00
|
|
|
funcs := scope.BinInfo.Functions
|
2015-08-07 16:50:14 +00:00
|
|
|
matches, err := regexFilterFuncs(loc.FuncRegex, funcs)
|
|
|
|
if err != nil {
|
2023-07-20 10:29:59 +00:00
|
|
|
return nil, "", err
|
2015-08-07 16:50:14 +00:00
|
|
|
}
|
|
|
|
r := make([]api.Location, 0, len(matches))
|
|
|
|
for i := range matches {
|
2020-05-13 06:38:10 +00:00
|
|
|
addrs, _ := proc.FindFunctionLocation(t, matches[i], 0)
|
2019-11-04 16:43:12 +00:00
|
|
|
if len(addrs) > 0 {
|
|
|
|
r = append(r, addressesToLocation(addrs))
|
2015-08-07 16:50:14 +00:00
|
|
|
}
|
|
|
|
}
|
2023-07-20 10:29:59 +00:00
|
|
|
return r, "", nil
|
2015-08-07 16:50:14 +00:00
|
|
|
}
|
|
|
|
|
2020-05-13 06:38:10 +00:00
|
|
|
// Find returns the locations specified via the address location spec.
|
2023-07-20 10:29:59 +00:00
|
|
|
func (loc *AddrLocationSpec) Find(t *proc.Target, _ []string, scope *proc.EvalScope, locStr string, includeNonExecutableLines bool, _ [][2]string) ([]api.Location, string, error) {
|
2016-01-10 13:08:16 +00:00
|
|
|
if scope == nil {
|
|
|
|
addr, err := strconv.ParseInt(loc.AddrExpr, 0, 64)
|
|
|
|
if err != nil {
|
2023-07-20 10:29:59 +00:00
|
|
|
return nil, "", fmt.Errorf("could not determine current location (scope is nil)")
|
2016-01-10 13:08:16 +00:00
|
|
|
}
|
2023-07-20 10:29:59 +00:00
|
|
|
return []api.Location{{PC: uint64(addr)}}, "", nil
|
2020-05-13 06:38:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
v, err := scope.EvalExpression(loc.AddrExpr, proc.LoadConfig{FollowPointers: true})
|
|
|
|
if err != nil {
|
2023-07-20 10:29:59 +00:00
|
|
|
return nil, "", err
|
2020-05-13 06:38:10 +00:00
|
|
|
}
|
|
|
|
if v.Unreadable != nil {
|
2023-07-20 10:29:59 +00:00
|
|
|
return nil, "", v.Unreadable
|
2020-05-13 06:38:10 +00:00
|
|
|
}
|
|
|
|
switch v.Kind {
|
|
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
|
|
addr, _ := constant.Uint64Val(v.Value)
|
2023-07-20 10:29:59 +00:00
|
|
|
return []api.Location{{PC: addr}}, "", nil
|
2020-05-13 06:38:10 +00:00
|
|
|
case reflect.Func:
|
2023-11-14 15:36:55 +00:00
|
|
|
fn := scope.BinInfo.PCToFunc(v.Base)
|
2020-05-13 06:38:10 +00:00
|
|
|
pc, err := proc.FirstPCAfterPrologue(t, fn, false)
|
2016-01-10 13:08:16 +00:00
|
|
|
if err != nil {
|
2023-07-20 10:29:59 +00:00
|
|
|
return nil, "", err
|
2016-01-10 13:08:16 +00:00
|
|
|
}
|
2023-07-20 10:29:59 +00:00
|
|
|
return []api.Location{{PC: pc}}, v.Name, nil
|
2020-05-13 06:38:10 +00:00
|
|
|
default:
|
2023-07-20 10:29:59 +00:00
|
|
|
return nil, "", fmt.Errorf("wrong expression kind: %v", v.Kind)
|
2016-01-10 13:08:16 +00:00
|
|
|
}
|
2015-08-07 16:50:14 +00:00
|
|
|
}
|
|
|
|
|
2020-05-13 06:38:10 +00:00
|
|
|
// FileMatch is true if the path matches the location spec.
|
2015-08-07 16:50:14 +00:00
|
|
|
func (loc *NormalLocationSpec) FileMatch(path string) bool {
|
2015-12-12 14:01:35 +00:00
|
|
|
return partialPathMatch(loc.Base, path)
|
|
|
|
}
|
|
|
|
|
2019-02-21 19:21:41 +00:00
|
|
|
func tryMatchRelativePathByProc(expr, debugname, file string) bool {
|
|
|
|
return len(expr) > 0 && expr[0] == '.' && file == path.Join(path.Dir(debugname), expr)
|
|
|
|
}
|
|
|
|
|
2015-12-12 14:01:35 +00:00
|
|
|
func partialPathMatch(expr, path string) bool {
|
2016-01-30 21:27:08 +00:00
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
// Accept `expr` which is case-insensitive and slash-insensitive match to `path`
|
|
|
|
expr = strings.ToLower(filepath.ToSlash(expr))
|
|
|
|
path = strings.ToLower(filepath.ToSlash(path))
|
|
|
|
}
|
2019-11-25 17:10:18 +00:00
|
|
|
return partialPackageMatch(expr, path)
|
|
|
|
}
|
|
|
|
|
|
|
|
func partialPackageMatch(expr, path string) bool {
|
2015-12-12 14:01:35 +00:00
|
|
|
if len(expr) < len(path)-1 {
|
2016-01-15 05:26:54 +00:00
|
|
|
return strings.HasSuffix(path, expr) && (path[len(path)-len(expr)-1] == '/')
|
2015-09-08 20:00:38 +00:00
|
|
|
}
|
2020-05-13 06:38:10 +00:00
|
|
|
return expr == path
|
2015-08-07 16:50:14 +00:00
|
|
|
}
|
|
|
|
|
2020-05-13 06:38:10 +00:00
|
|
|
// AmbiguousLocationError is returned when the location spec
|
|
|
|
// should only return one location but returns multiple instead.
|
2015-07-29 14:24:52 +00:00
|
|
|
type AmbiguousLocationError struct {
|
|
|
|
Location string
|
|
|
|
CandidatesString []string
|
|
|
|
CandidatesLocation []api.Location
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ale AmbiguousLocationError) Error() string {
|
|
|
|
var candidates []string
|
|
|
|
if ale.CandidatesLocation != nil {
|
|
|
|
for i := range ale.CandidatesLocation {
|
2018-07-06 07:37:31 +00:00
|
|
|
candidates = append(candidates, ale.CandidatesLocation[i].Function.Name())
|
2015-07-29 14:24:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
candidates = ale.CandidatesString
|
|
|
|
}
|
2023-07-05 15:49:08 +00:00
|
|
|
return fmt.Sprintf("Location %q ambiguous: %s…", ale.Location, strings.Join(candidates, ", "))
|
2015-07-29 14:24:52 +00:00
|
|
|
}
|
|
|
|
|
2020-05-13 06:38:10 +00:00
|
|
|
// Find will return a list of locations that match the given location spec.
|
|
|
|
// This matches each other location spec that does not already have its own spec
|
|
|
|
// implemented (such as regex, or addr).
|
2023-07-20 10:29:59 +00:00
|
|
|
func (loc *NormalLocationSpec) Find(t *proc.Target, processArgs []string, scope *proc.EvalScope, locStr string, includeNonExecutableLines bool, substitutePathRules [][2]string) ([]api.Location, string, error) {
|
2017-07-20 19:02:59 +00:00
|
|
|
limit := maxFindLocationCandidates
|
|
|
|
var candidateFiles []string
|
2022-09-26 17:12:34 +00:00
|
|
|
for _, sourceFile := range t.BinInfo().Sources {
|
2020-10-26 12:36:52 +00:00
|
|
|
substFile := sourceFile
|
|
|
|
if len(substitutePathRules) > 0 {
|
|
|
|
substFile = SubstitutePath(sourceFile, substitutePathRules)
|
|
|
|
}
|
|
|
|
if loc.FileMatch(substFile) || (len(processArgs) >= 1 && tryMatchRelativePathByProc(loc.Base, processArgs[0], substFile)) {
|
|
|
|
candidateFiles = append(candidateFiles, sourceFile)
|
2017-07-20 19:02:59 +00:00
|
|
|
if len(candidateFiles) >= limit {
|
2015-08-07 16:50:14 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-20 19:02:59 +00:00
|
|
|
limit -= len(candidateFiles)
|
|
|
|
|
|
|
|
var candidateFuncs []string
|
2021-10-30 18:52:26 +00:00
|
|
|
if loc.FuncBase != nil && limit > 0 {
|
2022-09-26 17:12:34 +00:00
|
|
|
candidateFuncs = loc.findFuncCandidates(t.BinInfo(), limit)
|
2015-08-07 16:50:14 +00:00
|
|
|
}
|
|
|
|
|
2022-10-04 15:07:05 +00:00
|
|
|
if matching := len(candidateFiles) + len(candidateFuncs); matching == 0 {
|
|
|
|
if scope == nil {
|
2023-07-20 10:29:59 +00:00
|
|
|
return nil, "", fmt.Errorf("location %q not found", locStr)
|
2022-10-04 15:07:05 +00:00
|
|
|
}
|
2020-05-13 06:38:10 +00:00
|
|
|
// if no result was found this locations string could be an
|
2017-07-26 18:52:51 +00:00
|
|
|
// expression that the user forgot to prefix with '*', try treating it as
|
|
|
|
// such.
|
2020-05-13 06:38:10 +00:00
|
|
|
addrSpec := &AddrLocationSpec{AddrExpr: locStr}
|
2023-07-20 10:29:59 +00:00
|
|
|
locs, subst, err := addrSpec.Find(t, processArgs, scope, locStr, includeNonExecutableLines, nil)
|
2017-07-26 18:52:51 +00:00
|
|
|
if err != nil {
|
2023-07-20 10:29:59 +00:00
|
|
|
return nil, "", fmt.Errorf("location %q not found", locStr)
|
2017-07-26 18:52:51 +00:00
|
|
|
}
|
2023-07-20 10:29:59 +00:00
|
|
|
return locs, subst, nil
|
2017-07-20 19:02:59 +00:00
|
|
|
} else if matching > 1 {
|
2023-07-20 10:29:59 +00:00
|
|
|
return nil, "", AmbiguousLocationError{Location: locStr, CandidatesString: append(candidateFiles, candidateFuncs...)}
|
2017-07-20 19:02:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// len(candidateFiles) + len(candidateFuncs) == 1
|
2019-11-01 19:41:06 +00:00
|
|
|
var addrs []uint64
|
2017-07-20 19:02:59 +00:00
|
|
|
var err error
|
|
|
|
if len(candidateFiles) == 1 {
|
|
|
|
if loc.LineOffset < 0 {
|
2021-09-28 19:07:42 +00:00
|
|
|
//lint:ignore ST1005 backwards compatibility
|
2023-07-20 10:29:59 +00:00
|
|
|
return nil, "", fmt.Errorf("Malformed breakpoint location, no line offset specified")
|
2015-08-07 16:50:14 +00:00
|
|
|
}
|
2020-05-13 06:38:10 +00:00
|
|
|
addrs, err = proc.FindFileLocation(t, candidateFiles[0], loc.LineOffset)
|
2019-10-25 16:59:18 +00:00
|
|
|
if includeNonExecutableLines {
|
|
|
|
if _, isCouldNotFindLine := err.(*proc.ErrCouldNotFindLine); isCouldNotFindLine {
|
2023-07-20 10:29:59 +00:00
|
|
|
return []api.Location{{File: candidateFiles[0], Line: loc.LineOffset}}, "", nil
|
2019-10-25 16:59:18 +00:00
|
|
|
}
|
|
|
|
}
|
2019-11-04 16:43:12 +00:00
|
|
|
} else { // len(candidateFuncs) == 1
|
2020-05-13 06:38:10 +00:00
|
|
|
addrs, err = proc.FindFunctionLocation(t, candidateFuncs[0], loc.LineOffset)
|
2017-07-20 19:02:59 +00:00
|
|
|
}
|
2015-08-07 16:50:14 +00:00
|
|
|
|
2017-07-20 19:02:59 +00:00
|
|
|
if err != nil {
|
2023-07-20 10:29:59 +00:00
|
|
|
return nil, "", err
|
2015-08-07 16:50:14 +00:00
|
|
|
}
|
2023-07-20 10:29:59 +00:00
|
|
|
return []api.Location{addressesToLocation(addrs)}, "", nil
|
2019-11-01 19:41:06 +00:00
|
|
|
}
|
|
|
|
|
2022-09-26 17:12:34 +00:00
|
|
|
func (loc *NormalLocationSpec) findFuncCandidates(bi *proc.BinaryInfo, limit int) []string {
|
2021-10-30 18:52:26 +00:00
|
|
|
candidateFuncs := map[string]struct{}{}
|
|
|
|
// See if it matches generic functions first
|
2022-09-26 17:12:34 +00:00
|
|
|
for fname := range bi.LookupGenericFunc() {
|
2021-10-30 18:52:26 +00:00
|
|
|
if len(candidateFuncs) >= limit {
|
|
|
|
break
|
|
|
|
}
|
2022-09-26 17:12:34 +00:00
|
|
|
if !loc.FuncBase.Match(&proc.Function{Name: fname}, bi.PackageMap) {
|
2021-10-30 18:52:26 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
if loc.Base == fname {
|
|
|
|
return []string{fname}
|
|
|
|
}
|
|
|
|
candidateFuncs[fname] = struct{}{}
|
|
|
|
}
|
2023-03-22 18:38:09 +00:00
|
|
|
for _, fns := range bi.LookupFunc() {
|
|
|
|
f := fns[0]
|
2021-10-30 18:52:26 +00:00
|
|
|
if len(candidateFuncs) >= limit {
|
|
|
|
break
|
|
|
|
}
|
2022-09-26 17:12:34 +00:00
|
|
|
if !loc.FuncBase.Match(f, bi.PackageMap) {
|
2021-10-30 18:52:26 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
if loc.Base == f.Name {
|
|
|
|
// if an exact match for the function name is found use it
|
|
|
|
return []string{f.Name}
|
|
|
|
}
|
|
|
|
// If f is an instantiation of a generic function see if we should add its generic version instead.
|
|
|
|
if gn := f.NameWithoutTypeParams(); gn != "" {
|
|
|
|
if _, alreadyAdded := candidateFuncs[gn]; !alreadyAdded {
|
|
|
|
candidateFuncs[f.Name] = struct{}{}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
candidateFuncs[f.Name] = struct{}{}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// convert candidateFuncs map into an array of its keys
|
|
|
|
r := make([]string, 0, len(candidateFuncs))
|
|
|
|
for s := range candidateFuncs {
|
|
|
|
r = append(r, s)
|
|
|
|
}
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
*: misc improvements to config command and substitute-path rules (#3335)
A series of interconnected changes to both the terminal command
'config', DAP command 'dlv config', quality of life improvements to how
substitute-path works, and better documentation.
- Let 'config substitute-path' show the current substitute path rules
- Add a -clear command to 'config substitute-path'
- Support 'config-debug-info-directories'
- rewrite SubstitutePath to be platform independent (see below)
- document path substitution more
Regarding the rewrite of SubstitutePath: the previous version used
runtime.GOOS and filepath.IsAbs to determine which filepath separator to use
and if matching should be case insensitive. This is wrong in all situations
where the client and server run on different OSes, when examining core files
and when cross-compilation is involved.
The new version of SubstitutePath checks the rules and the input path to
determine if Windows is involved in the process, if it looks like it is it
switches to case-insensitive matching. It uses a lax version of
filepath.IsAbs to determine if a path is absolute and tries to avoid having
to select a path separator as much as possible
Fixes #2891, #2890, #2889, #3179, #3332, #3343
2023-05-02 19:23:59 +00:00
|
|
|
// isAbs returns true if path looks like an absolute path.
|
|
|
|
func isAbs(path string) bool {
|
|
|
|
// Unix-like absolute path
|
|
|
|
if strings.HasPrefix(path, "/") {
|
|
|
|
return true
|
2020-10-26 12:36:52 +00:00
|
|
|
}
|
*: misc improvements to config command and substitute-path rules (#3335)
A series of interconnected changes to both the terminal command
'config', DAP command 'dlv config', quality of life improvements to how
substitute-path works, and better documentation.
- Let 'config substitute-path' show the current substitute path rules
- Add a -clear command to 'config substitute-path'
- Support 'config-debug-info-directories'
- rewrite SubstitutePath to be platform independent (see below)
- document path substitution more
Regarding the rewrite of SubstitutePath: the previous version used
runtime.GOOS and filepath.IsAbs to determine which filepath separator to use
and if matching should be case insensitive. This is wrong in all situations
where the client and server run on different OSes, when examining core files
and when cross-compilation is involved.
The new version of SubstitutePath checks the rules and the input path to
determine if Windows is involved in the process, if it looks like it is it
switches to case-insensitive matching. It uses a lax version of
filepath.IsAbs to determine if a path is absolute and tries to avoid having
to select a path separator as much as possible
Fixes #2891, #2890, #2889, #3179, #3332, #3343
2023-05-02 19:23:59 +00:00
|
|
|
return windowsAbsPath(path)
|
|
|
|
}
|
|
|
|
|
|
|
|
func windowsAbsPath(path string) bool {
|
|
|
|
// Windows UNC absolute path
|
|
|
|
if strings.HasPrefix(path, `\\`) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
// DOS absolute paths
|
|
|
|
if len(path) < 3 || path[1] != ':' {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return path[2] == '/' || path[2] == '\\'
|
|
|
|
}
|
|
|
|
|
|
|
|
func hasPathSeparatorSuffix(path string) bool {
|
|
|
|
return strings.HasSuffix(path, "/") || strings.HasSuffix(path, "\\")
|
|
|
|
}
|
|
|
|
|
|
|
|
func hasPathSeparatorPrefix(path string) bool {
|
|
|
|
return strings.HasPrefix(path, "/") || strings.HasPrefix(path, "\\")
|
2020-10-26 12:36:52 +00:00
|
|
|
}
|
|
|
|
|
2023-08-14 22:34:29 +00:00
|
|
|
func pickSeparator(to string) string {
|
|
|
|
var sep byte
|
|
|
|
for i := range to {
|
|
|
|
if to[i] == '/' || to[i] == '\\' {
|
|
|
|
if sep == 0 {
|
|
|
|
sep = to[i]
|
|
|
|
} else if sep != to[i] {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return string(sep)
|
|
|
|
}
|
|
|
|
|
|
|
|
func joinPath(to, rest string) string {
|
|
|
|
sep := pickSeparator(to)
|
|
|
|
|
|
|
|
switch sep {
|
|
|
|
case "/":
|
|
|
|
rest = strings.ReplaceAll(rest, "\\", sep)
|
|
|
|
case "\\":
|
|
|
|
rest = strings.ReplaceAll(rest, "/", sep)
|
|
|
|
default:
|
|
|
|
sep = "/"
|
|
|
|
}
|
|
|
|
|
|
|
|
toEndsWithSlash := hasPathSeparatorSuffix(to)
|
|
|
|
restStartsWithSlash := hasPathSeparatorPrefix(rest)
|
|
|
|
|
|
|
|
switch {
|
|
|
|
case toEndsWithSlash && restStartsWithSlash:
|
|
|
|
return to[:len(to)-1] + rest
|
|
|
|
case toEndsWithSlash && !restStartsWithSlash:
|
|
|
|
return to + rest
|
|
|
|
case !toEndsWithSlash && restStartsWithSlash:
|
|
|
|
return to + rest
|
|
|
|
case !toEndsWithSlash && !restStartsWithSlash:
|
|
|
|
fallthrough
|
|
|
|
default:
|
|
|
|
return to + sep + rest
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-26 12:36:52 +00:00
|
|
|
// SubstitutePath applies the specified path substitution rules to path.
|
|
|
|
func SubstitutePath(path string, rules [][2]string) string {
|
*: misc improvements to config command and substitute-path rules (#3335)
A series of interconnected changes to both the terminal command
'config', DAP command 'dlv config', quality of life improvements to how
substitute-path works, and better documentation.
- Let 'config substitute-path' show the current substitute path rules
- Add a -clear command to 'config substitute-path'
- Support 'config-debug-info-directories'
- rewrite SubstitutePath to be platform independent (see below)
- document path substitution more
Regarding the rewrite of SubstitutePath: the previous version used
runtime.GOOS and filepath.IsAbs to determine which filepath separator to use
and if matching should be case insensitive. This is wrong in all situations
where the client and server run on different OSes, when examining core files
and when cross-compilation is involved.
The new version of SubstitutePath checks the rules and the input path to
determine if Windows is involved in the process, if it looks like it is it
switches to case-insensitive matching. It uses a lax version of
filepath.IsAbs to determine if a path is absolute and tries to avoid having
to select a path separator as much as possible
Fixes #2891, #2890, #2889, #3179, #3332, #3343
2023-05-02 19:23:59 +00:00
|
|
|
// Look for evidence that we are dealing with windows somewhere, if we are use case-insensitive matching
|
|
|
|
caseInsensitive := windowsAbsPath(path)
|
|
|
|
if !caseInsensitive {
|
|
|
|
for i := range rules {
|
|
|
|
if windowsAbsPath(rules[i][0]) || windowsAbsPath(rules[i][1]) {
|
|
|
|
caseInsensitive = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2020-10-26 12:36:52 +00:00
|
|
|
}
|
|
|
|
for _, r := range rules {
|
*: misc improvements to config command and substitute-path rules (#3335)
A series of interconnected changes to both the terminal command
'config', DAP command 'dlv config', quality of life improvements to how
substitute-path works, and better documentation.
- Let 'config substitute-path' show the current substitute path rules
- Add a -clear command to 'config substitute-path'
- Support 'config-debug-info-directories'
- rewrite SubstitutePath to be platform independent (see below)
- document path substitution more
Regarding the rewrite of SubstitutePath: the previous version used
runtime.GOOS and filepath.IsAbs to determine which filepath separator to use
and if matching should be case insensitive. This is wrong in all situations
where the client and server run on different OSes, when examining core files
and when cross-compilation is involved.
The new version of SubstitutePath checks the rules and the input path to
determine if Windows is involved in the process, if it looks like it is it
switches to case-insensitive matching. It uses a lax version of
filepath.IsAbs to determine if a path is absolute and tries to avoid having
to select a path separator as much as possible
Fixes #2891, #2890, #2889, #3179, #3332, #3343
2023-05-02 19:23:59 +00:00
|
|
|
from, to := r[0], r[1]
|
2020-10-26 12:36:52 +00:00
|
|
|
|
*: misc improvements to config command and substitute-path rules (#3335)
A series of interconnected changes to both the terminal command
'config', DAP command 'dlv config', quality of life improvements to how
substitute-path works, and better documentation.
- Let 'config substitute-path' show the current substitute path rules
- Add a -clear command to 'config substitute-path'
- Support 'config-debug-info-directories'
- rewrite SubstitutePath to be platform independent (see below)
- document path substitution more
Regarding the rewrite of SubstitutePath: the previous version used
runtime.GOOS and filepath.IsAbs to determine which filepath separator to use
and if matching should be case insensitive. This is wrong in all situations
where the client and server run on different OSes, when examining core files
and when cross-compilation is involved.
The new version of SubstitutePath checks the rules and the input path to
determine if Windows is involved in the process, if it looks like it is it
switches to case-insensitive matching. It uses a lax version of
filepath.IsAbs to determine if a path is absolute and tries to avoid having
to select a path separator as much as possible
Fixes #2891, #2890, #2889, #3179, #3332, #3343
2023-05-02 19:23:59 +00:00
|
|
|
// if we have an exact match, use it directly.
|
2022-07-25 16:57:31 +00:00
|
|
|
if path == from {
|
|
|
|
return to
|
|
|
|
}
|
|
|
|
|
*: misc improvements to config command and substitute-path rules (#3335)
A series of interconnected changes to both the terminal command
'config', DAP command 'dlv config', quality of life improvements to how
substitute-path works, and better documentation.
- Let 'config substitute-path' show the current substitute path rules
- Add a -clear command to 'config substitute-path'
- Support 'config-debug-info-directories'
- rewrite SubstitutePath to be platform independent (see below)
- document path substitution more
Regarding the rewrite of SubstitutePath: the previous version used
runtime.GOOS and filepath.IsAbs to determine which filepath separator to use
and if matching should be case insensitive. This is wrong in all situations
where the client and server run on different OSes, when examining core files
and when cross-compilation is involved.
The new version of SubstitutePath checks the rules and the input path to
determine if Windows is involved in the process, if it looks like it is it
switches to case-insensitive matching. It uses a lax version of
filepath.IsAbs to determine if a path is absolute and tries to avoid having
to select a path separator as much as possible
Fixes #2891, #2890, #2889, #3179, #3332, #3343
2023-05-02 19:23:59 +00:00
|
|
|
match := false
|
|
|
|
var rest string
|
|
|
|
if from == "" {
|
|
|
|
match = !isAbs(path)
|
|
|
|
rest = path
|
|
|
|
} else {
|
|
|
|
if caseInsensitive {
|
|
|
|
match = strings.HasPrefix(strings.ToLower(path), strings.ToLower(from))
|
|
|
|
if match {
|
|
|
|
path = strings.ToLower(path)
|
|
|
|
from = strings.ToLower(from)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
match = strings.HasPrefix(path, from)
|
|
|
|
}
|
|
|
|
if match {
|
|
|
|
// make sure the match ends on something that looks like a path separator boundary
|
|
|
|
rest = path[len(from):]
|
|
|
|
match = hasPathSeparatorSuffix(from) || hasPathSeparatorPrefix(rest)
|
|
|
|
}
|
2020-10-26 12:36:52 +00:00
|
|
|
}
|
2022-08-14 14:01:39 +00:00
|
|
|
|
*: misc improvements to config command and substitute-path rules (#3335)
A series of interconnected changes to both the terminal command
'config', DAP command 'dlv config', quality of life improvements to how
substitute-path works, and better documentation.
- Let 'config substitute-path' show the current substitute path rules
- Add a -clear command to 'config substitute-path'
- Support 'config-debug-info-directories'
- rewrite SubstitutePath to be platform independent (see below)
- document path substitution more
Regarding the rewrite of SubstitutePath: the previous version used
runtime.GOOS and filepath.IsAbs to determine which filepath separator to use
and if matching should be case insensitive. This is wrong in all situations
where the client and server run on different OSes, when examining core files
and when cross-compilation is involved.
The new version of SubstitutePath checks the rules and the input path to
determine if Windows is involved in the process, if it looks like it is it
switches to case-insensitive matching. It uses a lax version of
filepath.IsAbs to determine if a path is absolute and tries to avoid having
to select a path separator as much as possible
Fixes #2891, #2890, #2889, #3179, #3332, #3343
2023-05-02 19:23:59 +00:00
|
|
|
if match {
|
|
|
|
if to == "" {
|
|
|
|
// make sure we return a relative path, regardless of whether 'from' consumed a final / or not
|
|
|
|
if hasPathSeparatorPrefix(rest) {
|
|
|
|
return rest[1:]
|
|
|
|
}
|
|
|
|
return rest
|
|
|
|
}
|
2022-08-14 14:01:39 +00:00
|
|
|
|
2023-08-14 22:34:29 +00:00
|
|
|
return joinPath(to, rest)
|
2020-10-26 12:36:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return path
|
|
|
|
}
|
|
|
|
|
2019-11-04 16:43:12 +00:00
|
|
|
func addressesToLocation(addrs []uint64) api.Location {
|
2023-07-13 18:30:32 +00:00
|
|
|
if len(addrs) == 0 {
|
2019-11-04 16:43:12 +00:00
|
|
|
return api.Location{}
|
2019-11-01 19:41:06 +00:00
|
|
|
}
|
2019-11-04 16:43:12 +00:00
|
|
|
return api.Location{PC: addrs[0], PCs: addrs}
|
2015-08-07 16:50:14 +00:00
|
|
|
}
|
|
|
|
|
2020-05-13 06:38:10 +00:00
|
|
|
// Find returns the location after adding the offset amount to the current line number.
|
2023-07-20 10:29:59 +00:00
|
|
|
func (loc *OffsetLocationSpec) Find(t *proc.Target, _ []string, scope *proc.EvalScope, _ string, includeNonExecutableLines bool, _ [][2]string) ([]api.Location, string, error) {
|
2016-01-10 13:08:16 +00:00
|
|
|
if scope == nil {
|
2023-07-20 10:29:59 +00:00
|
|
|
return nil, "", fmt.Errorf("could not determine current location (scope is nil)")
|
2016-01-10 13:08:16 +00:00
|
|
|
}
|
2023-07-20 10:29:59 +00:00
|
|
|
file, line, fn := scope.BinInfo.PCToLine(scope.PC)
|
2017-11-28 14:32:25 +00:00
|
|
|
if loc.Offset == 0 {
|
2023-07-20 10:29:59 +00:00
|
|
|
subst := ""
|
|
|
|
if fn != nil {
|
|
|
|
subst = fmt.Sprintf("%s:%d", file, line)
|
|
|
|
}
|
|
|
|
return []api.Location{{PC: scope.PC}}, subst, nil
|
2017-11-28 14:32:25 +00:00
|
|
|
}
|
2015-08-28 20:06:29 +00:00
|
|
|
if fn == nil {
|
2023-07-20 10:29:59 +00:00
|
|
|
return nil, "", fmt.Errorf("could not determine current location")
|
2015-08-07 16:50:14 +00:00
|
|
|
}
|
2023-07-20 10:29:59 +00:00
|
|
|
subst := fmt.Sprintf("%s:%d", file, line+loc.Offset)
|
2020-05-13 06:38:10 +00:00
|
|
|
addrs, err := proc.FindFileLocation(t, file, line+loc.Offset)
|
2019-10-25 16:59:18 +00:00
|
|
|
if includeNonExecutableLines {
|
|
|
|
if _, isCouldNotFindLine := err.(*proc.ErrCouldNotFindLine); isCouldNotFindLine {
|
2023-07-20 10:29:59 +00:00
|
|
|
return []api.Location{{File: file, Line: line + loc.Offset}}, subst, nil
|
2019-10-25 16:59:18 +00:00
|
|
|
}
|
|
|
|
}
|
2023-07-20 10:29:59 +00:00
|
|
|
return []api.Location{addressesToLocation(addrs)}, subst, err
|
2015-08-07 16:50:14 +00:00
|
|
|
}
|
|
|
|
|
2020-05-13 06:38:10 +00:00
|
|
|
// Find will return the location at the given line in the current file.
|
2023-07-20 10:29:59 +00:00
|
|
|
func (loc *LineLocationSpec) Find(t *proc.Target, _ []string, scope *proc.EvalScope, _ string, includeNonExecutableLines bool, _ [][2]string) ([]api.Location, string, error) {
|
2016-01-10 13:08:16 +00:00
|
|
|
if scope == nil {
|
2023-07-20 10:29:59 +00:00
|
|
|
return nil, "", fmt.Errorf("could not determine current location (scope is nil)")
|
2016-01-10 13:08:16 +00:00
|
|
|
}
|
2020-05-13 06:38:10 +00:00
|
|
|
file, _, fn := scope.BinInfo.PCToLine(scope.PC)
|
2015-08-28 20:06:29 +00:00
|
|
|
if fn == nil {
|
2023-07-20 10:29:59 +00:00
|
|
|
return nil, "", fmt.Errorf("could not determine current location")
|
2015-08-07 16:50:14 +00:00
|
|
|
}
|
2023-07-20 10:29:59 +00:00
|
|
|
subst := fmt.Sprintf("%s:%d", file, loc.Line)
|
2020-05-13 06:38:10 +00:00
|
|
|
addrs, err := proc.FindFileLocation(t, file, loc.Line)
|
2019-10-25 16:59:18 +00:00
|
|
|
if includeNonExecutableLines {
|
|
|
|
if _, isCouldNotFindLine := err.(*proc.ErrCouldNotFindLine); isCouldNotFindLine {
|
2023-07-20 10:29:59 +00:00
|
|
|
return []api.Location{{File: file, Line: loc.Line}}, subst, nil
|
2019-10-25 16:59:18 +00:00
|
|
|
}
|
|
|
|
}
|
2023-07-20 10:29:59 +00:00
|
|
|
return []api.Location{addressesToLocation(addrs)}, subst, err
|
2015-08-07 16:50:14 +00:00
|
|
|
}
|
2020-05-13 06:38:10 +00:00
|
|
|
|
|
|
|
func regexFilterFuncs(filter string, allFuncs []proc.Function) ([]string, error) {
|
|
|
|
regex, err := regexp.Compile(filter)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("invalid filter argument: %s", err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
funcs := []string{}
|
|
|
|
for _, f := range allFuncs {
|
|
|
|
if regex.MatchString(f.Name) {
|
|
|
|
funcs = append(funcs, f.Name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return funcs, nil
|
|
|
|
}
|