Browse Source

Merge pull request #32 from wade-k/master

Adds hook to allow pprof to handle perf.data files.
Raul Silvera 8 years ago
parent
commit
1a10f4067d
2 changed files with 44 additions and 1 deletions
  1. 43
    0
      internal/driver/fetch.go
  2. 1
    1
      internal/driver/tempfile.go

+ 43
- 0
internal/driver/fetch.go View File

15
 package driver
15
 package driver
16
 
16
 
17
 import (
17
 import (
18
+	"bytes"
18
 	"fmt"
19
 	"fmt"
19
 	"io"
20
 	"io"
20
 	"net/http"
21
 	"net/http"
21
 	"net/url"
22
 	"net/url"
22
 	"os"
23
 	"os"
24
+	"os/exec"
23
 	"path/filepath"
25
 	"path/filepath"
24
 	"strconv"
26
 	"strconv"
25
 	"sync"
27
 	"sync"
356
 		}
358
 		}
357
 		f, err = fetchURL(sourceURL, timeout)
359
 		f, err = fetchURL(sourceURL, timeout)
358
 		src = sourceURL
360
 		src = sourceURL
361
+	} else if isPerfFile(source) {
362
+		f, err = convertPerfData(source, ui)
359
 	} else {
363
 	} else {
360
 		f, err = os.Open(source)
364
 		f, err = os.Open(source)
361
 	}
365
 	}
379
 	return resp.Body, nil
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
 // adjustURL validates if a profile source is a URL and returns an
425
 // adjustURL validates if a profile source is a URL and returns an
383
 // cleaned up URL and the timeout to use for retrieval over HTTP.
426
 // cleaned up URL and the timeout to use for retrieval over HTTP.
384
 // If the source cannot be recognized as a URL it returns an empty string.
427
 // If the source cannot be recognized as a URL it returns an empty string.

+ 1
- 1
internal/driver/tempfile.go View File

21
 	"sync"
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
 func newTempFile(dir, prefix, suffix string) (*os.File, error) {
25
 func newTempFile(dir, prefix, suffix string) (*os.File, error) {
26
 	for index := 1; index < 10000; index++ {
26
 	for index := 1; index < 10000; index++ {
27
 		path := filepath.Join(dir, fmt.Sprintf("%s%03d%s", prefix, index, suffix))
27
 		path := filepath.Join(dir, fmt.Sprintf("%s%03d%s", prefix, index, suffix))