| | |
| | |
| | |
| |
|
| | package runtime_test |
| |
|
| | import ( |
| | "go/ast" |
| | "go/build" |
| | "go/importer" |
| | "go/parser" |
| | "go/printer" |
| | "go/token" |
| | "go/types" |
| | "internal/testenv" |
| | "os" |
| | "regexp" |
| | "runtime" |
| | "strings" |
| | "testing" |
| | ) |
| |
|
| | |
| | |
| | func TestAtomicAlignment(t *testing.T) { |
| | testenv.MustHaveGoBuild(t) |
| |
|
| | |
| | |
| | checked := map[string]bool{} |
| | x, err := os.ReadFile("./align_runtime_test.go") |
| | if err != nil { |
| | t.Fatalf("read failed: %v", err) |
| | } |
| | fieldDesc := map[int]string{} |
| | r := regexp.MustCompile(`unsafe[.]Offsetof[(](\w+){}[.](\w+)[)]`) |
| | matches := r.FindAllStringSubmatch(string(x), -1) |
| | for i, v := range matches { |
| | checked["field runtime."+v[1]+"."+v[2]] = true |
| | fieldDesc[i] = v[1] + "." + v[2] |
| | } |
| | varDesc := map[int]string{} |
| | r = regexp.MustCompile(`unsafe[.]Pointer[(]&(\w+)[)]`) |
| | matches = r.FindAllStringSubmatch(string(x), -1) |
| | for i, v := range matches { |
| | checked["var "+v[1]] = true |
| | varDesc[i] = v[1] |
| | } |
| |
|
| | |
| | for i, d := range runtime.AtomicFields { |
| | if d%8 != 0 { |
| | t.Errorf("field alignment of %s failed: offset is %d", fieldDesc[i], d) |
| | } |
| | } |
| | for i, p := range runtime.AtomicVariables { |
| | if uintptr(p)%8 != 0 { |
| | t.Errorf("variable alignment of %s failed: address is %x", varDesc[i], p) |
| | } |
| | } |
| |
|
| | |
| | |
| |
|
| | |
| | |
| | fset := token.NewFileSet() |
| | m, err := parser.ParseDir(fset, ".", nil, 0) |
| | if err != nil { |
| | t.Fatalf("parsing runtime failed: %v", err) |
| | } |
| | pkg := m["runtime"] |
| |
|
| | |
| | fileMap := map[string]bool{} |
| | for _, f := range buildableFiles(t, ".") { |
| | fileMap[f] = true |
| | } |
| | var files []*ast.File |
| | for fname, f := range pkg.Files { |
| | if fileMap[fname] { |
| | files = append(files, f) |
| | } |
| | } |
| |
|
| | |
| | var info types.Info |
| | info.Types = map[ast.Expr]types.TypeAndValue{} |
| | conf := types.Config{Importer: importer.Default()} |
| | _, err = conf.Check("runtime", fset, files, &info) |
| | if err != nil { |
| | t.Fatalf("typechecking runtime failed: %v", err) |
| | } |
| |
|
| | |
| | v := Visitor{t: t, fset: fset, types: info.Types, checked: checked} |
| | ast.Walk(&v, pkg) |
| | } |
| |
|
| | type Visitor struct { |
| | fset *token.FileSet |
| | types map[ast.Expr]types.TypeAndValue |
| | checked map[string]bool |
| | t *testing.T |
| | } |
| |
|
| | func (v *Visitor) Visit(n ast.Node) ast.Visitor { |
| | c, ok := n.(*ast.CallExpr) |
| | if !ok { |
| | return v |
| | } |
| | f, ok := c.Fun.(*ast.SelectorExpr) |
| | if !ok { |
| | return v |
| | } |
| | p, ok := f.X.(*ast.Ident) |
| | if !ok { |
| | return v |
| | } |
| | if p.Name != "atomic" { |
| | return v |
| | } |
| | if !strings.HasSuffix(f.Sel.Name, "64") { |
| | return v |
| | } |
| |
|
| | a := c.Args[0] |
| |
|
| | |
| | |
| | |
| |
|
| | if u, ok := a.(*ast.UnaryExpr); ok && u.Op == token.AND { |
| | v.checkAddr(u.X) |
| | return v |
| | } |
| |
|
| | |
| | v.t.Logf("unchecked atomic operation %s %v", v.fset.Position(n.Pos()), v.print(n)) |
| |
|
| | return v |
| | } |
| |
|
| | |
| | func (v *Visitor) checkAddr(n ast.Node) { |
| | switch n := n.(type) { |
| | case *ast.IndexExpr: |
| | |
| | v.checkAddr(n.X) |
| | return |
| | case *ast.Ident: |
| | key := "var " + v.print(n) |
| | if !v.checked[key] { |
| | v.t.Errorf("unchecked variable %s %s", v.fset.Position(n.Pos()), key) |
| | } |
| | return |
| | case *ast.SelectorExpr: |
| | t := v.types[n.X].Type |
| | if t == nil { |
| | |
| | |
| | return |
| | } |
| | if p, ok := t.(*types.Pointer); ok { |
| | |
| | |
| | t = p.Elem() |
| | } else { |
| | v.checkAddr(n.X) |
| | } |
| | if t.Underlying() == t { |
| | v.t.Errorf("analysis can't handle unnamed type %s %v", v.fset.Position(n.Pos()), t) |
| | } |
| | key := "field " + t.String() + "." + n.Sel.Name |
| | if !v.checked[key] { |
| | v.t.Errorf("unchecked field %s %s", v.fset.Position(n.Pos()), key) |
| | } |
| | default: |
| | v.t.Errorf("unchecked atomic address %s %v", v.fset.Position(n.Pos()), v.print(n)) |
| |
|
| | } |
| | } |
| |
|
| | func (v *Visitor) print(n ast.Node) string { |
| | var b strings.Builder |
| | printer.Fprint(&b, v.fset, n) |
| | return b.String() |
| | } |
| |
|
| | |
| | |
| | func buildableFiles(t *testing.T, dir string) []string { |
| | ctxt := build.Default |
| | ctxt.CgoEnabled = true |
| | pkg, err := ctxt.ImportDir(dir, 0) |
| | if err != nil { |
| | t.Fatalf("can't find buildable files: %v", err) |
| | } |
| | return pkg.GoFiles |
| | } |
| |
|