-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
39 changed files
with
3,106 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
## stat/sys | ||
|
||
System Information | ||
|
||
## 项目简介 | ||
|
||
获取Linux平台下的系统信息,包括cpu主频、cpu使用率等 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
package cpukit | ||
|
||
import ( | ||
"bufio" | ||
"fmt" | ||
"io" | ||
"os" | ||
"path" | ||
"strconv" | ||
"strings" | ||
) | ||
|
||
const cgroupRootDir = "/sys/fs/cgroup" | ||
|
||
// cgroup Linux cgroup | ||
type cgroup struct { | ||
cgroupSet map[string]string | ||
} | ||
|
||
// CPUCFSQuotaUs cpu.cfs_quota_us | ||
func (c *cgroup) CPUCFSQuotaUs() (int64, error) { | ||
data, err := readFile(path.Join(c.cgroupSet["cpu"], "cpu.cfs_quota_us")) | ||
if err != nil { | ||
return 0, err | ||
} | ||
return strconv.ParseInt(data, 10, 64) | ||
} | ||
|
||
// CPUCFSPeriodUs cpu.cfs_period_us | ||
func (c *cgroup) CPUCFSPeriodUs() (uint64, error) { | ||
data, err := readFile(path.Join(c.cgroupSet["cpu"], "cpu.cfs_period_us")) | ||
if err != nil { | ||
return 0, err | ||
} | ||
return parseUint(data) | ||
} | ||
|
||
// CPUAcctUsage cpuacct.usage | ||
func (c *cgroup) CPUAcctUsage() (uint64, error) { | ||
data, err := readFile(path.Join(c.cgroupSet["cpuacct"], "cpuacct.usage")) | ||
if err != nil { | ||
return 0, err | ||
} | ||
return parseUint(data) | ||
} | ||
|
||
// CPUAcctUsagePerCPU cpuacct.usage_percpu | ||
func (c *cgroup) CPUAcctUsagePerCPU() ([]uint64, error) { | ||
data, err := readFile(path.Join(c.cgroupSet["cpuacct"], "cpuacct.usage_percpu")) | ||
if err != nil { | ||
return nil, err | ||
} | ||
var usage []uint64 | ||
for _, v := range strings.Fields(string(data)) { | ||
var u uint64 | ||
if u, err = parseUint(v); err != nil { | ||
return nil, err | ||
} | ||
// fix possible_cpu:https://www.ibm.com/support/knowledgecenter/en/linuxonibm/com.ibm.linux.z.lgdd/lgdd_r_posscpusparm.html | ||
if u != 0 { | ||
usage = append(usage, u) | ||
} | ||
} | ||
return usage, nil | ||
} | ||
|
||
// CPUSetCPUs cpuset.cpus | ||
func (c *cgroup) CPUSetCPUs() ([]uint64, error) { | ||
data, err := readFile(path.Join(c.cgroupSet["cpuset"], "cpuset.cpus")) | ||
if err != nil { | ||
return nil, err | ||
} | ||
cpus, err := ParseUintList(data) | ||
if err != nil { | ||
return nil, err | ||
} | ||
var sets []uint64 | ||
for k := range cpus { | ||
sets = append(sets, uint64(k)) | ||
} | ||
return sets, nil | ||
} | ||
|
||
// CurrentcGroup get current process cgroup | ||
func currentcGroup() (*cgroup, error) { | ||
pid := os.Getpid() | ||
cgroupFile := fmt.Sprintf("/proc/%d/cgroup", pid) | ||
cgroupSet := make(map[string]string) | ||
fp, err := os.Open(cgroupFile) | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer fp.Close() | ||
buf := bufio.NewReader(fp) | ||
for { | ||
line, err := buf.ReadString('\n') | ||
if err != nil { | ||
if err == io.EOF { | ||
break | ||
} | ||
return nil, err | ||
} | ||
col := strings.Split(strings.TrimSpace(line), ":") | ||
if len(col) != 3 { | ||
return nil, fmt.Errorf("invalid cgroup format %s", line) | ||
} | ||
dir := col[2] | ||
// When dir is not equal to /, it must be in docker | ||
if dir != "/" { | ||
cgroupSet[col[1]] = path.Join(cgroupRootDir, col[1]) | ||
if strings.Contains(col[1], ",") { | ||
for _, k := range strings.Split(col[1], ",") { | ||
cgroupSet[k] = path.Join(cgroupRootDir, k) | ||
} | ||
} | ||
} else { | ||
cgroupSet[col[1]] = path.Join(cgroupRootDir, col[1], col[2]) | ||
if strings.Contains(col[1], ",") { | ||
for _, k := range strings.Split(col[1], ",") { | ||
cgroupSet[k] = path.Join(cgroupRootDir, k, col[2]) | ||
} | ||
} | ||
} | ||
} | ||
return &cgroup{cgroupSet: cgroupSet}, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,250 @@ | ||
package cpukit | ||
|
||
import ( | ||
"bufio" | ||
"fmt" | ||
"os" | ||
"strconv" | ||
"strings" | ||
|
||
"github.com/pkg/errors" | ||
pscpu "github.com/shirou/gopsutil/cpu" | ||
) | ||
|
||
type cgroupCPU struct { | ||
frequency uint64 | ||
quota float64 | ||
cores uint64 | ||
|
||
preSystem uint64 | ||
preTotal uint64 | ||
usage uint64 | ||
} | ||
|
||
func newCgroupCPU() (cpu *cgroupCPU, err error) { | ||
var cores int | ||
cores, err = pscpu.Counts(true) | ||
if err != nil || cores == 0 { | ||
var cpus []uint64 | ||
cpus, err = perCPUUsage() | ||
if err != nil { | ||
err = errors.Errorf("perCPUUsage() failed!err:=%v", err) | ||
return | ||
} | ||
cores = len(cpus) | ||
} | ||
|
||
sets, err := cpuSets() | ||
if err != nil { | ||
err = errors.Errorf("cpuSets() failed!err:=%v", err) | ||
return | ||
} | ||
quota := float64(len(sets)) | ||
cq, err := cpuQuota() | ||
if err == nil && cq != -1 { | ||
var period uint64 | ||
if period, err = cpuPeriod(); err != nil { | ||
err = errors.Errorf("cpuPeriod() failed!err:=%v", err) | ||
return | ||
} | ||
limit := float64(cq) / float64(period) | ||
if limit < quota { | ||
quota = limit | ||
} | ||
} | ||
maxFreq := cpuMaxFreq() | ||
|
||
preSystem, err := systemCPUUsage() | ||
if err != nil { | ||
err = errors.Errorf("systemCPUUsage() failed!err:=%v", err) | ||
return | ||
} | ||
preTotal, err := totalCPUUsage() | ||
if err != nil { | ||
err = errors.Errorf("totalCPUUsage() failed!err:=%v", err) | ||
return | ||
} | ||
cpu = &cgroupCPU{ | ||
frequency: maxFreq, | ||
quota: quota, | ||
cores: uint64(cores), | ||
preSystem: preSystem, | ||
preTotal: preTotal, | ||
} | ||
return | ||
} | ||
|
||
func (cpu *cgroupCPU) Usage() (u uint64, err error) { | ||
var ( | ||
total uint64 | ||
system uint64 | ||
) | ||
total, err = totalCPUUsage() | ||
if err != nil { | ||
return | ||
} | ||
system, err = systemCPUUsage() | ||
if err != nil { | ||
return | ||
} | ||
if system != cpu.preSystem { | ||
u = uint64(float64((total-cpu.preTotal)*cpu.cores*1e3) / (float64(system-cpu.preSystem) * cpu.quota)) | ||
} | ||
cpu.preSystem = system | ||
cpu.preTotal = total | ||
return | ||
} | ||
|
||
func (cpu *cgroupCPU) Info() Info { | ||
return Info{ | ||
Frequency: cpu.frequency, | ||
Quota: cpu.quota, | ||
} | ||
} | ||
|
||
const nanoSecondsPerSecond = 1e9 | ||
|
||
// ErrNoCFSLimit is no quota limit | ||
var ErrNoCFSLimit = errors.Errorf("no quota limit") | ||
|
||
var clockTicksPerSecond = uint64(getClockTicks()) | ||
|
||
// systemCPUUsage returns the host system's cpu usage in | ||
// nanoseconds. An error is returned if the format of the underlying | ||
// file does not match. | ||
// | ||
// Uses /proc/stat defined by POSIX. Looks for the cpu | ||
// statistics line and then sums up the first seven fields | ||
// provided. See man 5 proc for details on specific field | ||
// information. | ||
func systemCPUUsage() (usage uint64, err error) { | ||
var ( | ||
line string | ||
f *os.File | ||
) | ||
if f, err = os.Open("/proc/stat"); err != nil { | ||
return | ||
} | ||
bufReader := bufio.NewReaderSize(nil, 128) | ||
defer func() { | ||
bufReader.Reset(nil) | ||
f.Close() | ||
}() | ||
bufReader.Reset(f) | ||
for err == nil { | ||
if line, err = bufReader.ReadString('\n'); err != nil { | ||
err = errors.WithStack(err) | ||
return | ||
} | ||
parts := strings.Fields(line) | ||
switch parts[0] { | ||
case "cpu": | ||
if len(parts) < 8 { | ||
err = errors.WithStack(fmt.Errorf("bad format of cpu stats")) | ||
return | ||
} | ||
var totalClockTicks uint64 | ||
for _, i := range parts[1:8] { | ||
var v uint64 | ||
if v, err = strconv.ParseUint(i, 10, 64); err != nil { | ||
err = errors.WithStack(fmt.Errorf("error parsing cpu stats")) | ||
return | ||
} | ||
totalClockTicks += v | ||
} | ||
usage = (totalClockTicks * nanoSecondsPerSecond) / clockTicksPerSecond | ||
return | ||
} | ||
} | ||
err = errors.Errorf("bad stats format") | ||
return | ||
} | ||
|
||
func totalCPUUsage() (usage uint64, err error) { | ||
var cg *cgroup | ||
if cg, err = currentcGroup(); err != nil { | ||
return | ||
} | ||
return cg.CPUAcctUsage() | ||
} | ||
|
||
func perCPUUsage() (usage []uint64, err error) { | ||
var cg *cgroup | ||
if cg, err = currentcGroup(); err != nil { | ||
return | ||
} | ||
return cg.CPUAcctUsagePerCPU() | ||
} | ||
|
||
func cpuSets() (sets []uint64, err error) { | ||
var cg *cgroup | ||
if cg, err = currentcGroup(); err != nil { | ||
return | ||
} | ||
return cg.CPUSetCPUs() | ||
} | ||
|
||
func cpuQuota() (quota int64, err error) { | ||
var cg *cgroup | ||
if cg, err = currentcGroup(); err != nil { | ||
return | ||
} | ||
return cg.CPUCFSQuotaUs() | ||
} | ||
|
||
func cpuPeriod() (peroid uint64, err error) { | ||
var cg *cgroup | ||
if cg, err = currentcGroup(); err != nil { | ||
return | ||
} | ||
return cg.CPUCFSPeriodUs() | ||
} | ||
|
||
func cpuFreq() uint64 { | ||
lines, err := readLines("/proc/cpuinfo") | ||
if err != nil { | ||
return 0 | ||
} | ||
for _, line := range lines { | ||
fields := strings.Split(line, ":") | ||
if len(fields) < 2 { | ||
continue | ||
} | ||
key := strings.TrimSpace(fields[0]) | ||
value := strings.TrimSpace(fields[1]) | ||
if key == "cpu MHz" || key == "clock" { | ||
// treat this as the fallback value, thus we ignore error | ||
if t, err := strconv.ParseFloat(strings.Replace(value, "MHz", "", 1), 64); err == nil { | ||
return uint64(t * 1000.0 * 1000.0) | ||
} | ||
} | ||
} | ||
return 0 | ||
} | ||
|
||
func cpuMaxFreq() uint64 { | ||
feq := cpuFreq() | ||
data, err := readFile("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq") | ||
if err != nil { | ||
return feq | ||
} | ||
// override the max freq from /proc/cpuinfo | ||
cfeq, err := parseUint(data) | ||
if err == nil { | ||
feq = cfeq | ||
} | ||
return feq | ||
} | ||
|
||
//GetClockTicks get the OS's ticks per second | ||
func getClockTicks() int { | ||
// TODO figure out a better alternative for platforms where we're missing cgo | ||
// | ||
// TODO Windows. This could be implemented using Win32 QueryPerformanceFrequency(). | ||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms644905(v=vs.85).aspx | ||
// | ||
// An example of its usage can be found here. | ||
// https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx | ||
|
||
return 100 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// +build linux | ||
|
||
package cpukit | ||
|
||
import ( | ||
"testing" | ||
) | ||
|
||
func TestCGroup(t *testing.T) { | ||
// TODO | ||
} |
Oops, something went wrong.