logicalProcessorsFromProcCPUInfo reads the `/proc/cpuinfo` pseudofile and returns a map, keyed by logical processor ID, of logical processor structs. `/proc/cpuinfo` files look like the following: ``` processor : 0 vendor_id : AuthenticAMD cpu family : 23 model : 8 model name : AMD Ryzen 7 2700X
( ctx context.Context, )
| 348 | // with blank line-separated blocks of colon-delimited attribute name/value |
| 349 | // pairs for a specific logical processor on the host. |
| 350 | func logicalProcessorsFromProcCPUInfo( |
| 351 | ctx context.Context, |
| 352 | ) map[int]*logicalProcessor { |
| 353 | paths := linuxpath.New(ctx) |
| 354 | r, err := os.Open(paths.ProcCpuinfo) |
| 355 | if err != nil { |
| 356 | return nil |
| 357 | } |
| 358 | defer util.SafeClose(r) |
| 359 | |
| 360 | lps := map[int]*logicalProcessor{} |
| 361 | |
| 362 | // A map of attributes describing the logical processor |
| 363 | lpAttrs := map[string]string{} |
| 364 | |
| 365 | scanner := bufio.NewScanner(r) |
| 366 | for scanner.Scan() { |
| 367 | line := strings.TrimSpace(scanner.Text()) |
| 368 | if line == "" { |
| 369 | // Output of /proc/cpuinfo has a blank newline to separate logical |
| 370 | // processors, so here we collect up all the attributes we've |
| 371 | // collected for this logical processor block |
| 372 | // s390x identifies CPUs via "cpu number", while most other |
| 373 | // architectures use "processor". |
| 374 | idStr, ok := lpAttrs["processor"] |
| 375 | if !ok { |
| 376 | idStr, ok = lpAttrs["cpu number"] |
| 377 | } |
| 378 | |
| 379 | if ok { |
| 380 | id, _ := strconv.Atoi(idStr) |
| 381 | lps[id] = &logicalProcessor{ |
| 382 | ID: id, |
| 383 | Attrs: lpAttrs, |
| 384 | } |
| 385 | // Only reset attributes after a valid processor block is saved. |
| 386 | // This ensures that shared header metadata (like vendor_id on s390x) |
| 387 | // is carried over and available to the processor entries. |
| 388 | lpAttrs = map[string]string{} |
| 389 | } else if len(lpAttrs) > 0 { |
| 390 | // s390x header check: if we have 'vendor_id' but no 'processor' ID, |
| 391 | // it's the summary block. We don't warn and we don't reset lpAttrs |
| 392 | // so the vendor_id carries over to the first actual CPU block. |
| 393 | if _, isS390Header := lpAttrs["vendor_id"]; !isS390Header { |
| 394 | log.Warn(ctx, |
| 395 | "expected to find 'processor' or 'cpu number' key "+ |
| 396 | "in /proc/cpuinfo attributes") |
| 397 | } |
| 398 | } |
| 399 | continue |
| 400 | } |
| 401 | parts := strings.SplitN(line, ":", 2) |
| 402 | if len(parts) >= 2 { |
| 403 | key := strings.TrimSpace(parts[0]) |
| 404 | value := strings.TrimSpace(parts[1]) |
| 405 | lpAttrs[key] = value |
| 406 | } |
| 407 | } |
no test coverage detected
searching dependent graphs…