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  }