blob: 1108d0a6161febf9d1eeb6c9cca222dc39eba556 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 runtime
import (
"bufio"
"os"
"runtime"
"strconv"
"strings"
)
const (
_cgroupPath = "/proc/self/cgroup"
_dockerPath = "/docker"
_kubepodsPath = "/kubepods"
_cpuPeriodPath = "/sys/fs/cgroup/cpu/cpu.cfs_period_us"
_cpuQuotaPath = "/sys/fs/cgroup/cpu/cpu.cfs_quota_us"
)
// GetCPUNum gets current os's cpu number
func GetCPUNum() int {
if isContainer() {
cpus, _ := numCPU()
return cpus
}
return runtime.NumCPU()
}
// readLinesFromFile reads the lines from a file.
func readLinesFromFile(filepath string) []string {
res := make([]string, 0)
f, err := os.Open(filepath)
if err != nil {
return res
}
defer f.Close()
buff := bufio.NewReader(f)
for {
line, _, err := buff.ReadLine()
if err != nil {
return res
}
res = append(res, string(line))
}
}
// isContainer returns true if the process is running in a container.
func isContainer() bool {
lines := readLinesFromFile(_cgroupPath)
for _, line := range lines {
if strings.Contains(line, _dockerPath) ||
strings.Contains(line, _kubepodsPath) {
return true
}
}
return false
}
// copied from https://github.com/containerd/cgroups/blob/318312a373405e5e91134d8063d04d59768a1bff/utils.go#L243
func readUint(path string) (uint64, error) {
v, err := os.ReadFile(path)
if err != nil {
return 0, err
}
return parseUint(strings.TrimSpace(string(v)), 10, 64)
}
// copied from https://github.com/containerd/cgroups/blob/318312a373405e5e91134d8063d04d59768a1bff/utils.go#L251
func parseUint(s string, base, bitSize int) (uint64, error) {
v, err := strconv.ParseUint(s, base, bitSize)
if err != nil {
intValue, intErr := strconv.ParseInt(s, base, bitSize)
// 1. Handle negative values greater than MinInt64 (and)
// 2. Handle negative values lesser than MinInt64
if intErr == nil && intValue < 0 {
return 0, nil
} else if intErr != nil &&
intErr.(*strconv.NumError).Err == strconv.ErrRange &&
intValue < 0 {
return 0, nil
}
return 0, err
}
return v, nil
}
// numCPU returns the CPU quota
func numCPU() (num int, err error) {
if !isContainer() {
return runtime.NumCPU(), nil
}
// If the container is running in a cgroup, we can use the cgroup cpu
// quota to limit the number of CPUs.
period, err := readUint(_cpuPeriodPath)
if err != nil {
return runtime.NumCPU(), err
}
quota, err := readUint(_cpuQuotaPath)
if err != nil {
return runtime.NumCPU(), err
}
// The number of CPUs is the quota divided by the period.
// See https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt
if quota <= 0 || period <= 0 {
return runtime.NumCPU(), err
}
return int(quota) / int(period), nil
}