metric/system/numcpu/cpu_linux.go (50 lines of code) (raw):

// Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Elasticsearch B.V. licenses this file to you under // the Apache License, Version 2.0 (the "License"); you may // not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package numcpu import ( "errors" "fmt" "os" "strings" ) // getCPU implements NumCPU on linux // see https://www.kernel.org/doc/Documentation/admin-guide/cputopology.rst func getCPU() (int, bool, error) { // These are the files that LSCPU looks for // This will report online CPUs, which are are the logical CPUS // that are currently online and scheduleable by the system. // Some users may expect a "present" count, which reflects what // CPUs are available to the OS, online or off. // These two values will only differ in cases where CPU hotplugging is in affect. // This env var swaps between them. _, isPresent := os.LookupEnv("LINUX_CPU_COUNT_PRESENT") var cpuPath = "/sys/devices/system/cpu/online" if isPresent { cpuPath = "/sys/devices/system/cpu/present" } rawFile, err := os.ReadFile(cpuPath) // if the file doesn't exist, assume it's a support issue and not a bug if errors.Is(err, os.ErrNotExist) { return -1, false, nil } if err != nil { return -1, false, fmt.Errorf("error reading file %s: %w", cpuPath, err) } cpuCount, err := parseCPUList(string(rawFile)) if err != nil { return -1, false, fmt.Errorf("error parsing file %s: %w", cpuPath, err) } return cpuCount, true, nil } // parse the weird list files we get from sysfs func parseCPUList(raw string) (int, error) { listPart := strings.Split(raw, ",") count := 0 for _, v := range listPart { if strings.Contains(v, "-") { rangeC, err := parseCPURange(v) if err != nil { return 0, fmt.Errorf("error parsing line %s: %w", v, err) } count = count + rangeC } else { count++ } } return count, nil } func parseCPURange(cpuRange string) (int, error) { var first, last int _, err := fmt.Sscanf(cpuRange, "%d-%d", &first, &last) if err != nil { return 0, fmt.Errorf("error reading from range %s: %w", cpuRange, err) } return (last - first) + 1, nil }