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 }