diff --git a/internal/driver/fetch.go b/internal/driver/fetch.go index 584c5d85..e8c65b56 100644 --- a/internal/driver/fetch.go +++ b/internal/driver/fetch.go @@ -492,7 +492,9 @@ mapping: func fetch(source string, duration, timeout time.Duration, ui plugin.UI, tr http.RoundTripper) (p *profile.Profile, src string, err error) { var f io.ReadCloser - if sourceURL, timeout := adjustURL(source, duration, timeout); sourceURL != "" { + if isFile(source) { + f, err = os.Open(source) + } else if sourceURL, timeout := adjustURL(source, duration, timeout); sourceURL != "" { ui.Print("Fetching profile over HTTP from " + sourceURL) if duration > 0 { ui.Print(fmt.Sprintf("Please wait... (%v)", duration)) @@ -614,3 +616,15 @@ func adjustURL(source string, duration, timeout time.Duration) (string, time.Dur u.RawQuery = values.Encode() return u.String(), timeout } + +// isFile validates if the profile source is a file, but it will be recognized +// as a link by url.Parse (when it contains `:`, like mem_2023-11-01_15:04:05). +// If such a file exists in the current directory, it will be recognized as a +// file rather than a link. +func isFile(source string) bool { + _, err := os.Stat(source) + if err == nil { + return true + } + return false +} diff --git a/internal/driver/fetch_test.go b/internal/driver/fetch_test.go index 8a6a5535..1d2f5431 100644 --- a/internal/driver/fetch_test.go +++ b/internal/driver/fetch_test.go @@ -216,6 +216,38 @@ func TestFetch(t *testing.T) { } } +func TestFetchWhenFileContainColon(t *testing.T) { + const path = "testdata/" + type testcase struct { + source, execName string + } + + for _, tc := range []testcase{ + // If the file containing a colon is in the current directory, an error will be reported + // and the test will not pass (the test will be passed after adding logic) + {path + "go.crc32.cpu_2023-11-11_01:02:03", ""}, + // If the file containing colon is in a non-current directory, it can pass the test + // regardless of whether it is modified or not. + {"go.crc32.cpu_2023-12-12_06:06:06", ""}, + } { + p, _, _, err := grabProfile(&source{ExecName: tc.execName}, tc.source, nil, testObj{}, &proftest.TestUI{T: t}, &httpTransport{}) + if err != nil { + t.Fatalf("%s: %s", tc.source, err) + } + if len(p.Sample) == 0 { + t.Errorf("%s: want non-zero samples", tc.source) + } + if e := tc.execName; e != "" { + switch { + case len(p.Mapping) == 0 || p.Mapping[0] == nil: + t.Errorf("%s: want mapping[0].execName == %s, got no mappings", tc.source, e) + case p.Mapping[0].File != e: + t.Errorf("%s: want mapping[0].execName == %s, got %s", tc.source, e, p.Mapping[0].File) + } + } + } +} + func TestFetchWithBase(t *testing.T) { baseConfig := currentConfig() defer setCurrentConfig(baseConfig) diff --git a/internal/driver/go.crc32.cpu_2023-12-12_06:06:06 b/internal/driver/go.crc32.cpu_2023-12-12_06:06:06 new file mode 100644 index 00000000..ce08313d Binary files /dev/null and b/internal/driver/go.crc32.cpu_2023-12-12_06:06:06 differ diff --git a/internal/driver/testdata/go.crc32.cpu_2023-11-11_01:02:03 b/internal/driver/testdata/go.crc32.cpu_2023-11-11_01:02:03 new file mode 100644 index 00000000..ce08313d Binary files /dev/null and b/internal/driver/testdata/go.crc32.cpu_2023-11-11_01:02:03 differ