github.com/cilium/ebpf@v0.10.0/btf/traversal.go (about) 1 package btf 2 3 import ( 4 "fmt" 5 6 "github.com/cilium/ebpf/internal" 7 ) 8 9 // Functions to traverse a cyclic graph of types. The below was very useful: 10 // https://eli.thegreenplace.net/2015/directed-graph-traversal-orderings-and-applications-to-data-flow-analysis/#post-order-and-reverse-post-order 11 12 type postorderIterator struct { 13 // Iteration skips types for which this function returns true. 14 skip func(Type) bool 15 // The root type. May be nil if skip(root) is true. 16 root Type 17 18 // Contains types which need to be either walked or passed to the callback. 19 types typeDeque 20 // Contains a boolean whether the type has been walked or not. 21 walked internal.Deque[bool] 22 // The set of types which has been pushed onto types. 23 pushed map[Type]struct{} 24 25 // The current type. Only valid after a call to Next(). 26 Type Type 27 } 28 29 // postorderTraversal calls fn for all types reachable from root. 30 // 31 // fn is invoked on children of root before root itself. 32 // 33 // Types for which skip returns true are ignored. skip may be nil. 34 func postorderTraversal(root Type, skip func(Type) (skip bool)) postorderIterator { 35 // Avoid allocations for the common case of a skipped root. 36 if skip != nil && skip(root) { 37 return postorderIterator{} 38 } 39 40 po := postorderIterator{root: root, skip: skip} 41 walkType(root, po.push) 42 43 return po 44 } 45 46 func (po *postorderIterator) push(t *Type) { 47 if _, ok := po.pushed[*t]; ok || *t == po.root { 48 return 49 } 50 51 if po.skip != nil && po.skip(*t) { 52 return 53 } 54 55 if po.pushed == nil { 56 // Lazily allocate pushed to avoid an allocation for Types without children. 57 po.pushed = make(map[Type]struct{}) 58 } 59 60 po.pushed[*t] = struct{}{} 61 po.types.Push(t) 62 po.walked.Push(false) 63 } 64 65 // Next returns true if there is another Type to traverse. 66 func (po *postorderIterator) Next() bool { 67 for !po.types.Empty() { 68 t := po.types.Pop() 69 70 if !po.walked.Pop() { 71 // Push the type again, so that we re-evaluate it in done state 72 // after all children have been handled. 73 po.types.Push(t) 74 po.walked.Push(true) 75 76 // Add all direct children to todo. 77 walkType(*t, po.push) 78 } else { 79 // We've walked this type previously, so we now know that all 80 // children have been handled. 81 po.Type = *t 82 return true 83 } 84 } 85 86 // Only return root once. 87 po.Type, po.root = po.root, nil 88 return po.Type != nil 89 } 90 91 // walkType calls fn on each child of typ. 92 func walkType(typ Type, fn func(*Type)) { 93 // Explicitly type switch on the most common types to allow the inliner to 94 // do its work. This avoids allocating intermediate slices from walk() on 95 // the heap. 96 switch v := typ.(type) { 97 case *Void, *Int, *Enum, *Fwd, *Float: 98 // No children to traverse. 99 case *Pointer: 100 fn(&v.Target) 101 case *Array: 102 fn(&v.Index) 103 fn(&v.Type) 104 case *Struct: 105 for i := range v.Members { 106 fn(&v.Members[i].Type) 107 } 108 case *Union: 109 for i := range v.Members { 110 fn(&v.Members[i].Type) 111 } 112 case *Typedef: 113 fn(&v.Type) 114 case *Volatile: 115 fn(&v.Type) 116 case *Const: 117 fn(&v.Type) 118 case *Restrict: 119 fn(&v.Type) 120 case *Func: 121 fn(&v.Type) 122 case *FuncProto: 123 fn(&v.Return) 124 for i := range v.Params { 125 fn(&v.Params[i].Type) 126 } 127 case *Var: 128 fn(&v.Type) 129 case *Datasec: 130 for i := range v.Vars { 131 fn(&v.Vars[i].Type) 132 } 133 case *declTag: 134 fn(&v.Type) 135 case *typeTag: 136 fn(&v.Type) 137 case *cycle: 138 // cycle has children, but we ignore them deliberately. 139 default: 140 panic(fmt.Sprintf("don't know how to walk Type %T", v)) 141 } 142 }