aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/pgo/irgraph.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/compile/internal/pgo/irgraph.go')
-rw-r--r--src/cmd/compile/internal/pgo/irgraph.go207
1 files changed, 34 insertions, 173 deletions
diff --git a/src/cmd/compile/internal/pgo/irgraph.go b/src/cmd/compile/internal/pgo/irgraph.go
index 9a7dadfe25..96485e33ab 100644
--- a/src/cmd/compile/internal/pgo/irgraph.go
+++ b/src/cmd/compile/internal/pgo/irgraph.go
@@ -41,20 +41,16 @@
package pgo
import (
- "bufio"
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
+ "cmd/compile/internal/pgo/internal/graph"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
- "cmd/internal/bio"
"errors"
"fmt"
"internal/profile"
- "log"
"os"
"sort"
- "strconv"
- "strings"
)
// IRGraph is a call graph with nodes pointing to IRs of functions and edges
@@ -109,7 +105,6 @@ type NamedCallEdge struct {
CallerName string
CalleeName string
CallSiteOffset int // Line offset from function start line.
- CallStartLine int // Start line of the function. Can be 0 which means missing.
}
// NamedEdgeMap contains all unique call edges in the profile and their
@@ -144,52 +139,8 @@ type Profile struct {
WeightedCG *IRGraph
}
-var wantHdr = "GO PREPROFILE V1\n"
-
-func isPreProfileFile(filename string) (bool, error) {
- file, err := bio.Open(filename)
- if err != nil {
- return false, err
- }
- defer file.Close()
-
- /* check the header */
- line, err := file.ReadString('\n')
- if err != nil {
- return false, err
- }
-
- if wantHdr == line {
- return true, nil
- }
- return false, nil
-}
-
-// New generates a profile-graph from the profile or pre-processed profile.
+// New generates a profile-graph from the profile.
func New(profileFile string) (*Profile, error) {
- var profile *Profile
- var err error
- isPreProf, err := isPreProfileFile(profileFile)
- if err != nil {
- return nil, fmt.Errorf("error opening profile: %w", err)
- }
- if !isPreProf {
- profile, err = processProto(profileFile)
- if err != nil {
- log.Fatalf("%s: PGO error: %v", profileFile, err)
- }
- } else {
- profile, err = processPreprof(profileFile)
- if err != nil {
- log.Fatalf("%s: Preprocessed PGO error: %v", profileFile, 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)
@@ -224,7 +175,7 @@ func processProto(profileFile string) (*Profile, error) {
return nil, fmt.Errorf(`profile does not contain a sample index with value/type "samples/count" or cpu/nanoseconds"`)
}
- g := profile.NewGraph(p, &profile.Options{
+ g := graph.NewGraph(p, &graph.Options{
SampleValue: func(v []int64) int64 { return v[valueIndex] },
})
@@ -247,130 +198,11 @@ func processProto(profileFile string) (*Profile, error) {
}, nil
}
-// processPreprof generates a profile-graph from the pre-procesed profile.
-func processPreprof(preprofileFile string) (*Profile, error) {
- namedEdgeMap, totalWeight, err := createNamedEdgeMapFromPreprocess(preprofileFile)
- if err != nil {
- return nil, err
- }
-
- if totalWeight == 0 {
- return nil, nil // accept but ignore profile with no samples.
- }
-
- // Create package-level call graph with weights from profile and IR.
- wg := createIRGraph(namedEdgeMap)
-
- return &Profile{
- TotalWeight: totalWeight,
- NamedEdgeMap: namedEdgeMap,
- WeightedCG: wg,
- }, nil
-}
-
-func postProcessNamedEdgeMap(weight map[NamedCallEdge]int64, weightVal int64) (edgeMap NamedEdgeMap, totalWeight int64, err error) {
- if weightVal == 0 {
- return NamedEdgeMap{}, 0, nil // accept but ignore profile with no samples.
- }
- byWeight := make([]NamedCallEdge, 0, len(weight))
- for namedEdge := range weight {
- byWeight = append(byWeight, namedEdge)
- }
- sort.Slice(byWeight, func(i, j int) bool {
- ei, ej := byWeight[i], byWeight[j]
- if wi, wj := weight[ei], weight[ej]; wi != wj {
- return wi > wj // want larger weight first
- }
- // same weight, order by name/line number
- if ei.CallerName != ej.CallerName {
- return ei.CallerName < ej.CallerName
- }
- if ei.CalleeName != ej.CalleeName {
- return ei.CalleeName < ej.CalleeName
- }
- return ei.CallSiteOffset < ej.CallSiteOffset
- })
-
- edgeMap = NamedEdgeMap{
- Weight: weight,
- ByWeight: byWeight,
- }
-
- totalWeight = weightVal
-
- return edgeMap, totalWeight, nil
-}
-
-// 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 {
- log.Fatal("preprofile: failed to open file " + preprofileFile)
- return
- }
- defer readFile.Close()
-
- fileScanner := bufio.NewScanner(readFile)
- fileScanner.Split(bufio.ScanLines)
- weight := make(map[NamedCallEdge]int64)
-
- if !fileScanner.Scan() {
- log.Fatal("fail to parse preprocessed profile: missing header")
- return
- }
- if fileScanner.Text()+"\n" != wantHdr {
- log.Fatal("fail to parse preprocessed profile: mismatched header")
- return
- }
-
- for fileScanner.Scan() {
- readStr := fileScanner.Text()
-
- callerName := readStr
-
- if !fileScanner.Scan() {
- log.Fatal("fail to parse preprocessed profile: missing callee")
- return
- }
- calleeName := fileScanner.Text()
-
- if !fileScanner.Scan() {
- log.Fatal("fail to parse preprocessed profile: missing weight")
- return
- }
- readStr = fileScanner.Text()
-
- split := strings.Split(readStr, " ")
-
- if len(split) == 5 {
- co, _ := strconv.Atoi(split[0])
- cs, _ := strconv.Atoi(split[1])
-
- namedEdge := NamedCallEdge{
- CallerName: callerName,
- CallSiteOffset: co - cs,
- }
-
- namedEdge.CalleeName = calleeName
- EWeight, _ := strconv.ParseInt(split[4], 10, 64)
-
- weight[namedEdge] += EWeight
- totalWeight += EWeight
- } else {
- log.Fatal("fail to parse preprocessed profile: mismatched fields.\n")
- }
- }
-
- return postProcessNamedEdgeMap(weight, totalWeight)
-
-}
-
// createNamedEdgeMap builds a map of callsite-callee edge weights from the
// profile-graph.
//
// Caller should ignore the profile if totalWeight == 0.
-func createNamedEdgeMap(g *profile.Graph) (edgeMap NamedEdgeMap, totalWeight int64, err error) {
+func createNamedEdgeMap(g *graph.Graph) (edgeMap NamedEdgeMap, totalWeight int64, err error) {
seenStartLine := false
// Process graph and build various node and edge maps which will
@@ -394,13 +226,42 @@ func createNamedEdgeMap(g *profile.Graph) (edgeMap NamedEdgeMap, totalWeight int
}
}
+ if totalWeight == 0 {
+ return NamedEdgeMap{}, 0, nil // accept but ignore profile with no samples.
+ }
+
if !seenStartLine {
// TODO(prattmic): If Function.start_line is missing we could
// fall back to using absolute line numbers, which is better
// than nothing.
return NamedEdgeMap{}, 0, fmt.Errorf("profile missing Function.start_line data (Go version of profiled application too old? Go 1.20+ automatically adds this to profiles)")
}
- return postProcessNamedEdgeMap(weight, totalWeight)
+
+ byWeight := make([]NamedCallEdge, 0, len(weight))
+ for namedEdge := range weight {
+ byWeight = append(byWeight, namedEdge)
+ }
+ sort.Slice(byWeight, func(i, j int) bool {
+ ei, ej := byWeight[i], byWeight[j]
+ if wi, wj := weight[ei], weight[ej]; wi != wj {
+ return wi > wj // want larger weight first
+ }
+ // same weight, order by name/line number
+ if ei.CallerName != ej.CallerName {
+ return ei.CallerName < ej.CallerName
+ }
+ if ei.CalleeName != ej.CalleeName {
+ return ei.CalleeName < ej.CalleeName
+ }
+ return ei.CallSiteOffset < ej.CallSiteOffset
+ })
+
+ edgeMap = NamedEdgeMap{
+ Weight: weight,
+ ByWeight: byWeight,
+ }
+
+ return edgeMap, totalWeight, nil
}
// initializeIRGraph builds the IRGraph by visiting all the ir.Func in decl list