Source file
src/runtime/align_test.go
Documentation: runtime
1
2
3
4
5 package runtime_test
6
7 import (
8 "go/ast"
9 "go/build"
10 "go/importer"
11 "go/parser"
12 "go/printer"
13 "go/token"
14 "go/types"
15 "internal/testenv"
16 "os"
17 "regexp"
18 "runtime"
19 "strings"
20 "testing"
21 )
22
23
24
25 func TestAtomicAlignment(t *testing.T) {
26 testenv.MustHaveGoBuild(t)
27
28
29
30 checked := map[string]bool{}
31 x, err := os.ReadFile("./align_runtime_test.go")
32 if err != nil {
33 t.Fatalf("read failed: %v", err)
34 }
35 fieldDesc := map[int]string{}
36 r := regexp.MustCompile(`unsafe[.]Offsetof[(](\w+){}[.](\w+)[)]`)
37 matches := r.FindAllStringSubmatch(string(x), -1)
38 for i, v := range matches {
39 checked["field runtime."+v[1]+"."+v[2]] = true
40 fieldDesc[i] = v[1] + "." + v[2]
41 }
42 varDesc := map[int]string{}
43 r = regexp.MustCompile(`unsafe[.]Pointer[(]&(\w+)[)]`)
44 matches = r.FindAllStringSubmatch(string(x), -1)
45 for i, v := range matches {
46 checked["var "+v[1]] = true
47 varDesc[i] = v[1]
48 }
49
50
51 for i, d := range runtime.AtomicFields {
52 if d%8 != 0 {
53 t.Errorf("field alignment of %s failed: offset is %d", fieldDesc[i], d)
54 }
55 }
56 for i, p := range runtime.AtomicVariables {
57 if uintptr(p)%8 != 0 {
58 t.Errorf("variable alignment of %s failed: address is %x", varDesc[i], p)
59 }
60 }
61
62
63
64
65
66
67 fset := token.NewFileSet()
68 m, err := parser.ParseDir(fset, ".", nil, 0)
69 if err != nil {
70 t.Fatalf("parsing runtime failed: %v", err)
71 }
72 pkg := m["runtime"]
73
74
75 fileMap := map[string]bool{}
76 for _, f := range buildableFiles(t, ".") {
77 fileMap[f] = true
78 }
79 var files []*ast.File
80 for fname, f := range pkg.Files {
81 if fileMap[fname] {
82 files = append(files, f)
83 }
84 }
85
86
87 var info types.Info
88 info.Types = map[ast.Expr]types.TypeAndValue{}
89 conf := types.Config{Importer: importer.Default()}
90 _, err = conf.Check("runtime", fset, files, &info)
91 if err != nil {
92 t.Fatalf("typechecking runtime failed: %v", err)
93 }
94
95
96 v := Visitor{t: t, fset: fset, types: info.Types, checked: checked}
97 ast.Walk(&v, pkg)
98 }
99
100 type Visitor struct {
101 fset *token.FileSet
102 types map[ast.Expr]types.TypeAndValue
103 checked map[string]bool
104 t *testing.T
105 }
106
107 func (v *Visitor) Visit(n ast.Node) ast.Visitor {
108 c, ok := n.(*ast.CallExpr)
109 if !ok {
110 return v
111 }
112 f, ok := c.Fun.(*ast.SelectorExpr)
113 if !ok {
114 return v
115 }
116 p, ok := f.X.(*ast.Ident)
117 if !ok {
118 return v
119 }
120 if p.Name != "atomic" {
121 return v
122 }
123 if !strings.HasSuffix(f.Sel.Name, "64") {
124 return v
125 }
126
127 a := c.Args[0]
128
129
130
131
132
133 if u, ok := a.(*ast.UnaryExpr); ok && u.Op == token.AND {
134 v.checkAddr(u.X)
135 return v
136 }
137
138
139 v.t.Logf("unchecked atomic operation %s %v", v.fset.Position(n.Pos()), v.print(n))
140
141 return v
142 }
143
144
145 func (v *Visitor) checkAddr(n ast.Node) {
146 switch n := n.(type) {
147 case *ast.IndexExpr:
148
149 v.checkAddr(n.X)
150 return
151 case *ast.Ident:
152 key := "var " + v.print(n)
153 if !v.checked[key] {
154 v.t.Errorf("unchecked variable %s %s", v.fset.Position(n.Pos()), key)
155 }
156 return
157 case *ast.SelectorExpr:
158 t := v.types[n.X].Type
159 if t == nil {
160
161
162 return
163 }
164 if p, ok := t.(*types.Pointer); ok {
165
166
167 t = p.Elem()
168 } else {
169 v.checkAddr(n.X)
170 }
171 if t.Underlying() == t {
172 v.t.Errorf("analysis can't handle unnamed type %s %v", v.fset.Position(n.Pos()), t)
173 }
174 key := "field " + t.String() + "." + n.Sel.Name
175 if !v.checked[key] {
176 v.t.Errorf("unchecked field %s %s", v.fset.Position(n.Pos()), key)
177 }
178 default:
179 v.t.Errorf("unchecked atomic address %s %v", v.fset.Position(n.Pos()), v.print(n))
180
181 }
182 }
183
184 func (v *Visitor) print(n ast.Node) string {
185 var b strings.Builder
186 printer.Fprint(&b, v.fset, n)
187 return b.String()
188 }
189
190
191
192 func buildableFiles(t *testing.T, dir string) []string {
193 ctxt := build.Default
194 ctxt.CgoEnabled = true
195 pkg, err := ctxt.ImportDir(dir, 0)
196 if err != nil {
197 t.Fatalf("can't find buildable files: %v", err)
198 }
199 return pkg.GoFiles
200 }
201
View as plain text