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