-
Notifications
You must be signed in to change notification settings - Fork 666
internal/report: fall back to $GOROOT/src when locating source files #1010
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,6 +25,7 @@ import ( | |
| "os" | ||
| "path/filepath" | ||
| "regexp" | ||
| "runtime" | ||
| "slices" | ||
| "sort" | ||
| "strconv" | ||
|
|
@@ -1007,28 +1008,95 @@ func openSourceFile(path, searchPath, trim string) (*os.File, error) { | |
| path = trimPath(path, trim, searchPath) | ||
| // If file is still absolute, require file to exist. | ||
| if filepath.IsAbs(path) { | ||
| f, err := os.Open(path) | ||
| return f, err | ||
| } | ||
| // Scan each component of the path. | ||
| for _, dir := range filepath.SplitList(searchPath) { | ||
| // Search up for every parent of each possible path. | ||
| for { | ||
| filename := filepath.Join(dir, path) | ||
| if f, err := os.Open(filename); err == nil { | ||
| return f, nil | ||
| } | ||
| parent := filepath.Dir(dir) | ||
| if parent == dir { | ||
| break | ||
| if f, err := tryOpenFile(path); err == nil { | ||
| return f, nil | ||
| } | ||
| } else { | ||
| // Scan each component of the path. | ||
| for _, dir := range filepath.SplitList(searchPath) { | ||
| // Search up for every parent of each possible path. | ||
| for { | ||
| filename := filepath.Join(dir, path) | ||
| if f, err := tryOpenFile(filename); err == nil { | ||
| return f, nil | ||
| } | ||
| parent := filepath.Dir(dir) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note to self: This is historical but I think we should remove using even parent path of the search paths. When I specify /foo/bar as the search path, I do not expect that the tool will attempt /foo/src/baz.cc paths or something like that. In particular, /foo/src can be a slow NFS / remote mounted directory and trying to look up those paths will slow down the scan. |
||
| if parent == dir { | ||
| break | ||
| } | ||
| dir = parent | ||
| } | ||
| dir = parent | ||
| } | ||
| } | ||
| // Fall back to looking for Go standard library sources under the local | ||
| // $GOROOT/src, since profiles from Go programs refer to standard library | ||
| // files under the GOROOT of the machine where the program was built. | ||
| if f, err := openGorootSourceFile(path, gorootSrc); err == nil { | ||
| return f, nil | ||
| } | ||
|
|
||
| return nil, fmt.Errorf("could not find file %s on path %s", path, searchPath) | ||
| } | ||
|
|
||
| // tryOpenFile opens the file at filename if it exists and is not a directory, | ||
| // which os.Open would also happily open. | ||
| func tryOpenFile(filename string) (*os.File, error) { | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fail fast when you try to open a directory.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did you see cases where this practically happens? Asking because it seems unlikely that a source file path matches a directory name because source file names usually have an extension and directory names do not. |
||
| f, err := os.Open(filename) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| stat, err := f.Stat() | ||
| if err != nil { | ||
| f.Close() | ||
| return nil, err | ||
| } | ||
| if stat.IsDir() { | ||
| f.Close() | ||
| return nil, fmt.Errorf("%s is a directory", filename) | ||
| } | ||
| return f, nil | ||
| } | ||
|
|
||
| // gorootSrc is the directory holding the Go standard library sources, if | ||
| // known. runtime.GOROOT honors the $GOROOT environment variable and otherwise | ||
| // reports the GOROOT this binary was built with, which is the Go installation | ||
| // used to `go install` pprof in the common case. | ||
| var gorootSrc = func() string { | ||
| if goroot := runtime.GOROOT(); goroot != "" { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think pprof binary's |
||
| return filepath.Join(goroot, "src") | ||
| } | ||
| return "" | ||
| }() | ||
|
|
||
| // openGorootSourceFile tries to open a Go standard library source file under | ||
| // gorootSrc. Binaries built without -trimpath record standard library files | ||
| // under the build machine's GOROOT (e.g. /usr/local/go/src/runtime/proc.go), | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a possibility we could instead 1) modify |
||
| // which may not exist locally, so resolve the path components after a "/src/" | ||
| // component against gorootSrc. Binaries built with -trimpath record them | ||
| // relative to $GOROOT/src (e.g. runtime/proc.go), so resolve relative paths | ||
| // against gorootSrc directly. | ||
| func openGorootSourceFile(path, gorootSrc string) (*os.File, error) { | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is the special handling for goroot, only executed if the openSourceFile previous machinery doesn't find anything. |
||
| if gorootSrc == "" { | ||
| return nil, fmt.Errorf("GOROOT is not known") | ||
| } | ||
| if !filepath.IsAbs(path) { | ||
| if f, err := tryOpenFile(filepath.Join(gorootSrc, path)); err == nil { | ||
| return f, nil | ||
| } | ||
| } | ||
| sPath := filepath.ToSlash(path) | ||
| for { | ||
| i := strings.Index(sPath, "/src/") | ||
| if i == -1 { | ||
| return nil, fmt.Errorf("could not find file %s under %s", path, gorootSrc) | ||
| } | ||
| sPath = sPath[i+len("/src/"):] | ||
| if f, err := tryOpenFile(filepath.Join(gorootSrc, filepath.FromSlash(sPath))); err == nil { | ||
| return f, nil | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // trimPath cleans up a path by removing prefixes that are commonly | ||
| // found on profiles plus configured prefixes. | ||
| // TODO(aalexand): Consider optimizing out the redundant work done in this | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All this changes are round allowing to fallback later to other "handlers", for now we are adding the goroot handler, but in other PRs we will add more special handlers like gomodcache or vendor.