Add stats for memory allocation failure count and instantaneous cpu usage in the usermode and kernelmode.
Docker-DCO-1.1-Signed-off-by: Vishnu Kannan <vishnuk@google.com> (github: vishh)
This commit is contained in:
parent
2616e87cad
commit
59c33dfe0b
4 changed files with 39 additions and 20 deletions
|
@ -20,6 +20,8 @@ var (
|
||||||
clockTicks = uint64(system.GetClockTicks())
|
clockTicks = uint64(system.GetClockTicks())
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const nanosecondsInSecond = 1000000000
|
||||||
|
|
||||||
type cpuacctGroup struct {
|
type cpuacctGroup struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,13 +39,13 @@ func (s *cpuacctGroup) Remove(d *data) error {
|
||||||
|
|
||||||
func (s *cpuacctGroup) GetStats(d *data, stats *cgroups.Stats) error {
|
func (s *cpuacctGroup) GetStats(d *data, stats *cgroups.Stats) error {
|
||||||
var (
|
var (
|
||||||
startCpu, lastCpu, startSystem, lastSystem, startUsage, lastUsage uint64
|
startCpu, lastCpu, startSystem, lastSystem, startUsage, lastUsage, kernelModeUsage, userModeUsage, percentage uint64
|
||||||
percentage uint64
|
|
||||||
)
|
)
|
||||||
path, err := d.path("cpuacct")
|
path, err := d.path("cpuacct")
|
||||||
if startCpu, err = s.getCpuUsage(d, path); err != nil {
|
if kernelModeUsage, userModeUsage, err = s.getCpuUsage(d, path); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
startCpu = kernelModeUsage + userModeUsage
|
||||||
if startSystem, err = s.getSystemCpuUsage(d); err != nil {
|
if startSystem, err = s.getSystemCpuUsage(d); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -53,9 +55,10 @@ func (s *cpuacctGroup) GetStats(d *data, stats *cgroups.Stats) error {
|
||||||
}
|
}
|
||||||
// sample for 100ms
|
// sample for 100ms
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
if lastCpu, err = s.getCpuUsage(d, path); err != nil {
|
if kernelModeUsage, userModeUsage, err = s.getCpuUsage(d, path); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
lastCpu = kernelModeUsage + userModeUsage
|
||||||
if lastSystem, err = s.getSystemCpuUsage(d); err != nil {
|
if lastSystem, err = s.getSystemCpuUsage(d); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -82,6 +85,8 @@ func (s *cpuacctGroup) GetStats(d *data, stats *cgroups.Stats) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
stats.CpuStats.CpuUsage.PercpuUsage = percpuUsage
|
stats.CpuStats.CpuUsage.PercpuUsage = percpuUsage
|
||||||
|
stats.CpuStats.CpuUsage.UsageInKernelmode = (kernelModeUsage * nanosecondsInSecond) / clockTicks
|
||||||
|
stats.CpuStats.CpuUsage.UsageInUsermode = (userModeUsage * nanosecondsInSecond) / clockTicks
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,24 +124,25 @@ func (s *cpuacctGroup) getSystemCpuUsage(d *data) (uint64, error) {
|
||||||
return 0, fmt.Errorf("invalid stat format")
|
return 0, fmt.Errorf("invalid stat format")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *cpuacctGroup) getCpuUsage(d *data, path string) (uint64, error) {
|
func (s *cpuacctGroup) getCpuUsage(d *data, path string) (uint64, uint64, error) {
|
||||||
cpuTotal := uint64(0)
|
kernelModeUsage := uint64(0)
|
||||||
f, err := os.Open(filepath.Join(path, "cpuacct.stat"))
|
userModeUsage := uint64(0)
|
||||||
|
data, err := ioutil.ReadFile(filepath.Join(path, "cpuacct.stat"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
fields := strings.Fields(string(data))
|
||||||
|
if len(fields) != 4 {
|
||||||
|
return 0, 0, fmt.Errorf("Failure - %s is expected to have 4 fields", filepath.Join(path, "cpuacct.stat"))
|
||||||
|
}
|
||||||
|
if userModeUsage, err = strconv.ParseUint(fields[1], 10, 64); err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
if kernelModeUsage, err = strconv.ParseUint(fields[3], 10, 64); err != nil {
|
||||||
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
sc := bufio.NewScanner(f)
|
return kernelModeUsage, userModeUsage, nil
|
||||||
for sc.Scan() {
|
|
||||||
_, v, err := getCgroupParamKeyValue(sc.Text())
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
// set the raw data in map
|
|
||||||
cpuTotal += v
|
|
||||||
}
|
|
||||||
return cpuTotal, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *cpuacctGroup) getPercpuUsage(path string) ([]uint64, error) {
|
func (s *cpuacctGroup) getPercpuUsage(path string) ([]uint64, error) {
|
||||||
|
|
|
@ -84,6 +84,11 @@ func (s *memoryGroup) GetStats(d *data, stats *cgroups.Stats) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
stats.MemoryStats.MaxUsage = value
|
stats.MemoryStats.MaxUsage = value
|
||||||
|
value, err = getCgroupParamInt(path, "memory.failcnt")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
stats.MemoryStats.Failcnt = value
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ const (
|
||||||
rss 1024`
|
rss 1024`
|
||||||
memoryUsageContents = "2048\n"
|
memoryUsageContents = "2048\n"
|
||||||
memoryMaxUsageContents = "4096\n"
|
memoryMaxUsageContents = "4096\n"
|
||||||
|
memoryFailcnt = "100\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMemoryStats(t *testing.T) {
|
func TestMemoryStats(t *testing.T) {
|
||||||
|
@ -20,6 +21,7 @@ func TestMemoryStats(t *testing.T) {
|
||||||
"memory.stat": memoryStatContents,
|
"memory.stat": memoryStatContents,
|
||||||
"memory.usage_in_bytes": memoryUsageContents,
|
"memory.usage_in_bytes": memoryUsageContents,
|
||||||
"memory.max_usage_in_bytes": memoryMaxUsageContents,
|
"memory.max_usage_in_bytes": memoryMaxUsageContents,
|
||||||
|
"memory.failcnt": memoryFailcnt,
|
||||||
})
|
})
|
||||||
|
|
||||||
memory := &memoryGroup{}
|
memory := &memoryGroup{}
|
||||||
|
@ -27,7 +29,7 @@ func TestMemoryStats(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
expectedStats := cgroups.MemoryStats{Usage: 2048, MaxUsage: 4096, Stats: map[string]uint64{"cache": 512, "rss": 1024}}
|
expectedStats := cgroups.MemoryStats{Usage: 2048, MaxUsage: 4096, Failcnt: 100, Stats: map[string]uint64{"cache": 512, "rss": 1024}}
|
||||||
expectMemoryStatEquals(t, expectedStats, actualStats.MemoryStats)
|
expectMemoryStatEquals(t, expectedStats, actualStats.MemoryStats)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,10 @@ type CpuUsage struct {
|
||||||
// nanoseconds of cpu time consumed over the last 100 ms.
|
// nanoseconds of cpu time consumed over the last 100 ms.
|
||||||
CurrentUsage uint64 `json:"current_usage,omitempty"`
|
CurrentUsage uint64 `json:"current_usage,omitempty"`
|
||||||
PercpuUsage []uint64 `json:"percpu_usage,omitempty"`
|
PercpuUsage []uint64 `json:"percpu_usage,omitempty"`
|
||||||
|
// Time spent by tasks of the cgroup in kernel mode. Units: nanoseconds.
|
||||||
|
UsageInKernelmode uint64 `json:"usage_in_kernelmode"`
|
||||||
|
// Time spent by tasks of the cgroup in user mode. Units: nanoseconds.
|
||||||
|
UsageInUsermode uint64 `json:"usage_in_usermode"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CpuStats struct {
|
type CpuStats struct {
|
||||||
|
@ -30,6 +34,8 @@ type MemoryStats struct {
|
||||||
// TODO(vishh): Export these as stronger types.
|
// TODO(vishh): Export these as stronger types.
|
||||||
// all the stats exported via memory.stat.
|
// all the stats exported via memory.stat.
|
||||||
Stats map[string]uint64 `json:"stats,omitempty"`
|
Stats map[string]uint64 `json:"stats,omitempty"`
|
||||||
|
// number of times memory usage hits limits.
|
||||||
|
Failcnt uint64 `json:"failcnt"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type BlkioStatEntry struct {
|
type BlkioStatEntry struct {
|
||||||
|
|
Loading…
Reference in a new issue