浏览代码

Merge pull request #32 from wade-k/master

Adds hook to allow pprof to handle perf.data files.
Raul Silvera 8 年前
父节点
当前提交
1a10f4067d
共有 2 个文件被更改,包括 44 次插入1 次删除
  1. 43
    0
      internal/driver/fetch.go
  2. 1
    1
      internal/driver/tempfile.go

+ 43
- 0
internal/driver/fetch.go 查看文件

@@ -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.

+ 1
- 1
internal/driver/tempfile.go 查看文件

@@ -21,7 +21,7 @@ import (
21 21
 	"sync"
22 22
 )
23 23
 
24
-// newTempFile returns an unused filename for output files.
24
+// newTempFile returns a new output file in dir with the provided prefix and suffix.
25 25
 func newTempFile(dir, prefix, suffix string) (*os.File, error) {
26 26
 	for index := 1; index < 10000; index++ {
27 27
 		path := filepath.Join(dir, fmt.Sprintf("%s%03d%s", prefix, index, suffix))