github.com/cilium/ebpf@v0.15.1-0.20240517100537-8079b37aa138/btf/traversal.go (about)

     1  package btf
     2  
     3  import (
     4  	"fmt"
     5  )
     6  
     7  // Functions to traverse a cyclic graph of types. The below was very useful:
     8  // https://eli.thegreenplace.net/2015/directed-graph-traversal-orderings-and-applications-to-data-flow-analysis/#post-order-and-reverse-post-order
     9  
    10  // Visit all types reachable from root in postorder.
    11  //
    12  // Traversal stops if yield returns false.
    13  //
    14  // Returns false if traversal was aborted.
    15  func visitInPostorder(root Type, visited map[Type]struct{}, yield func(typ Type) bool) bool {
    16  	if _, ok := visited[root]; ok {
    17  		return true
    18  	}
    19  	if visited == nil {
    20  		visited = make(map[Type]struct{})
    21  	}
    22  	visited[root] = struct{}{}
    23  
    24  	cont := children(root, func(child *Type) bool {
    25  		return visitInPostorder(*child, visited, yield)
    26  	})
    27  	if !cont {
    28  		return false
    29  	}
    30  
    31  	return yield(root)
    32  }
    33  
    34  // children calls yield on each child of typ.
    35  //
    36  // Traversal stops if yield returns false.
    37  //
    38  // Returns false if traversal was aborted.
    39  func children(typ Type, yield func(child *Type) bool) bool {
    40  	// Explicitly type switch on the most common types to allow the inliner to
    41  	// do its work. This avoids allocating intermediate slices from walk() on
    42  	// the heap.
    43  	switch v := typ.(type) {
    44  	case *Void, *Int, *Enum, *Fwd, *Float:
    45  		// No children to traverse.
    46  	case *Pointer:
    47  		if !yield(&v.Target) {
    48  			return false
    49  		}
    50  	case *Array:
    51  		if !yield(&v.Index) {
    52  			return false
    53  		}
    54  		if !yield(&v.Type) {
    55  			return false
    56  		}
    57  	case *Struct:
    58  		for i := range v.Members {
    59  			if !yield(&v.Members[i].Type) {
    60  				return false
    61  			}
    62  		}
    63  	case *Union:
    64  		for i := range v.Members {
    65  			if !yield(&v.Members[i].Type) {
    66  				return false
    67  			}
    68  		}
    69  	case *Typedef:
    70  		if !yield(&v.Type) {
    71  			return false
    72  		}
    73  	case *Volatile:
    74  		if !yield(&v.Type) {
    75  			return false
    76  		}
    77  	case *Const:
    78  		if !yield(&v.Type) {
    79  			return false
    80  		}
    81  	case *Restrict:
    82  		if !yield(&v.Type) {
    83  			return false
    84  		}
    85  	case *Func:
    86  		if !yield(&v.Type) {
    87  			return false
    88  		}
    89  	case *FuncProto:
    90  		if !yield(&v.Return) {
    91  			return false
    92  		}
    93  		for i := range v.Params {
    94  			if !yield(&v.Params[i].Type) {
    95  				return false
    96  			}
    97  		}
    98  	case *Var:
    99  		if !yield(&v.Type) {
   100  			return false
   101  		}
   102  	case *Datasec:
   103  		for i := range v.Vars {
   104  			if !yield(&v.Vars[i].Type) {
   105  				return false
   106  			}
   107  		}
   108  	case *declTag:
   109  		if !yield(&v.Type) {
   110  			return false
   111  		}
   112  	case *typeTag:
   113  		if !yield(&v.Type) {
   114  			return false
   115  		}
   116  	case *cycle:
   117  		// cycle has children, but we ignore them deliberately.
   118  	default:
   119  		panic(fmt.Sprintf("don't know how to walk Type %T", v))
   120  	}
   121  
   122  	return true
   123  }