github.com/aclements/go-misc@v0.0.0-20240129233631-2f6ede80790c/rtcheck/lockclass.go (about) 1 // Copyright 2016 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 main 6 7 import ( 8 "fmt" 9 "go/types" 10 "strings" 11 12 "golang.org/x/tools/go/ssa" 13 ) 14 15 // A LockClass represents a set of locks. LockClasses form equivalence 16 // classes: a given lock instance belongs to exactly one lock class. 17 // Often, a LockClass is identified by the struct type and field that 18 // embeds the lock object. It may also simply be a package-level 19 // variable. 20 type LockClass struct { 21 label string 22 isUnique bool 23 id int 24 lca *LockClassAnalysis 25 } 26 27 func (lc *LockClass) Analysis() *LockClassAnalysis { 28 return lc.lca 29 } 30 31 func (lc *LockClass) String() string { 32 if !lc.isUnique { 33 return lc.label + "*" 34 } 35 return lc.label 36 } 37 38 // IsUnique returns true if lc is inhabited by a single lock instance. 39 func (lc *LockClass) IsUnique() bool { 40 return lc.isUnique 41 } 42 43 // Id returns a small integer ID for this lock class that is unique 44 // within the LockClassAnalysis that returned this *LockClass. 45 func (lc *LockClass) Id() int { 46 return lc.id 47 } 48 49 type lockClassKey struct { 50 parent interface{} 51 field int 52 global *ssa.Global 53 typ *types.Named 54 } 55 56 type LockClassAnalysis struct { 57 classes map[lockClassKey]*LockClass 58 list []*LockClass 59 } 60 61 // Get returns the LockClass of the given ssa.Value, which must be a 62 // pointer to a global or a field address expression. If Get cannot 63 // resolve the lock class of v, it returns an error indicating why. 64 // 65 // The returned pointer uniquely identifies the lock class. That is, 66 // a.Get(x) == a.Get(y) if and only if x and y are the same lock 67 // class. 68 func (a *LockClassAnalysis) Get(v ssa.Value) (*LockClass, error) { 69 // Strip away FieldAddrs until we get to something that's a 70 // global or a *struct value. 71 label := make([]string, 0, 10) 72 var key lockClassKey 73 var isUnique bool 74 loop: 75 for { 76 switch v2 := v.(type) { 77 case *ssa.FieldAddr: 78 // TODO: How does this handle nested structs? 79 label = append(label, v2.X.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Struct).Field(v2.Field).Name()) 80 key = lockClassKey{parent: key, field: v2.Field} 81 v = v2.X 82 83 case *ssa.Global: 84 // TODO: Check formatting 85 label = append(label, v2.String()) 86 key = lockClassKey{parent: key, global: v2} 87 isUnique = true 88 break loop 89 90 default: 91 if len(label) == 0 { 92 // This wasn't a field of something, 93 // so we have no idea what its lock 94 // class is. This happens in the 95 // runtime for example in 96 // parkunlock_c, which cases an 97 // unsafe.Pointer argument to a 98 // *mutex. 99 return nil, fmt.Errorf("lock is not a field or global") 100 } 101 // This must be a *struct. Get the struct's 102 // name. 103 styp, ok := v.Type().Underlying().(*types.Pointer).Elem().(*types.Named) 104 if !ok { 105 return nil, fmt.Errorf("lock is a field of an unnamed struct") 106 } 107 sname := styp.Obj().Name() 108 label = append(label, styp.Obj().Pkg().Name()+"."+sname) 109 key = lockClassKey{parent: key, typ: styp} 110 isUnique = false 111 break loop 112 } 113 } 114 115 if a.classes == nil { 116 a.classes = make(map[lockClassKey]*LockClass) 117 } 118 if lc, ok := a.classes[key]; ok { 119 return lc, nil 120 } 121 122 for i := 0; i < len(label)/2; i++ { 123 label[i], label[len(label)-i-1] = label[len(label)-i-1], label[i] 124 } 125 lc := &LockClass{ 126 label: strings.Join(label, "."), 127 isUnique: isUnique, 128 id: len(a.list), 129 lca: a, 130 } 131 a.classes[key] = lc 132 a.list = append(a.list, lc) 133 return lc, nil 134 } 135 136 // NewLockClass returns a new lock class that is distinct from every 137 // other lock class. This can be used to model lock-like abstractions 138 // that are not actually Go objects. 139 func (a *LockClassAnalysis) NewLockClass(label string, isUnique bool) *LockClass { 140 lc := &LockClass{ 141 label: label, 142 isUnique: isUnique, 143 id: len(a.list), 144 lca: a, 145 } 146 a.list = append(a.list, lc) 147 return lc 148 } 149 150 // Lookup returns the *LockClass whose Id() is id. 151 func (a *LockClassAnalysis) Lookup(id int) *LockClass { 152 return a.list[id] 153 }