]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/test/pgo_devirtualize_test.go
cmd/compile: support lookup of functions from export data
[gostls13.git] / src / cmd / compile / internal / test / pgo_devirtualize_test.go
1 // Copyright 2023 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package test
6
7 import (
8         "bufio"
9         "fmt"
10         "internal/testenv"
11         "os"
12         "path/filepath"
13         "regexp"
14         "testing"
15 )
16
17 // testPGODevirtualize tests that specific PGO devirtualize rewrites are performed.
18 func testPGODevirtualize(t *testing.T, dir string) {
19         testenv.MustHaveGoRun(t)
20         t.Parallel()
21
22         const pkg = "example.com/pgo/devirtualize"
23
24         // Add a go.mod so we have a consistent symbol names in this temp dir.
25         goMod := fmt.Sprintf(`module %s
26 go 1.19
27 `, pkg)
28         if err := os.WriteFile(filepath.Join(dir, "go.mod"), []byte(goMod), 0644); err != nil {
29                 t.Fatalf("error writing go.mod: %v", err)
30         }
31
32         // Run the test without PGO to ensure that the test assertions are
33         // correct even in the non-optimized version.
34         cmd := testenv.CleanCmdEnv(testenv.Command(t, testenv.GoToolPath(t), "test", "."))
35         cmd.Dir = dir
36         b, err := cmd.CombinedOutput()
37         t.Logf("Test without PGO:\n%s", b)
38         if err != nil {
39                 t.Fatalf("Test failed without PGO: %v", err)
40         }
41
42         // Build the test with the profile.
43         pprof := filepath.Join(dir, "devirt.pprof")
44         gcflag := fmt.Sprintf("-gcflags=-m=2 -pgoprofile=%s -d=pgodebug=3", pprof)
45         out := filepath.Join(dir, "test.exe")
46         cmd = testenv.CleanCmdEnv(testenv.Command(t, testenv.GoToolPath(t), "test", "-o", out, gcflag, "."))
47         cmd.Dir = dir
48
49         pr, pw, err := os.Pipe()
50         if err != nil {
51                 t.Fatalf("error creating pipe: %v", err)
52         }
53         defer pr.Close()
54         cmd.Stdout = pw
55         cmd.Stderr = pw
56
57         err = cmd.Start()
58         pw.Close()
59         if err != nil {
60                 t.Fatalf("error starting go test: %v", err)
61         }
62
63         type devirtualization struct {
64                 pos    string
65                 callee string
66         }
67
68         want := []devirtualization{
69                 // ExerciseIface
70                 {
71                         pos:    "./devirt.go:101:20",
72                         callee: "mult.Mult.Multiply",
73                 },
74                 {
75                         pos:    "./devirt.go:101:39",
76                         callee: "Add.Add",
77                 },
78                 // ExerciseFuncConcrete
79                 {
80                         pos:    "./devirt.go:173:36",
81                         callee: "AddFn",
82                 },
83                 {
84                         pos:    "./devirt.go:173:15",
85                         callee: "mult.MultFn",
86                 },
87                 // ExerciseFuncField
88                 {
89                         pos:    "./devirt.go:207:35",
90                         callee: "AddFn",
91                 },
92                 {
93                         pos:    "./devirt.go:207:19",
94                         callee: "mult.MultFn",
95                 },
96                 // ExerciseFuncClosure
97                 // TODO(prattmic): Closure callees not implemented.
98                 //{
99                 //      pos:    "./devirt.go:249:27",
100                 //      callee: "AddClosure.func1",
101                 //},
102                 //{
103                 //      pos:    "./devirt.go:249:15",
104                 //      callee: "mult.MultClosure.func1",
105                 //},
106         }
107
108         got := make(map[devirtualization]struct{})
109
110         devirtualizedLine := regexp.MustCompile(`(.*): PGO devirtualizing \w+ call .* to (.*)`)
111
112         scanner := bufio.NewScanner(pr)
113         for scanner.Scan() {
114                 line := scanner.Text()
115                 t.Logf("child: %s", line)
116
117                 m := devirtualizedLine.FindStringSubmatch(line)
118                 if m == nil {
119                         continue
120                 }
121
122                 d := devirtualization{
123                         pos:    m[1],
124                         callee: m[2],
125                 }
126                 got[d] = struct{}{}
127         }
128         if err := cmd.Wait(); err != nil {
129                 t.Fatalf("error running go test: %v", err)
130         }
131         if err := scanner.Err(); err != nil {
132                 t.Fatalf("error reading go test output: %v", err)
133         }
134
135         if len(got) != len(want) {
136                 t.Errorf("mismatched devirtualization count; got %v want %v", got, want)
137         }
138         for _, w := range want {
139                 if _, ok := got[w]; ok {
140                         continue
141                 }
142                 t.Errorf("devirtualization %v missing; got %v", w, got)
143         }
144
145         // Run test with PGO to ensure the assertions are still true.
146         cmd = testenv.CleanCmdEnv(testenv.Command(t, out))
147         cmd.Dir = dir
148         b, err = cmd.CombinedOutput()
149         t.Logf("Test with PGO:\n%s", b)
150         if err != nil {
151                 t.Fatalf("Test failed without PGO: %v", err)
152         }
153 }
154
155 // TestPGODevirtualize tests that specific functions are devirtualized when PGO
156 // is applied to the exact source that was profiled.
157 func TestPGODevirtualize(t *testing.T) {
158         wd, err := os.Getwd()
159         if err != nil {
160                 t.Fatalf("error getting wd: %v", err)
161         }
162         srcDir := filepath.Join(wd, "testdata", "pgo", "devirtualize")
163
164         // Copy the module to a scratch location so we can add a go.mod.
165         dir := t.TempDir()
166         if err := os.Mkdir(filepath.Join(dir, "mult.pkg"), 0755); err != nil {
167                 t.Fatalf("error creating dir: %v", err)
168         }
169         for _, file := range []string{"devirt.go", "devirt_test.go", "devirt.pprof", filepath.Join("mult.pkg", "mult.go")} {
170                 if err := copyFile(filepath.Join(dir, file), filepath.Join(srcDir, file)); err != nil {
171                         t.Fatalf("error copying %s: %v", file, err)
172                 }
173         }
174
175         testPGODevirtualize(t, dir)
176 }