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  }