-
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) | ||
| 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. |
||
| 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 != "" { | ||
| 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), | ||
| // 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.