|
@@ -15,11 +15,13 @@
|
15
|
15
|
package driver
|
16
|
16
|
|
17
|
17
|
import (
|
|
18
|
+ "bytes"
|
18
|
19
|
"fmt"
|
19
|
20
|
"io"
|
20
|
21
|
"net/http"
|
21
|
22
|
"net/url"
|
22
|
23
|
"os"
|
|
24
|
+ "os/exec"
|
23
|
25
|
"path/filepath"
|
24
|
26
|
"strconv"
|
25
|
27
|
"sync"
|
|
@@ -356,6 +358,8 @@ func fetch(source string, duration, timeout time.Duration, ui plugin.UI) (p *pro
|
356
|
358
|
}
|
357
|
359
|
f, err = fetchURL(sourceURL, timeout)
|
358
|
360
|
src = sourceURL
|
|
361
|
+ } else if isPerfFile(source) {
|
|
362
|
+ f, err = convertPerfData(source, ui)
|
359
|
363
|
} else {
|
360
|
364
|
f, err = os.Open(source)
|
361
|
365
|
}
|
|
@@ -379,6 +383,45 @@ func fetchURL(source string, timeout time.Duration) (io.ReadCloser, error) {
|
379
|
383
|
return resp.Body, nil
|
380
|
384
|
}
|
381
|
385
|
|
|
386
|
+// isPerfFile checks if a file is in perf.data format. It also returns false
|
|
387
|
+// if it encounters an error during the check.
|
|
388
|
+func isPerfFile(path string) bool {
|
|
389
|
+ sourceFile, openErr := os.Open(path)
|
|
390
|
+ if openErr != nil {
|
|
391
|
+ return false
|
|
392
|
+ }
|
|
393
|
+ defer sourceFile.Close()
|
|
394
|
+
|
|
395
|
+ // If the file is the output of a perf record command, it should begin
|
|
396
|
+ // with the string PERFILE2.
|
|
397
|
+ perfHeader := []byte("PERFILE2")
|
|
398
|
+ actualHeader := make([]byte, len(perfHeader))
|
|
399
|
+ if _, readErr := sourceFile.Read(actualHeader); readErr != nil {
|
|
400
|
+ return false
|
|
401
|
+ }
|
|
402
|
+ return bytes.Equal(actualHeader, perfHeader)
|
|
403
|
+}
|
|
404
|
+
|
|
405
|
+// convertPerfData converts the file at path which should be in perf.data format
|
|
406
|
+// using the perf_to_profile tool and returns the file containing the
|
|
407
|
+// profile.proto formatted data.
|
|
408
|
+func convertPerfData(perfPath string, ui plugin.UI) (*os.File, error) {
|
|
409
|
+ ui.Print(fmt.Sprintf(
|
|
410
|
+ "Converting %s to a profile.proto... (May take a few minutes)",
|
|
411
|
+ perfPath))
|
|
412
|
+ profile, err := newTempFile("/tmp", "pprof_", ".pb.gz")
|
|
413
|
+ if err != nil {
|
|
414
|
+ return nil, err
|
|
415
|
+ }
|
|
416
|
+ deferDeleteTempFile(profile.Name())
|
|
417
|
+ cmd := exec.Command("perf_to_profile", perfPath, profile.Name())
|
|
418
|
+ if err := cmd.Run(); err != nil {
|
|
419
|
+ profile.Close()
|
|
420
|
+ return nil, err
|
|
421
|
+ }
|
|
422
|
+ return profile, nil
|
|
423
|
+}
|
|
424
|
+
|
382
|
425
|
// adjustURL validates if a profile source is a URL and returns an
|
383
|
426
|
// cleaned up URL and the timeout to use for retrieval over HTTP.
|
384
|
427
|
// If the source cannot be recognized as a URL it returns an empty string.
|