github.com/vmware/govmomi@v0.51.0/cli/device/model/tree.go (about) 1 // © Broadcom. All Rights Reserved. 2 // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. 3 // SPDX-License-Identifier: Apache-2.0 4 5 package model 6 7 import ( 8 "context" 9 "flag" 10 "fmt" 11 "reflect" 12 "sort" 13 "unsafe" 14 15 "github.com/xlab/treeprint" 16 17 "github.com/vmware/govmomi/cli" 18 "github.com/vmware/govmomi/vim25/types" 19 ) 20 21 type tree struct { 22 backings bool 23 } 24 25 func init() { 26 cli.Register("device.model.tree", &tree{}) 27 } 28 29 func (cmd *tree) Register(ctx context.Context, f *flag.FlagSet) { 30 f.BoolVar(&cmd.backings, "backings", false, "Print the devices backings") 31 } 32 33 func (cmd *tree) Description() string { 34 return `Print the device model as a tree. 35 36 Examples: 37 govc device.model.tree 38 govc device.model.tree VirtualEthernetCard 39 govc device.model.tree -backings 40 govc device.model.tree -backings VirtualDiskRawDiskVer2BackingInfo` 41 } 42 43 func (cmd *tree) Process(ctx context.Context) error { 44 return nil 45 } 46 47 func (cmd *tree) Run(ctx context.Context, f *flag.FlagSet) error { 48 49 typeName := f.Arg(0) 50 51 var node treeprint.Tree 52 if cmd.backings { 53 node = getTree[ 54 types.BaseVirtualDeviceBackingInfo, 55 types.VirtualDeviceBackingInfo, 56 ]() 57 } else { 58 node = getTree[ 59 types.BaseVirtualDevice, 60 types.VirtualDevice, 61 ]() 62 } 63 64 if typeName != "" { 65 var found treeprint.Tree 66 node.VisitAll(func(n *treeprint.Node) { 67 if n.Value == typeName { 68 found = n 69 } 70 }) 71 if found == nil { 72 return fmt.Errorf("%q not found", typeName) 73 } 74 node = found 75 node = node.Branch() 76 } 77 78 fmt.Print(node.String()) 79 80 return nil 81 } 82 83 //go:linkname typelinks reflect.typelinks 84 func typelinks() (sections []unsafe.Pointer, offset [][]int32) 85 86 //go:linkname add reflect.add 87 func add(p unsafe.Pointer, x uintptr, whySafe string) unsafe.Pointer 88 89 // typeEmbeds returns true if a embeds any of the b's. 90 func typeEmbeds(a reflect.Type, b ...reflect.Type) bool { 91 for i := range b { 92 if _, ok := a.FieldByName(b[i].Name()); ok { 93 return true 94 } 95 } 96 return false 97 } 98 99 // typeEmbeddedBy returns true if a is embedded by any of the b's. 100 func typeEmbeddedBy(a reflect.Type, b ...reflect.Type) bool { 101 for i := range b { 102 if _, ok := b[i].FieldByName(a.Name()); ok { 103 return true 104 } 105 } 106 return false 107 } 108 109 func getTree[T, K any]() treeprint.Tree { 110 var ( 111 rootObj K 112 rootType = reflect.TypeOf(rootObj) 113 allTypes = []reflect.Type{} 114 embedsTypeMap = map[reflect.Type][]reflect.Type{} 115 rootIfaceType = reflect.TypeOf((*T)(nil)).Elem() 116 ) 117 118 sections, offsets := typelinks() 119 for i := range sections { 120 base := sections[i] 121 for _, offset := range offsets[i] { 122 typeAddr := add(base, uintptr(offset), "") 123 typ3 := reflect.TypeOf(*(*any)(unsafe.Pointer(&typeAddr))) 124 if typ3.Implements(rootIfaceType) { 125 realType := reflect.Zero(typ3.Elem()).Type() 126 allTypes = append(allTypes, realType) 127 embedsTypeMap[realType] = []reflect.Type{} 128 } 129 } 130 } 131 132 // Create the child->parents map. 133 for i := range allTypes { 134 a := allTypes[i] 135 for b := range embedsTypeMap { 136 if typeEmbeds(a, b) { 137 embedsTypeMap[a] = append(embedsTypeMap[a], b) 138 } 139 } 140 } 141 142 // Each child should have a single parent. 143 for child, parents := range embedsTypeMap { 144 notAncestors := []reflect.Type{} 145 for i := range parents { 146 p := parents[i] 147 if !typeEmbeddedBy(p, parents...) { 148 notAncestors = append(notAncestors, p) 149 } 150 } 151 embedsTypeMap[child] = notAncestors 152 } 153 154 // Create the parent->children map. 155 typeMap := map[string][]string{} 156 for child, parents := range embedsTypeMap { 157 for i := range parents { 158 p := parents[i] 159 typeMap[p.Name()] = append(typeMap[p.Name()], child.Name()) 160 } 161 } 162 163 // Sort the children for each parent by name. 164 for _, children := range typeMap { 165 sort.Strings(children) 166 } 167 168 var buildTree func(parent string, tree treeprint.Tree) treeprint.Tree 169 buildTree = func(parent string, tree treeprint.Tree) treeprint.Tree { 170 children := typeMap[parent] 171 for i := range children { 172 child := children[i] 173 if _, childIsParentToo := typeMap[child]; childIsParentToo { 174 buildTree(child, tree.AddBranch(child)) 175 } else { 176 tree.AddNode(child) 177 } 178 } 179 return tree 180 } 181 182 return buildTree(rootType.Name(), treeprint.NewWithRoot(rootType.Name())) 183 }