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