diff --git a/api/client/commands.go b/api/client/commands.go index 27dc627699..07554ed609 100644 --- a/api/client/commands.go +++ b/api/client/commands.go @@ -2717,11 +2717,16 @@ func (cli *DockerCli) CmdStats(args ...string) error { } func calcuateCpuPercent(previousCpu, previousSystem uint64, v *stats.Stats) float64 { - cpuPercent := 0.0 - cpuDelta := float64(v.CpuStats.CpuUsage.TotalUsage) - float64(previousCpu) - systemDelta := float64(int(v.CpuStats.SystemUsage)/v.ClockTicks) - float64(int(previousSystem)/v.ClockTicks) - if systemDelta > 0.0 { - cpuPercent = (cpuDelta / systemDelta) * float64(v.ClockTicks*len(v.CpuStats.CpuUsage.PercpuUsage)) + var ( + cpuPercent = 0.0 + // calculate the change for the cpu usage of the container in between readings + cpuDelta = float64(v.CpuStats.CpuUsage.TotalUsage - previousCpu) + // calculate the change for the entire system between readings + systemDelta = float64(v.CpuStats.SystemUsage - previousSystem) + ) + + if systemDelta > 0.0 && cpuDelta > 0.0 { + cpuPercent = (cpuDelta / systemDelta) * float64(len(v.CpuStats.CpuUsage.PercpuUsage)) * 100.0 } return cpuPercent } diff --git a/api/client/sort.go b/api/client/sort.go deleted file mode 100644 index 1b8232c3f8..0000000000 --- a/api/client/sort.go +++ /dev/null @@ -1,29 +0,0 @@ -package client - -import "sort" - -func sortStatsByName(cStats map[string]containerStats) []containerStats { - sStats := []containerStats{} - for _, s := range cStats { - sStats = append(sStats, s) - } - sorter := &statSorter{sStats} - sort.Sort(sorter) - return sStats -} - -type statSorter struct { - stats []containerStats -} - -func (s *statSorter) Len() int { - return len(s.stats) -} - -func (s *statSorter) Swap(i, j int) { - s.stats[i], s.stats[j] = s.stats[j], s.stats[i] -} - -func (s *statSorter) Less(i, j int) bool { - return s.stats[i].Name < s.stats[j].Name -} diff --git a/api/stats/stats.go b/api/stats/stats.go index b2820f2439..43146cf7bf 100644 --- a/api/stats/stats.go +++ b/api/stats/stats.go @@ -83,8 +83,6 @@ type Network struct { type Stats struct { Read time.Time `json:"read"` - ClockTicks int `json:"clock_ticks"` - Interval int `json:"interval"` // in ms Network Network `json:"network,omitempty"` CpuStats CpuStats `json:"cpu_stats,omitempty"` MemoryStats MemoryStats `json:"memory_stats,omitempty"` diff --git a/daemon/execdriver/driver.go b/daemon/execdriver/driver.go index f6e0ac7284..2215d03cff 100644 --- a/daemon/execdriver/driver.go +++ b/daemon/execdriver/driver.go @@ -107,7 +107,6 @@ type Resources struct { type ResourceStats struct { *libcontainer.ContainerStats Read time.Time `json:"read"` - ClockTicks int `json:"clock_ticks"` MemoryLimit int64 `json:"memory_limit"` SystemUsage uint64 `json:"system_usage"` } diff --git a/daemon/execdriver/execdrivers/execdrivers.go b/daemon/execdriver/execdrivers/execdrivers.go index a665985d10..be3222a8b4 100644 --- a/daemon/execdriver/execdrivers/execdrivers.go +++ b/daemon/execdriver/execdrivers/execdrivers.go @@ -8,15 +8,9 @@ import ( "github.com/docker/docker/daemon/execdriver/lxc" "github.com/docker/docker/daemon/execdriver/native" "github.com/docker/docker/pkg/sysinfo" - "github.com/docker/docker/pkg/system" ) func NewDriver(name, root, initPath string, sysInfo *sysinfo.SysInfo) (execdriver.Driver, error) { - meminfo, err := system.ReadMemInfo() - if err != nil { - return nil, err - } - switch name { case "lxc": // we want to give the lxc driver the full docker root because it needs @@ -24,7 +18,7 @@ func NewDriver(name, root, initPath string, sysInfo *sysinfo.SysInfo) (execdrive // to be backwards compatible return lxc.NewDriver(root, initPath, sysInfo.AppArmor) case "native": - return native.NewDriver(path.Join(root, "execdriver", "native"), initPath, meminfo.MemTotal) + return native.NewDriver(path.Join(root, "execdriver", "native"), initPath) } return nil, fmt.Errorf("unknown exec driver %s", name) } diff --git a/daemon/execdriver/native/driver.go b/daemon/execdriver/native/driver.go index 450d7e5f3d..533e6d61ef 100644 --- a/daemon/execdriver/native/driver.go +++ b/daemon/execdriver/native/driver.go @@ -17,6 +17,7 @@ import ( log "github.com/Sirupsen/logrus" "github.com/docker/docker/daemon/execdriver" + sysinfo "github.com/docker/docker/pkg/system" "github.com/docker/docker/pkg/term" "github.com/docker/libcontainer" "github.com/docker/libcontainer/apparmor" @@ -46,7 +47,12 @@ type driver struct { sync.Mutex } -func NewDriver(root, initPath string, machineMemory int64) (*driver, error) { +func NewDriver(root, initPath string) (*driver, error) { + meminfo, err := sysinfo.ReadMemInfo() + if err != nil { + return nil, err + } + if err := os.MkdirAll(root, 0700); err != nil { return nil, err } @@ -58,7 +64,7 @@ func NewDriver(root, initPath string, machineMemory int64) (*driver, error) { root: root, initPath: initPath, activeContainers: make(map[string]*activeContainer), - machineMemory: machineMemory, + machineMemory: meminfo.MemTotal, }, nil } @@ -303,7 +309,6 @@ func (d *driver) Stats(id string) (*execdriver.ResourceStats, error) { return &execdriver.ResourceStats{ Read: now, ContainerStats: stats, - ClockTicks: system.GetClockTicks(), MemoryLimit: memoryLimit, }, nil } diff --git a/daemon/stats.go b/daemon/stats.go index 5db1cf6081..22e7584acf 100644 --- a/daemon/stats.go +++ b/daemon/stats.go @@ -17,7 +17,6 @@ func (daemon *Daemon) ContainerStats(job *engine.Job) engine.Status { ss := stats.ToStats(update.ContainerStats) ss.MemoryStats.Limit = uint64(update.MemoryLimit) ss.Read = update.Read - ss.ClockTicks = update.ClockTicks ss.CpuStats.SystemUsage = update.SystemUsage if err := enc.Encode(ss); err != nil { // TODO: handle the specific broken pipe diff --git a/daemon/stats_collector.go b/daemon/stats_collector.go index 8b5662db14..0fa1b4cae6 100644 --- a/daemon/stats_collector.go +++ b/daemon/stats_collector.go @@ -11,6 +11,7 @@ import ( log "github.com/Sirupsen/logrus" "github.com/docker/docker/daemon/execdriver" + "github.com/docker/libcontainer/system" ) // newStatsCollector returns a new statsCollector that collections @@ -21,6 +22,7 @@ func newStatsCollector(interval time.Duration) *statsCollector { s := &statsCollector{ interval: interval, containers: make(map[string]*statsData), + clockTicks: uint64(system.GetClockTicks()), } s.start() return s @@ -36,6 +38,7 @@ type statsData struct { type statsCollector struct { m sync.Mutex interval time.Duration + clockTicks uint64 containers map[string]*statsData } @@ -128,8 +131,10 @@ func (s *statsCollector) start() { }() } -// getSystemdCpuUSage returns the host system's cpu usage -// in nanoseconds. +const nanoSeconds = 1e9 + +// getSystemdCpuUSage returns the host system's cpu usage in nanoseconds +// for the system to match the cgroup readings are returned in the same format. func (s *statsCollector) getSystemCpuUsage() (uint64, error) { f, err := os.Open("/proc/stat") if err != nil { @@ -144,17 +149,15 @@ func (s *statsCollector) getSystemCpuUsage() (uint64, error) { if len(parts) < 8 { return 0, fmt.Errorf("invalid number of cpu fields") } - var total uint64 + var sum uint64 for _, i := range parts[1:8] { v, err := strconv.ParseUint(i, 10, 64) if err != nil { - return 0.0, fmt.Errorf("Unable to convert value %s to int: %s", i, err) + return 0, fmt.Errorf("Unable to convert value %s to int: %s", i, err) } - total += v + sum += v } - return total * 1000000000, nil - default: - continue + return (sum * nanoSeconds) / s.clockTicks, nil } } return 0, fmt.Errorf("invalid stat format")