topology.go 4.2 KB

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