|
@@ -358,8 +358,15 @@ func fetch(source string, duration, timeout time.Duration, ui plugin.UI) (p *pro
|
358
|
358
|
}
|
359
|
359
|
f, err = fetchURL(sourceURL, timeout)
|
360
|
360
|
src = sourceURL
|
|
361
|
+ } else if isPerf, isPerfErr := isPerfFile(source); isPerf {
|
|
362
|
+ // Since the if statement is a new scope, if isPerfErr is named
|
|
363
|
+ // err, it shadows the err in the return, and does not compile.
|
|
364
|
+ if isPerfErr != nil {
|
|
365
|
+ return nil, "", isPerfErr
|
|
366
|
+ }
|
|
367
|
+ f, err = convertPerfData(source, ui)
|
361
|
368
|
} else {
|
362
|
|
- f, err = profileProtoReader(source, ui)
|
|
369
|
+ f, err = os.Open(source)
|
363
|
370
|
}
|
364
|
371
|
if err == nil {
|
365
|
372
|
defer f.Close()
|
|
@@ -381,62 +388,43 @@ func fetchURL(source string, timeout time.Duration) (io.ReadCloser, error) {
|
381
|
388
|
return resp.Body, nil
|
382
|
389
|
}
|
383
|
390
|
|
384
|
|
-// profileProtoReader takes a path, and using heuristics, will try to convert
|
385
|
|
-// the file to profile.proto format. It returns a ReadCloser to the
|
386
|
|
-// profile.proto data; however, if the file contents were unknown or conversion
|
387
|
|
-// failed, it may still not be a valid profile.proto.
|
388
|
|
-func profileProtoReader(path string, ui plugin.UI) (io.ReadCloser, error) {
|
|
391
|
+// isPerfFile checks if a file is in perf.data format.
|
|
392
|
+func isPerfFile(path string) (bool, error) {
|
389
|
393
|
sourceFile, openErr := os.Open(path)
|
390
|
394
|
if openErr != nil {
|
391
|
|
- return nil, openErr
|
|
395
|
+ return false, openErr
|
392
|
396
|
}
|
|
397
|
+ defer sourceFile.Close()
|
393
|
398
|
|
394
|
399
|
// If the file is the output of a perf record command, it should begin
|
395
|
400
|
// with the string PERFILE2.
|
396
|
401
|
perfHeader := []byte("PERFILE2")
|
397
|
402
|
actualHeader := make([]byte, len(perfHeader))
|
398
|
|
- if _, readErr := sourceFile.Read(actualHeader); readErr == io.EOF {
|
399
|
|
- _, seekErr := sourceFile.Seek(0, 0)
|
400
|
|
- if seekErr != nil {
|
401
|
|
- return nil, seekErr
|
402
|
|
- }
|
403
|
|
- } else if readErr != nil {
|
404
|
|
- return nil, readErr
|
405
|
|
- }
|
406
|
|
- if bytes.Equal(actualHeader, perfHeader) {
|
407
|
|
- sourceFile.Close()
|
408
|
|
- profileFilePath, convertErr := convertPerfData(path, ui)
|
409
|
|
- if convertErr != nil {
|
410
|
|
- return nil, convertErr
|
411
|
|
- }
|
412
|
|
- profileFile, openErr := os.Open(profileFilePath)
|
413
|
|
- if openErr != nil {
|
414
|
|
- return nil, openErr
|
415
|
|
- }
|
416
|
|
- return profileFile, nil
|
|
403
|
+ if _, readErr := sourceFile.Read(actualHeader); readErr != nil {
|
|
404
|
+ return false, readErr
|
417
|
405
|
}
|
418
|
|
- return sourceFile, nil
|
|
406
|
+ return bytes.Equal(actualHeader, perfHeader), nil
|
419
|
407
|
}
|
420
|
408
|
|
421
|
409
|
// convertPerfData converts the file at path which should be in perf.data format
|
422
|
|
-// using the perf_to_profile tool and returns the path to a file containing the
|
|
410
|
+// using the perf_to_profile tool and returns the file containing the
|
423
|
411
|
// profile.proto formatted data.
|
424
|
|
-func convertPerfData(perfPath string, ui plugin.UI) (string, error) {
|
|
412
|
+func convertPerfData(perfPath string, ui plugin.UI) (*os.File, error) {
|
425
|
413
|
ui.Print(fmt.Sprintf(
|
426
|
414
|
"Converting %s to a profile.proto... (May take a few minutes)",
|
427
|
415
|
perfPath))
|
428
|
416
|
profilePath, err := newTempFilePath("/tmp", "pprof_", ".pb.gz")
|
429
|
417
|
if err != nil {
|
430
|
|
- return "", err
|
|
418
|
+ return nil, err
|
431
|
419
|
}
|
432
|
420
|
cmd := exec.Command("perf_to_profile", perfPath, profilePath)
|
433
|
421
|
// If perf_to_profile failed before generating the file, this defer
|
434
|
422
|
// is just a no-op.
|
435
|
423
|
deferDeleteTempFile(profilePath)
|
436
|
424
|
if err := cmd.Run(); err != nil {
|
437
|
|
- return "", err
|
|
425
|
+ return nil, err
|
438
|
426
|
}
|
439
|
|
- return profilePath, nil
|
|
427
|
+ return os.Open(profilePath)
|
440
|
428
|
}
|
441
|
429
|
|
442
|
430
|
// adjustURL validates if a profile source is a URL and returns an
|