"errors"
"fmt"
"internal/profile"
- "io/ioutil"
+ "io"
"os"
"sort"
"strconv"
var wantHdr = "GO PREPROFILE V1\n"
-func isPreProfileFile(filename string) (bool, error) {
- content, err := ioutil.ReadFile(filename)
- if err != nil {
- return false, err
+func isPreProfileFile(r *bufio.Reader) (bool, error) {
+ hdr, err := r.Peek(len(wantHdr))
+ if err == io.EOF {
+ // Empty file.
+ return false, nil
+ } else if err != nil {
+ return false, fmt.Errorf("error reading profile header: %w", err)
}
- /* check the header */
- fileContent := string(content)
- if strings.HasPrefix(fileContent, wantHdr) {
- return true, nil
- }
- return false, nil
+ return string(hdr) == wantHdr, nil
}
// New generates a profile-graph from the profile or pre-processed profile.
func New(profileFile string) (*Profile, error) {
- var profile *Profile
- var err error
- isPreProf, err := isPreProfileFile(profileFile)
+ f, err := os.Open(profileFile)
if err != nil {
return nil, fmt.Errorf("error opening profile: %w", err)
}
- if !isPreProf {
- profile, err = processProto(profileFile)
- if err != nil {
- return nil, fmt.Errorf("error processing pprof PGO profile: %w", err)
- }
- } else {
- profile, err = processPreprof(profileFile)
+ defer f.Close()
+
+ r := bufio.NewReader(f)
+
+ isPreProf, err := isPreProfileFile(r)
+ if err != nil {
+ return nil, fmt.Errorf("error processing profile header: %w", err)
+ }
+
+ if isPreProf {
+ profile, err := processPreprof(r)
if err != nil {
return nil, fmt.Errorf("error processing preprocessed PGO profile: %w", err)
}
+ return profile, nil
+ }
+
+ profile, err := processProto(r)
+ if err != nil {
+ return nil, fmt.Errorf("error processing pprof PGO profile: %w", err)
}
return profile, nil
}
// processProto generates a profile-graph from the profile.
-func processProto(profileFile string) (*Profile, error) {
- f, err := os.Open(profileFile)
- if err != nil {
- return nil, fmt.Errorf("error opening profile: %w", err)
- }
- defer f.Close()
- p, err := profile.Parse(f)
+func processProto(r io.Reader) (*Profile, error) {
+ p, err := profile.Parse(r)
if errors.Is(err, profile.ErrNoData) {
// Treat a completely empty file the same as a profile with no
// samples: nothing to do.
}
// processPreprof generates a profile-graph from the pre-procesed profile.
-func processPreprof(preprofileFile string) (*Profile, error) {
- namedEdgeMap, totalWeight, err := createNamedEdgeMapFromPreprocess(preprofileFile)
+func processPreprof(r io.Reader) (*Profile, error) {
+ namedEdgeMap, totalWeight, err := createNamedEdgeMapFromPreprocess(r)
if err != nil {
return nil, err
}
// restore NodeMap information from a preprocessed profile.
// The reader can refer to the format of preprocessed profile in cmd/preprofile/main.go.
-func createNamedEdgeMapFromPreprocess(preprofileFile string) (edgeMap NamedEdgeMap, totalWeight int64, err error) {
- readFile, err := os.Open(preprofileFile)
- if err != nil {
- return NamedEdgeMap{}, 0, fmt.Errorf("error opening preprocessed profile: %w", err)
- }
- defer readFile.Close()
-
- fileScanner := bufio.NewScanner(readFile)
+func createNamedEdgeMapFromPreprocess(r io.Reader) (edgeMap NamedEdgeMap, totalWeight int64, err error) {
+ fileScanner := bufio.NewScanner(r)
fileScanner.Split(bufio.ScanLines)
weight := make(map[NamedCallEdge]int64)