go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/os/resources/procfs/cpu_info.go (about)

     1  // Copyright (c) Mondoo, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package procfs
     5  
     6  import (
     7  	"bufio"
     8  	"io"
     9  	"strconv"
    10  	"strings"
    11  
    12  	"github.com/rs/zerolog/log"
    13  )
    14  
    15  type CpuInfo struct {
    16  	Processors []Processor `json:"processors"`
    17  }
    18  
    19  type Processor struct {
    20  	Id              uint     `json:"id"`
    21  	VendorID        string   `json:"vendorID"`
    22  	CPUFamily       string   `json:"cpuFamily"`
    23  	Model           string   `json:"model"`
    24  	ModelName       string   `json:"modelName"`
    25  	Stepping        string   `json:"stepping"`
    26  	Microcode       string   `json:"microcode"`
    27  	CPUMHz          float64  `json:"cpuMhz"`
    28  	CacheSize       int64    `json:"cacheSize"`
    29  	PhysicalID      uint     `json:"physicalID"`
    30  	Siblings        uint     `json:"siblings"`
    31  	CoreID          uint     `json:"coreId"`
    32  	CPUCores        uint     `json:"cpuCores"`
    33  	ApicID          string   `json:"apicID"`
    34  	InitialApicID   string   `json:"initialApicID"`
    35  	FPU             string   `json:"fpu"`
    36  	FPUException    string   `json:"fpuException"`
    37  	CpuIDLevel      uint     `json:"cpuIdLevel"`
    38  	WP              string   `json:"wp"`
    39  	Flags           []string `json:"flags"`
    40  	Bugs            []string `json:"bugs"`
    41  	BogoMips        float64  `json:"bogoMips"`
    42  	CLFlushSize     uint     `json:"clflushSize"`
    43  	CacheAlignment  uint     `json:"cacheAlignment"`
    44  	AddressSizes    string   `json:"addressSizes"`
    45  	PowerManagement string   `json:"powerManagement"`
    46  }
    47  
    48  // different cpu platforms return a different list, problem is also that if you use an arm container on qemu,
    49  // proc fs will still return the information from x64. To make those cases work, we use the same parser for all
    50  // different cpu architectures
    51  func ParseCpuInfo(r io.Reader) (*CpuInfo, error) {
    52  	scanner := bufio.NewScanner(r)
    53  
    54  	cpuinfo := CpuInfo{
    55  		Processors: []Processor{},
    56  	}
    57  	i := -1 // first processor will start with index 0 then
    58  
    59  	for scanner.Scan() {
    60  		line := scanner.Text()
    61  		if strings.TrimSpace(line) == "" {
    62  			continue
    63  		}
    64  
    65  		if !strings.Contains(line, ":") {
    66  			log.Warn().Str("line", line).Msg("invalid cpuinfo line")
    67  			continue
    68  		}
    69  
    70  		// split the line to get the key value pair
    71  		entry := strings.SplitN(line, ": ", 2)
    72  
    73  		switch strings.TrimSpace(entry[0]) {
    74  		case "processor":
    75  			// start a next processor and append the new info to the full list
    76  			cpuinfo.Processors = append(cpuinfo.Processors, Processor{})
    77  			i++
    78  			v, err := strconv.ParseUint(entry[1], 0, 32)
    79  			if err != nil {
    80  				return nil, err
    81  			}
    82  			cpuinfo.Processors[i].Id = uint(v)
    83  		case "vendor", "vendor_id":
    84  			cpuinfo.Processors[i].VendorID = entry[1]
    85  		case "cpu family":
    86  			cpuinfo.Processors[i].CPUFamily = entry[1]
    87  		case "model":
    88  			cpuinfo.Processors[i].Model = entry[1]
    89  		case "model name":
    90  			cpuinfo.Processors[i].ModelName = entry[1]
    91  		case "stepping":
    92  			cpuinfo.Processors[i].Stepping = entry[1]
    93  		case "microcode":
    94  			cpuinfo.Processors[i].Microcode = entry[1]
    95  		case "cpu MHz":
    96  			v, err := strconv.ParseFloat(entry[1], 64)
    97  			if err != nil {
    98  				return nil, err
    99  			}
   100  			cpuinfo.Processors[i].CPUMHz = v
   101  		case "cache size":
   102  			raw := strings.TrimSpace(entry[1])
   103  			value := strings.Split(raw, " ")
   104  
   105  			v, err := strconv.ParseInt(value[0], 10, 64)
   106  			if err != nil {
   107  				return nil, err
   108  			}
   109  			// we expect cache size to be in kb
   110  			if strings.HasSuffix(strings.ToLower(value[1]), "mb") {
   111  				v = v * 1024
   112  			}
   113  			cpuinfo.Processors[i].CacheSize = v
   114  		case "physical id":
   115  			v, err := strconv.ParseUint(entry[1], 0, 32)
   116  			if err != nil {
   117  				return nil, err
   118  			}
   119  			cpuinfo.Processors[i].PhysicalID = uint(v)
   120  		case "siblings":
   121  			v, err := strconv.ParseUint(entry[1], 0, 32)
   122  			if err != nil {
   123  				return nil, err
   124  			}
   125  			cpuinfo.Processors[i].Siblings = uint(v)
   126  		case "core id":
   127  			v, err := strconv.ParseUint(entry[1], 0, 32)
   128  			if err != nil {
   129  				return nil, err
   130  			}
   131  			cpuinfo.Processors[i].CoreID = uint(v)
   132  		case "cpu cores":
   133  			v, err := strconv.ParseUint(entry[1], 0, 32)
   134  			if err != nil {
   135  				return nil, err
   136  			}
   137  			cpuinfo.Processors[i].CPUCores = uint(v)
   138  		case "apicid":
   139  			cpuinfo.Processors[i].ApicID = entry[1]
   140  		case "initial apicid":
   141  			cpuinfo.Processors[i].InitialApicID = entry[1]
   142  		case "fpu":
   143  			cpuinfo.Processors[i].FPU = entry[1]
   144  		case "fpu_exception":
   145  			cpuinfo.Processors[i].FPUException = entry[1]
   146  		case "cpuid level":
   147  			v, err := strconv.ParseUint(entry[1], 0, 32)
   148  			if err != nil {
   149  				return nil, err
   150  			}
   151  			cpuinfo.Processors[i].CpuIDLevel = uint(v)
   152  		case "wp":
   153  			cpuinfo.Processors[i].WP = entry[1]
   154  		case "flags", "Features": // arm flags
   155  			cpuinfo.Processors[i].Flags = strings.Fields(entry[1])
   156  		case "bugs":
   157  			cpuinfo.Processors[i].Bugs = strings.Fields(entry[1])
   158  		case "bogomips", "BogoMIPS": // x64 & arm
   159  			v, err := strconv.ParseFloat(entry[1], 64)
   160  			if err != nil {
   161  				return nil, err
   162  			}
   163  			cpuinfo.Processors[i].BogoMips = v
   164  		case "clflush size":
   165  			v, err := strconv.ParseUint(entry[1], 0, 32)
   166  			if err != nil {
   167  				return nil, err
   168  			}
   169  			cpuinfo.Processors[i].CLFlushSize = uint(v)
   170  		case "cache_alignment":
   171  			v, err := strconv.ParseUint(entry[1], 0, 32)
   172  			if err != nil {
   173  				return nil, err
   174  			}
   175  			cpuinfo.Processors[i].CacheAlignment = uint(v)
   176  		case "address sizes":
   177  			cpuinfo.Processors[i].AddressSizes = entry[1]
   178  		case "power management":
   179  			cpuinfo.Processors[i].PowerManagement = entry[1]
   180  		}
   181  	}
   182  
   183  	return &cpuinfo, nil
   184  }