containerd/vendor/github.com/crosbymichael/cgroups/cpuacct.go
Michael Crosby f36feb2ed4 Add prometheus container level metrics
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
2017-03-22 11:44:25 -07:00

112 lines
2.3 KiB
Go

package cgroups
import (
"fmt"
"io/ioutil"
"path/filepath"
"strconv"
"strings"
)
const nanosecondsInSecond = 1000000000
var clockTicks = getClockTicks()
func NewCpuacct(root string) *cpuacctController {
return &cpuacctController{
root: filepath.Join(root, string(Cpuacct)),
}
}
type cpuacctController struct {
root string
}
func (c *cpuacctController) Name() Name {
return Cpuacct
}
func (c *cpuacctController) Path(path string) string {
return filepath.Join(c.root, path)
}
func (c *cpuacctController) Stat(path string, stats *Stats) error {
user, kernel, err := c.getUsage(path)
if err != nil {
return err
}
total, err := readUint(filepath.Join(c.Path(path), "cpuacct.usage"))
if err != nil {
return err
}
percpu, err := c.percpuUsage(path)
if err != nil {
return err
}
stats.cpuMu.Lock()
cpu := stats.Cpu
if cpu == nil {
cpu = &CpuStat{}
stats.Cpu = cpu
}
stats.cpuMu.Unlock()
cpu.Usage.Total = total
cpu.Usage.User = user
cpu.Usage.Kernel = kernel
cpu.Usage.PerCpu = percpu
return nil
}
func (c *cpuacctController) percpuUsage(path string) ([]uint64, error) {
var usage []uint64
data, err := ioutil.ReadFile(filepath.Join(c.Path(path), "cpuacct.usage_percpu"))
if err != nil {
return nil, err
}
for _, v := range strings.Fields(string(data)) {
u, err := strconv.ParseUint(v, 10, 64)
if err != nil {
return nil, err
}
usage = append(usage, u)
}
return usage, nil
}
func (c *cpuacctController) getUsage(path string) (user uint64, kernel uint64, err error) {
statPath := filepath.Join(c.Path(path), "cpuacct.stat")
data, err := ioutil.ReadFile(statPath)
if err != nil {
return 0, 0, err
}
fields := strings.Fields(string(data))
if len(fields) != 4 {
return 0, 0, fmt.Errorf("%q is expected to have 4 fields", statPath)
}
for _, t := range []struct {
index int
name string
value *uint64
}{
{
index: 0,
name: "user",
value: &user,
},
{
index: 2,
name: "system",
value: &kernel,
},
} {
if fields[t.index] != t.name {
return 0, 0, fmt.Errorf("expected field %q but found %q in %q", t.name, fields[t.index], statPath)
}
v, err := strconv.ParseUint(fields[t.index+1], 10, 64)
if err != nil {
return 0, 0, err
}
*t.value = v
}
return (user * nanosecondsInSecond) / clockTicks, (kernel * nanosecondsInSecond) / clockTicks, nil
}