]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/test/pgo_devirtualize_test.go
cmd/compile/internal/devirtualize: devirtualize methods in other packages if current...
[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         // Build the test with the profile.
33         pprof := filepath.Join(dir, "devirt.pprof")
34         gcflag := fmt.Sprintf("-gcflags=-m=2 -pgoprofile=%s -d=pgodebug=2", pprof)
35         out := filepath.Join(dir, "test.exe")
36         cmd := testenv.CleanCmdEnv(testenv.Command(t, testenv.GoToolPath(t), "build", "-o", out, gcflag, "."))
37         cmd.Dir = dir
38
39         pr, pw, err := os.Pipe()
40         if err != nil {
41                 t.Fatalf("error creating pipe: %v", err)
42         }
43         defer pr.Close()
44         cmd.Stdout = pw
45         cmd.Stderr = pw
46
47         err = cmd.Start()
48         pw.Close()
49         if err != nil {
50                 t.Fatalf("error starting go test: %v", err)
51         }
52
53         type devirtualization struct {
54                 pos    string
55                 callee string
56         }
57
58         want := []devirtualization{
59                 {
60                         pos:    "./devirt.go:61:21",
61                         callee: "mult.Mult.Multiply",
62                 },
63                 {
64                         pos:    "./devirt.go:61:31",
65                         callee: "Add.Add",
66                 },
67         }
68
69         got := make(map[devirtualization]struct{})
70
71         devirtualizedLine := regexp.MustCompile(`(.*): PGO devirtualizing call to (.*)`)
72
73         scanner := bufio.NewScanner(pr)
74         for scanner.Scan() {
75                 line := scanner.Text()
76                 t.Logf("child: %s", line)
77
78                 m := devirtualizedLine.FindStringSubmatch(line)
79                 if m == nil {
80                         continue
81                 }
82
83                 d := devirtualization{
84                         pos:    m[1],
85                         callee: m[2],
86                 }
87                 got[d] = struct{}{}
88         }
89         if err := cmd.Wait(); err != nil {
90                 t.Fatalf("error running go test: %v", err)
91         }
92         if err := scanner.Err(); err != nil {
93                 t.Fatalf("error reading go test output: %v", err)
94         }
95
96         if len(got) != len(want) {
97                 t.Errorf("mismatched devirtualization count; got %v want %v", got, want)
98         }
99         for _, w := range want {
100                 if _, ok := got[w]; ok {
101                         continue
102                 }
103                 t.Errorf("devirtualization %v missing; got %v", w, got)
104         }
105 }
106
107 // TestPGODevirtualize tests that specific functions are devirtualized when PGO
108 // is applied to the exact source that was profiled.
109 func TestPGODevirtualize(t *testing.T) {
110         wd, err := os.Getwd()
111         if err != nil {
112                 t.Fatalf("error getting wd: %v", err)
113         }
114         srcDir := filepath.Join(wd, "testdata", "pgo", "devirtualize")
115
116         // Copy the module to a scratch location so we can add a go.mod.
117         dir := t.TempDir()
118         if err := os.Mkdir(filepath.Join(dir, "mult"), 0755); err != nil {
119                 t.Fatalf("error creating dir: %v", err)
120         }
121         for _, file := range []string{"devirt.go", "devirt_test.go", "devirt.pprof", filepath.Join("mult", "mult.go")} {
122                 if err := copyFile(filepath.Join(dir, file), filepath.Join(srcDir, file)); err != nil {
123                         t.Fatalf("error copying %s: %v", file, err)
124                 }
125         }
126
127         testPGODevirtualize(t, dir)
128 }