github.com/jaypipes/ghw@v0.21.1/pkg/topology/topology.go (about) 1 // 2 // Use and distribution licensed under the Apache license version 2. 3 // 4 // See the COPYING file in the root project directory for full text. 5 // 6 7 package topology 8 9 import ( 10 "encoding/json" 11 "fmt" 12 "sort" 13 "strconv" 14 "strings" 15 16 "github.com/jaypipes/ghw/pkg/context" 17 "github.com/jaypipes/ghw/pkg/cpu" 18 "github.com/jaypipes/ghw/pkg/marshal" 19 "github.com/jaypipes/ghw/pkg/memory" 20 "github.com/jaypipes/ghw/pkg/option" 21 ) 22 23 // Architecture describes the overall hardware architecture. It can be either 24 // Symmetric Multi-Processor (SMP) or Non-Uniform Memory Access (NUMA) 25 type Architecture int 26 27 const ( 28 // SMP is a Symmetric Multi-Processor system 29 ArchitectureSMP Architecture = iota 30 // NUMA is a Non-Uniform Memory Access system 31 ArchitectureNUMA 32 ) 33 34 const ( 35 // DEPRECATED: please use ArchitectureSMP. 36 // TODO(jaypipes): Remove before v1.0 37 ARCHITECTURE_SMP = ArchitectureSMP 38 // DEPRECATED: please use ArchitectureNUMA. 39 // TODO(jaypipes): Remove before v1.0 40 ARCHITECTURE_NUMA = ArchitectureNUMA 41 ) 42 43 var ( 44 architectureString = map[Architecture]string{ 45 ArchitectureSMP: "SMP", 46 ArchitectureNUMA: "NUMA", 47 } 48 49 // NOTE(fromani): the keys are all lowercase and do not match 50 // the keys in the opposite table `architectureString`. 51 // This is done because of the choice we made in 52 // Architecture:MarshalJSON. 53 // We use this table only in UnmarshalJSON, so it should be OK. 54 stringArchitecture = map[string]Architecture{ 55 "smp": ArchitectureSMP, 56 "numa": ArchitectureNUMA, 57 } 58 ) 59 60 func (a Architecture) String() string { 61 return architectureString[a] 62 } 63 64 // NOTE(jaypipes): since serialized output is as "official" as we're going to 65 // get, let's lowercase the string output when serializing, in order to 66 // "normalize" the expected serialized output 67 func (a Architecture) MarshalJSON() ([]byte, error) { 68 return []byte(strconv.Quote(strings.ToLower(a.String()))), nil 69 } 70 71 func (a *Architecture) UnmarshalJSON(b []byte) error { 72 var s string 73 if err := json.Unmarshal(b, &s); err != nil { 74 return err 75 } 76 key := strings.ToLower(s) 77 val, ok := stringArchitecture[key] 78 if !ok { 79 return fmt.Errorf("unknown architecture: %q", key) 80 } 81 *a = val 82 return nil 83 } 84 85 // Node is an abstract construct representing a collection of processors and 86 // various levels of memory cache that those processors share. In a NUMA 87 // architecture, there are multiple NUMA nodes, abstracted here as multiple 88 // Node structs. In an SMP architecture, a single Node will be available in the 89 // Info struct and this single struct can be used to describe the levels of 90 // memory caching available to the single physical processor package's physical 91 // processor cores 92 type Node struct { 93 ID int `json:"id"` 94 Cores []*cpu.ProcessorCore `json:"cores"` 95 Caches []*memory.Cache `json:"caches"` 96 Distances []int `json:"distances"` 97 Memory *memory.Area `json:"memory"` 98 } 99 100 func (n *Node) String() string { 101 return fmt.Sprintf( 102 "node #%d (%d cores)", 103 n.ID, 104 len(n.Cores), 105 ) 106 } 107 108 // Info describes the system topology for the host hardware 109 type Info struct { 110 ctx *context.Context 111 Architecture Architecture `json:"architecture"` 112 Nodes []*Node `json:"nodes"` 113 } 114 115 // New returns a pointer to an Info struct that contains information about the 116 // NUMA topology on the host system 117 func New(opts ...*option.Option) (*Info, error) { 118 merged := option.Merge(opts...) 119 ctx := context.New(merged) 120 info := &Info{ctx: ctx} 121 var err error 122 if context.Exists(merged) { 123 err = info.load() 124 } else { 125 err = ctx.Do(info.load) 126 } 127 if err != nil { 128 return nil, err 129 } 130 for _, node := range info.Nodes { 131 sort.Sort(memory.SortByCacheLevelTypeFirstProcessor(node.Caches)) 132 } 133 return info, nil 134 } 135 136 func (i *Info) String() string { 137 archStr := "SMP" 138 if i.Architecture == ArchitectureNUMA { 139 archStr = "NUMA" 140 } 141 res := fmt.Sprintf( 142 "topology %s (%d nodes)", 143 archStr, 144 len(i.Nodes), 145 ) 146 return res 147 } 148 149 // simple private struct used to encapsulate topology information in a 150 // top-level "topology" YAML/JSON map/object key 151 type topologyPrinter struct { 152 Info *Info `json:"topology"` 153 } 154 155 // YAMLString returns a string with the topology information formatted as YAML 156 // under a top-level "topology:" key 157 func (i *Info) YAMLString() string { 158 return marshal.SafeYAML(i.ctx, topologyPrinter{i}) 159 } 160 161 // JSONString returns a string with the topology information formatted as JSON 162 // under a top-level "topology:" key 163 func (i *Info) JSONString(indent bool) string { 164 return marshal.SafeJSON(i.ctx, topologyPrinter{i}, indent) 165 }