Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions events.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ func convertLibcontainerStats(ls *libcontainer.Stats) *types.Stats {
s.CPU.Usage.User = cg.CpuStats.CpuUsage.UsageInUsermode
s.CPU.Usage.Total = cg.CpuStats.CpuUsage.TotalUsage
s.CPU.Usage.Percpu = cg.CpuStats.CpuUsage.PercpuUsage
s.CPU.Usage.PercpuKernel = cg.CpuStats.CpuUsage.PercpuUsageInKernelmode
s.CPU.Usage.PercpuUser = cg.CpuStats.CpuUsage.PercpuUsageInUsermode
s.CPU.Throttling.Periods = cg.CpuStats.ThrottlingData.Periods
s.CPU.Throttling.ThrottledPeriods = cg.CpuStats.ThrottlingData.ThrottledPeriods
s.CPU.Throttling.ThrottledTime = cg.CpuStats.ThrottlingData.ThrottledTime
Expand Down
56 changes: 55 additions & 1 deletion libcontainer/cgroups/fs/cpuacct.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
package fs

import (
"bufio"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
Expand All @@ -15,9 +17,15 @@ import (
)

const (
cgroupCpuacctStat = "cpuacct.stat"
cgroupCpuacctStat = "cpuacct.stat"
cgroupCpuacctUsageAll = "cpuacct.usage_all"

nanosecondsInSecond = 1000000000

userModeColumn = 1
kernelModeColumn = 2
cuacctUsageAllColumnsNumber = 3

// The value comes from `C.sysconf(C._SC_CLK_TCK)`, and
// on Linux it's a constant which is safe to be hard coded,
// so we can avoid using cgo here. For details, see:
Expand Down Expand Up @@ -65,8 +73,15 @@ func (s *CpuacctGroup) GetStats(path string, stats *cgroups.Stats) error {
return err
}

percpuUsageInKernelmode, percpuUsageInUsermode, err := getPercpuUsageInModes(path)
if err != nil {
return err
}

stats.CpuStats.CpuUsage.TotalUsage = totalUsage
stats.CpuStats.CpuUsage.PercpuUsage = percpuUsage
stats.CpuStats.CpuUsage.PercpuUsageInKernelmode = percpuUsageInKernelmode
stats.CpuStats.CpuUsage.PercpuUsageInUsermode = percpuUsageInUsermode
stats.CpuStats.CpuUsage.UsageInUsermode = userModeUsage
stats.CpuStats.CpuUsage.UsageInKernelmode = kernelModeUsage
return nil
Expand Down Expand Up @@ -123,3 +138,42 @@ func getPercpuUsage(path string) ([]uint64, error) {
}
return percpuUsage, nil
}

func getPercpuUsageInModes(path string) ([]uint64, []uint64, error) {
usageKernelMode := []uint64{}
usageUserMode := []uint64{}

file, err := os.Open(filepath.Join(path, cgroupCpuacctUsageAll))
if err != nil {
return nil, nil, err
}
defer file.Close()

scanner := bufio.NewScanner(file)
scanner.Scan() //skipping header line

for scanner.Scan() {
lineFields := strings.SplitN(scanner.Text(), " ", cuacctUsageAllColumnsNumber+1)
if len(lineFields) != cuacctUsageAllColumnsNumber {
continue
}

usageInKernelMode, err := strconv.ParseUint(lineFields[kernelModeColumn], 10, 64)
if err != nil {
return nil, nil, fmt.Errorf("Unable to convert CPU usage in kernel mode to uint64: %s", err)
}
usageKernelMode = append(usageKernelMode, usageInKernelMode)

usageInUserMode, err := strconv.ParseUint(lineFields[userModeColumn], 10, 64)
if err != nil {
return nil, nil, fmt.Errorf("Unable to convert CPU usage in user mode to uint64: %s", err)
}
usageUserMode = append(usageUserMode, usageInUserMode)
}

if err := scanner.Err(); err != nil {
return nil, nil, fmt.Errorf("Problem in reading %s line by line, %s", cgroupCpuacctUsageAll, err)
}

return usageKernelMode, usageUserMode, nil
}
61 changes: 61 additions & 0 deletions libcontainer/cgroups/fs/cpuacct_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// +build linux

package fs

import (
"reflect"
"testing"

"github.com/opencontainers/runc/libcontainer/cgroups"
)

const (
cpuAcctUsageContents = "12262454190222160"
cpuAcctUsagePerCPUContents = "1564936537989058 1583937096487821 1604195415465681 1596445226820187 1481069084155629 1478735613864327 1477610593414743 1476362015778086"
cpuAcctStatContents = "user 452278264\nsystem 291429664"
cpuAcctUsageAll = `cpu user system
0 962250696038415 637727786389114
1 981956408513304 638197595421064
2 1002658817529022 638956774598358
3 994937703492523 637985531181620
4 874843781648690 638837766495476
5 872544369885276 638763309884944
6 870104915696359 640081778921247
7 870202363887496 638716766259495
`
)

func TestCpuacctStats(t *testing.T) {
helper := NewCgroupTestUtil("cpuacct.", t)
defer helper.cleanup()
helper.writeFileContents(map[string]string{
"cpuacct.usage": cpuAcctUsageContents,
"cpuacct.usage_percpu": cpuAcctUsagePerCPUContents,
"cpuacct.stat": cpuAcctStatContents,
"cpuacct.usage_all": cpuAcctUsageAll,
})

cpuacct := &CpuacctGroup{}
actualStats := *cgroups.NewStats()
err := cpuacct.GetStats(helper.CgroupPath, &actualStats)
if err != nil {
t.Fatal(err)
}

expectedStats := cgroups.CpuUsage{
TotalUsage: uint64(12262454190222160),
PercpuUsage: []uint64{1564936537989058, 1583937096487821, 1604195415465681, 1596445226820187,
1481069084155629, 1478735613864327, 1477610593414743, 1476362015778086},
PercpuUsageInKernelmode: []uint64{637727786389114, 638197595421064, 638956774598358, 637985531181620,
638837766495476, 638763309884944, 640081778921247, 638716766259495},
PercpuUsageInUsermode: []uint64{962250696038415, 981956408513304, 1002658817529022, 994937703492523,
874843781648690, 872544369885276, 870104915696359, 870202363887496},
UsageInKernelmode: (uint64(291429664) * nanosecondsInSecond) / clockTicks,
UsageInUsermode: (uint64(452278264) * nanosecondsInSecond) / clockTicks,
}

if !reflect.DeepEqual(expectedStats, actualStats.CpuStats.CpuUsage) {
t.Errorf("Expected CPU usage %#v but found %#v\n",
expectedStats, actualStats.CpuStats.CpuUsage)
}
}
6 changes: 6 additions & 0 deletions libcontainer/cgroups/stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ type CpuUsage struct {
// Total CPU time consumed per core.
// Units: nanoseconds.
PercpuUsage []uint64 `json:"percpu_usage,omitempty"`
// CPU time consumed per core in kernel mode
// Units: nanoseconds.
PercpuUsageInKernelmode []uint64 `json:"percpu_usage_in_kernelmode"`
// CPU time consumed per core in user mode
// Units: nanoseconds.
PercpuUsageInUsermode []uint64 `json:"percpu_usage_in_usermode"`
// Time spent by tasks of the cgroup in kernel mode.
// Units: nanoseconds.
UsageInKernelmode uint64 `json:"usage_in_kernelmode"`
Expand Down
10 changes: 6 additions & 4 deletions types/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,12 @@ type Throttling struct {

type CpuUsage struct {
// Units: nanoseconds.
Total uint64 `json:"total,omitempty"`
Percpu []uint64 `json:"percpu,omitempty"`
Kernel uint64 `json:"kernel"`
User uint64 `json:"user"`
Total uint64 `json:"total,omitempty"`
Percpu []uint64 `json:"percpu,omitempty"`
PercpuKernel []uint64 `json:"percpu_kernel,omitempty"`
PercpuUser []uint64 `json:"percpu_user,omitempty"`
Kernel uint64 `json:"kernel"`
User uint64 `json:"user"`
}

type Cpu struct {
Expand Down