Docker-DCO-1.1-Signed-off-by: Vishnu Kannan <vishnuk@google.com> (github: vishh)
| ... | ... |
@@ -26,7 +26,7 @@ var ( |
| 26 | 26 |
type subsystem interface {
|
| 27 | 27 |
Set(*data) error |
| 28 | 28 |
Remove(*data) error |
| 29 |
- Stats(*data) (map[string]int64, error) |
|
| 29 |
+ GetStats(*data, *cgroups.Stats) error |
|
| 30 | 30 |
} |
| 31 | 31 |
|
| 32 | 32 |
type data struct {
|
| ... | ... |
@@ -74,7 +74,8 @@ func Apply(c *cgroups.Cgroup, pid int) (cgroups.ActiveCgroup, error) {
|
| 74 | 74 |
return d, nil |
| 75 | 75 |
} |
| 76 | 76 |
|
| 77 |
-func GetStats(c *cgroups.Cgroup, subsystem string, pid int) (map[string]int64, error) {
|
|
| 77 |
+func GetStats(c *cgroups.Cgroup) (*cgroups.Stats, error) {
|
|
| 78 |
+ stats := cgroups.NewStats() |
|
| 78 | 79 |
cgroupRoot, err := cgroups.FindCgroupMountpoint("cpu")
|
| 79 | 80 |
if err != nil {
|
| 80 | 81 |
return nil, err |
| ... | ... |
@@ -94,13 +95,15 @@ func GetStats(c *cgroups.Cgroup, subsystem string, pid int) (map[string]int64, e |
| 94 | 94 |
root: cgroupRoot, |
| 95 | 95 |
cgroup: cgroup, |
| 96 | 96 |
c: c, |
| 97 |
- pid: pid, |
|
| 98 | 97 |
} |
| 99 |
- sys, exists := subsystems[subsystem] |
|
| 100 |
- if !exists {
|
|
| 101 |
- return nil, fmt.Errorf("subsystem %s does not exist", subsystem)
|
|
| 98 |
+ |
|
| 99 |
+ for _, sys := range subsystems {
|
|
| 100 |
+ if err := sys.GetStats(d, stats); err != nil {
|
|
| 101 |
+ return nil, err |
|
| 102 |
+ } |
|
| 102 | 103 |
} |
| 103 |
- return sys.Stats(d) |
|
| 104 |
+ |
|
| 105 |
+ return stats, nil |
|
| 104 | 106 |
} |
| 105 | 107 |
|
| 106 | 108 |
func GetPids(c *cgroups.Cgroup) ([]int, error) {
|
| ... | ... |
@@ -3,7 +3,6 @@ package fs |
| 3 | 3 |
import ( |
| 4 | 4 |
"bufio" |
| 5 | 5 |
"fmt" |
| 6 |
- "io/ioutil" |
|
| 7 | 6 |
"os" |
| 8 | 7 |
"path/filepath" |
| 9 | 8 |
"strconv" |
| ... | ... |
@@ -57,65 +56,87 @@ examples: |
| 57 | 57 |
8:0 Total 0 |
| 58 | 58 |
Total 0 |
| 59 | 59 |
*/ |
| 60 |
-func (s *blkioGroup) Stats(d *data) (map[string]int64, error) {
|
|
| 61 |
- var ( |
|
| 62 |
- paramData = make(map[string]int64) |
|
| 63 |
- params = []string{
|
|
| 64 |
- "io_service_bytes_recursive", |
|
| 65 |
- "io_serviced_recursive", |
|
| 66 |
- "io_queued_recursive", |
|
| 67 |
- } |
|
| 68 |
- ) |
|
| 69 | 60 |
|
| 70 |
- path, err := d.path("blkio")
|
|
| 71 |
- if err != nil {
|
|
| 72 |
- return nil, err |
|
| 73 |
- } |
|
| 61 |
+func splitBlkioStatLine(r rune) bool {
|
|
| 62 |
+ return r == ' ' || r == ':' |
|
| 63 |
+} |
|
| 74 | 64 |
|
| 75 |
- k, v, err := s.getSectors(path) |
|
| 65 |
+func getBlkioStat(path string) ([]cgroups.BlkioStatEntry, error) {
|
|
| 66 |
+ var blkioStats []cgroups.BlkioStatEntry |
|
| 67 |
+ f, err := os.Open(path) |
|
| 76 | 68 |
if err != nil {
|
| 77 | 69 |
return nil, err |
| 78 | 70 |
} |
| 79 |
- paramData[fmt.Sprintf("blkio.sectors_recursive:%s", k)] = v
|
|
| 71 |
+ defer f.Close() |
|
| 72 |
+ |
|
| 73 |
+ sc := bufio.NewScanner(f) |
|
| 74 |
+ for sc.Scan() {
|
|
| 75 |
+ // format: dev type amount |
|
| 76 |
+ fields := strings.FieldsFunc(sc.Text(), splitBlkioStatLine) |
|
| 77 |
+ if len(fields) < 3 {
|
|
| 78 |
+ if len(fields) == 2 && fields[0] == "Total" {
|
|
| 79 |
+ // skip total line |
|
| 80 |
+ continue |
|
| 81 |
+ } else {
|
|
| 82 |
+ return nil, fmt.Errorf("Invalid line found while parsing %s: %s", path, sc.Text())
|
|
| 83 |
+ } |
|
| 84 |
+ } |
|
| 80 | 85 |
|
| 81 |
- for _, param := range params {
|
|
| 82 |
- f, err := os.Open(filepath.Join(path, fmt.Sprintf("blkio.%s", param)))
|
|
| 86 |
+ v, err := strconv.ParseUint(fields[0], 10, 64) |
|
| 83 | 87 |
if err != nil {
|
| 84 | 88 |
return nil, err |
| 85 | 89 |
} |
| 86 |
- defer f.Close() |
|
| 87 |
- |
|
| 88 |
- sc := bufio.NewScanner(f) |
|
| 89 |
- for sc.Scan() {
|
|
| 90 |
- // format: dev type amount |
|
| 91 |
- fields := strings.Fields(sc.Text()) |
|
| 92 |
- switch len(fields) {
|
|
| 93 |
- case 3: |
|
| 94 |
- v, err := strconv.ParseInt(fields[2], 10, 64) |
|
| 95 |
- if err != nil {
|
|
| 96 |
- return nil, err |
|
| 97 |
- } |
|
| 98 |
- paramData[fmt.Sprintf("%s:%s:%s", param, fields[0], fields[1])] = v
|
|
| 99 |
- case 2: |
|
| 100 |
- // this is the total line, skip |
|
| 101 |
- default: |
|
| 102 |
- return nil, ErrNotValidFormat |
|
| 103 |
- } |
|
| 90 |
+ major := v |
|
| 91 |
+ |
|
| 92 |
+ v, err = strconv.ParseUint(fields[1], 10, 64) |
|
| 93 |
+ if err != nil {
|
|
| 94 |
+ return nil, err |
|
| 95 |
+ } |
|
| 96 |
+ minor := v |
|
| 97 |
+ |
|
| 98 |
+ op := "" |
|
| 99 |
+ valueField := 2 |
|
| 100 |
+ if len(fields) == 4 {
|
|
| 101 |
+ op = fields[2] |
|
| 102 |
+ valueField = 3 |
|
| 103 |
+ } |
|
| 104 |
+ v, err = strconv.ParseUint(fields[valueField], 10, 64) |
|
| 105 |
+ if err != nil {
|
|
| 106 |
+ return nil, err |
|
| 104 | 107 |
} |
| 108 |
+ blkioStats = append(blkioStats, cgroups.BlkioStatEntry{Major: major, Minor: minor, Op: op, Value: v})
|
|
| 105 | 109 |
} |
| 106 |
- return paramData, nil |
|
| 110 |
+ |
|
| 111 |
+ return blkioStats, nil |
|
| 107 | 112 |
} |
| 108 | 113 |
|
| 109 |
-func (s *blkioGroup) getSectors(path string) (string, int64, error) {
|
|
| 110 |
- f, err := os.Open(filepath.Join(path, "blkio.sectors_recursive")) |
|
| 114 |
+func (s *blkioGroup) GetStats(d *data, stats *cgroups.Stats) error {
|
|
| 115 |
+ var blkioStats []cgroups.BlkioStatEntry |
|
| 116 |
+ var err error |
|
| 117 |
+ path, err := d.path("blkio")
|
|
| 111 | 118 |
if err != nil {
|
| 112 |
- return "", 0, err |
|
| 119 |
+ return err |
|
| 113 | 120 |
} |
| 114 |
- defer f.Close() |
|
| 115 | 121 |
|
| 116 |
- data, err := ioutil.ReadAll(f) |
|
| 117 |
- if err != nil {
|
|
| 118 |
- return "", 0, err |
|
| 122 |
+ if blkioStats, err = getBlkioStat(filepath.Join(path, "blkio.sectors_recursive")); err != nil {
|
|
| 123 |
+ return err |
|
| 124 |
+ } |
|
| 125 |
+ stats.BlkioStats.SectorsRecursive = blkioStats |
|
| 126 |
+ |
|
| 127 |
+ if blkioStats, err = getBlkioStat(filepath.Join(path, "blkio.io_service_bytes_recursive")); err != nil {
|
|
| 128 |
+ return err |
|
| 119 | 129 |
} |
| 120 |
- return getCgroupParamKeyValue(string(data)) |
|
| 130 |
+ stats.BlkioStats.IoServiceBytesRecursive = blkioStats |
|
| 131 |
+ |
|
| 132 |
+ if blkioStats, err = getBlkioStat(filepath.Join(path, "blkio.io_serviced_recursive")); err != nil {
|
|
| 133 |
+ return err |
|
| 134 |
+ } |
|
| 135 |
+ stats.BlkioStats.IoServicedRecursive = blkioStats |
|
| 136 |
+ |
|
| 137 |
+ if blkioStats, err = getBlkioStat(filepath.Join(path, "blkio.io_queued_recursive")); err != nil {
|
|
| 138 |
+ return err |
|
| 139 |
+ } |
|
| 140 |
+ stats.BlkioStats.IoQueuedRecursive = blkioStats |
|
| 141 |
+ |
|
| 142 |
+ return nil |
|
| 121 | 143 |
} |
| ... | ... |
@@ -2,14 +2,16 @@ package fs |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"testing" |
| 5 |
+ |
|
| 6 |
+ "github.com/dotcloud/docker/pkg/libcontainer/cgroups" |
|
| 5 | 7 |
) |
| 6 | 8 |
|
| 7 | 9 |
const ( |
| 8 | 10 |
sectorsRecursiveContents = `8:0 1024` |
| 9 | 11 |
serviceBytesRecursiveContents = `8:0 Read 100 |
| 10 |
-8:0 Write 400 |
|
| 11 |
-8:0 Sync 200 |
|
| 12 |
-8:0 Async 300 |
|
| 12 |
+8:0 Write 200 |
|
| 13 |
+8:0 Sync 300 |
|
| 14 |
+8:0 Async 500 |
|
| 13 | 15 |
8:0 Total 500 |
| 14 | 16 |
Total 500` |
| 15 | 17 |
servicedRecursiveContents = `8:0 Read 10 |
| ... | ... |
@@ -26,6 +28,12 @@ Total 50` |
| 26 | 26 |
Total 5` |
| 27 | 27 |
) |
| 28 | 28 |
|
| 29 |
+var actualStats = *cgroups.NewStats() |
|
| 30 |
+ |
|
| 31 |
+func appendBlkioStatEntry(blkioStatEntries *[]cgroups.BlkioStatEntry, major, minor, value uint64, op string) {
|
|
| 32 |
+ *blkioStatEntries = append(*blkioStatEntries, cgroups.BlkioStatEntry{Major: major, Minor: minor, Value: value, Op: op})
|
|
| 33 |
+} |
|
| 34 |
+ |
|
| 29 | 35 |
func TestBlkioStats(t *testing.T) {
|
| 30 | 36 |
helper := NewCgroupTestUtil("blkio", t)
|
| 31 | 37 |
defer helper.cleanup() |
| ... | ... |
@@ -37,37 +45,34 @@ func TestBlkioStats(t *testing.T) {
|
| 37 | 37 |
}) |
| 38 | 38 |
|
| 39 | 39 |
blkio := &blkioGroup{}
|
| 40 |
- stats, err := blkio.Stats(helper.CgroupData) |
|
| 40 |
+ err := blkio.GetStats(helper.CgroupData, &actualStats) |
|
| 41 | 41 |
if err != nil {
|
| 42 | 42 |
t.Fatal(err) |
| 43 | 43 |
} |
| 44 | 44 |
|
| 45 | 45 |
// Verify expected stats. |
| 46 |
- expectedStats := map[string]int64{
|
|
| 47 |
- "blkio.sectors_recursive:8:0": 1024, |
|
| 48 |
- |
|
| 49 |
- // Serviced bytes. |
|
| 50 |
- "io_service_bytes_recursive:8:0:Read": 100, |
|
| 51 |
- "io_service_bytes_recursive:8:0:Write": 400, |
|
| 52 |
- "io_service_bytes_recursive:8:0:Sync": 200, |
|
| 53 |
- "io_service_bytes_recursive:8:0:Async": 300, |
|
| 54 |
- "io_service_bytes_recursive:8:0:Total": 500, |
|
| 55 |
- |
|
| 56 |
- // Serviced requests. |
|
| 57 |
- "io_serviced_recursive:8:0:Read": 10, |
|
| 58 |
- "io_serviced_recursive:8:0:Write": 40, |
|
| 59 |
- "io_serviced_recursive:8:0:Sync": 20, |
|
| 60 |
- "io_serviced_recursive:8:0:Async": 30, |
|
| 61 |
- "io_serviced_recursive:8:0:Total": 50, |
|
| 62 |
- |
|
| 63 |
- // Queued requests. |
|
| 64 |
- "io_queued_recursive:8:0:Read": 1, |
|
| 65 |
- "io_queued_recursive:8:0:Write": 4, |
|
| 66 |
- "io_queued_recursive:8:0:Sync": 2, |
|
| 67 |
- "io_queued_recursive:8:0:Async": 3, |
|
| 68 |
- "io_queued_recursive:8:0:Total": 5, |
|
| 69 |
- } |
|
| 70 |
- expectStats(t, expectedStats, stats) |
|
| 46 |
+ expectedStats := cgroups.BlkioStats{}
|
|
| 47 |
+ appendBlkioStatEntry(&expectedStats.SectorsRecursive, 8, 0, 1024, "") |
|
| 48 |
+ |
|
| 49 |
+ appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 100, "Read") |
|
| 50 |
+ appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 200, "Write") |
|
| 51 |
+ appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 300, "Sync") |
|
| 52 |
+ appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 500, "Async") |
|
| 53 |
+ appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 500, "Total") |
|
| 54 |
+ |
|
| 55 |
+ appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 10, "Read") |
|
| 56 |
+ appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 40, "Write") |
|
| 57 |
+ appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 20, "Sync") |
|
| 58 |
+ appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 30, "Async") |
|
| 59 |
+ appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 50, "Total") |
|
| 60 |
+ |
|
| 61 |
+ appendBlkioStatEntry(&expectedStats.IoQueuedRecursive, 8, 0, 1, "Read") |
|
| 62 |
+ appendBlkioStatEntry(&expectedStats.IoQueuedRecursive, 8, 0, 4, "Write") |
|
| 63 |
+ appendBlkioStatEntry(&expectedStats.IoQueuedRecursive, 8, 0, 2, "Sync") |
|
| 64 |
+ appendBlkioStatEntry(&expectedStats.IoQueuedRecursive, 8, 0, 3, "Async") |
|
| 65 |
+ appendBlkioStatEntry(&expectedStats.IoQueuedRecursive, 8, 0, 5, "Total") |
|
| 66 |
+ |
|
| 67 |
+ expectBlkioStatsEquals(t, expectedStats, actualStats.BlkioStats) |
|
| 71 | 68 |
} |
| 72 | 69 |
|
| 73 | 70 |
func TestBlkioStatsNoSectorsFile(t *testing.T) {
|
| ... | ... |
@@ -80,7 +85,7 @@ func TestBlkioStatsNoSectorsFile(t *testing.T) {
|
| 80 | 80 |
}) |
| 81 | 81 |
|
| 82 | 82 |
blkio := &blkioGroup{}
|
| 83 |
- _, err := blkio.Stats(helper.CgroupData) |
|
| 83 |
+ err := blkio.GetStats(helper.CgroupData, &actualStats) |
|
| 84 | 84 |
if err == nil {
|
| 85 | 85 |
t.Fatal("Expected to fail, but did not")
|
| 86 | 86 |
} |
| ... | ... |
@@ -96,7 +101,7 @@ func TestBlkioStatsNoServiceBytesFile(t *testing.T) {
|
| 96 | 96 |
}) |
| 97 | 97 |
|
| 98 | 98 |
blkio := &blkioGroup{}
|
| 99 |
- _, err := blkio.Stats(helper.CgroupData) |
|
| 99 |
+ err := blkio.GetStats(helper.CgroupData, &actualStats) |
|
| 100 | 100 |
if err == nil {
|
| 101 | 101 |
t.Fatal("Expected to fail, but did not")
|
| 102 | 102 |
} |
| ... | ... |
@@ -112,7 +117,7 @@ func TestBlkioStatsNoServicedFile(t *testing.T) {
|
| 112 | 112 |
}) |
| 113 | 113 |
|
| 114 | 114 |
blkio := &blkioGroup{}
|
| 115 |
- _, err := blkio.Stats(helper.CgroupData) |
|
| 115 |
+ err := blkio.GetStats(helper.CgroupData, &actualStats) |
|
| 116 | 116 |
if err == nil {
|
| 117 | 117 |
t.Fatal("Expected to fail, but did not")
|
| 118 | 118 |
} |
| ... | ... |
@@ -128,7 +133,7 @@ func TestBlkioStatsNoQueuedFile(t *testing.T) {
|
| 128 | 128 |
}) |
| 129 | 129 |
|
| 130 | 130 |
blkio := &blkioGroup{}
|
| 131 |
- _, err := blkio.Stats(helper.CgroupData) |
|
| 131 |
+ err := blkio.GetStats(helper.CgroupData, &actualStats) |
|
| 132 | 132 |
if err == nil {
|
| 133 | 133 |
t.Fatal("Expected to fail, but did not")
|
| 134 | 134 |
} |
| ... | ... |
@@ -145,7 +150,7 @@ func TestBlkioStatsUnexpectedNumberOfFields(t *testing.T) {
|
| 145 | 145 |
}) |
| 146 | 146 |
|
| 147 | 147 |
blkio := &blkioGroup{}
|
| 148 |
- _, err := blkio.Stats(helper.CgroupData) |
|
| 148 |
+ err := blkio.GetStats(helper.CgroupData, &actualStats) |
|
| 149 | 149 |
if err == nil {
|
| 150 | 150 |
t.Fatal("Expected to fail, but did not")
|
| 151 | 151 |
} |
| ... | ... |
@@ -162,7 +167,7 @@ func TestBlkioStatsUnexpectedFieldType(t *testing.T) {
|
| 162 | 162 |
}) |
| 163 | 163 |
|
| 164 | 164 |
blkio := &blkioGroup{}
|
| 165 |
- _, err := blkio.Stats(helper.CgroupData) |
|
| 165 |
+ err := blkio.GetStats(helper.CgroupData, &actualStats) |
|
| 166 | 166 |
if err == nil {
|
| 167 | 167 |
t.Fatal("Expected to fail, but did not")
|
| 168 | 168 |
} |
| ... | ... |
@@ -5,6 +5,8 @@ import ( |
| 5 | 5 |
"os" |
| 6 | 6 |
"path/filepath" |
| 7 | 7 |
"strconv" |
| 8 |
+ |
|
| 9 |
+ "github.com/dotcloud/docker/pkg/libcontainer/cgroups" |
|
| 8 | 10 |
) |
| 9 | 11 |
|
| 10 | 12 |
type cpuGroup struct {
|
| ... | ... |
@@ -39,16 +41,15 @@ func (s *cpuGroup) Remove(d *data) error {
|
| 39 | 39 |
return removePath(d.path("cpu"))
|
| 40 | 40 |
} |
| 41 | 41 |
|
| 42 |
-func (s *cpuGroup) Stats(d *data) (map[string]int64, error) {
|
|
| 43 |
- paramData := make(map[string]int64) |
|
| 42 |
+func (s *cpuGroup) GetStats(d *data, stats *cgroups.Stats) error {
|
|
| 44 | 43 |
path, err := d.path("cpu")
|
| 45 | 44 |
if err != nil {
|
| 46 |
- return nil, err |
|
| 45 |
+ return err |
|
| 47 | 46 |
} |
| 48 | 47 |
|
| 49 | 48 |
f, err := os.Open(filepath.Join(path, "cpu.stat")) |
| 50 | 49 |
if err != nil {
|
| 51 |
- return nil, err |
|
| 50 |
+ return err |
|
| 52 | 51 |
} |
| 53 | 52 |
defer f.Close() |
| 54 | 53 |
|
| ... | ... |
@@ -56,9 +57,18 @@ func (s *cpuGroup) Stats(d *data) (map[string]int64, error) {
|
| 56 | 56 |
for sc.Scan() {
|
| 57 | 57 |
t, v, err := getCgroupParamKeyValue(sc.Text()) |
| 58 | 58 |
if err != nil {
|
| 59 |
- return nil, err |
|
| 59 |
+ return err |
|
| 60 |
+ } |
|
| 61 |
+ switch t {
|
|
| 62 |
+ case "nr_periods": |
|
| 63 |
+ stats.CpuStats.ThrottlingData.Periods = v |
|
| 64 |
+ |
|
| 65 |
+ case "nr_throttled": |
|
| 66 |
+ stats.CpuStats.ThrottlingData.ThrottledPeriods = v |
|
| 67 |
+ |
|
| 68 |
+ case "throttled_time": |
|
| 69 |
+ stats.CpuStats.ThrottlingData.ThrottledTime = v |
|
| 60 | 70 |
} |
| 61 |
- paramData[t] = v |
|
| 62 | 71 |
} |
| 63 |
- return paramData, nil |
|
| 72 |
+ return nil |
|
| 64 | 73 |
} |
| ... | ... |
@@ -1,31 +1,40 @@ |
| 1 | 1 |
package fs |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "fmt" |
|
| 4 | 5 |
"testing" |
| 6 |
+ |
|
| 7 |
+ "github.com/dotcloud/docker/pkg/libcontainer/cgroups" |
|
| 5 | 8 |
) |
| 6 | 9 |
|
| 7 | 10 |
func TestCpuStats(t *testing.T) {
|
| 8 | 11 |
helper := NewCgroupTestUtil("cpu", t)
|
| 9 | 12 |
defer helper.cleanup() |
| 10 |
- cpuStatContent := `nr_periods 2000 |
|
| 11 |
- nr_throttled 200 |
|
| 12 |
- throttled_time 42424242424` |
|
| 13 |
+ |
|
| 14 |
+ const ( |
|
| 15 |
+ kNrPeriods = 2000 |
|
| 16 |
+ kNrThrottled = 200 |
|
| 17 |
+ kThrottledTime = uint64(18446744073709551615) |
|
| 18 |
+ ) |
|
| 19 |
+ |
|
| 20 |
+ cpuStatContent := fmt.Sprintf("nr_periods %d\n nr_throttled %d\n throttled_time %d\n",
|
|
| 21 |
+ kNrPeriods, kNrThrottled, kThrottledTime) |
|
| 13 | 22 |
helper.writeFileContents(map[string]string{
|
| 14 | 23 |
"cpu.stat": cpuStatContent, |
| 15 | 24 |
}) |
| 16 | 25 |
|
| 17 | 26 |
cpu := &cpuGroup{}
|
| 18 |
- stats, err := cpu.Stats(helper.CgroupData) |
|
| 27 |
+ err := cpu.GetStats(helper.CgroupData, &actualStats) |
|
| 19 | 28 |
if err != nil {
|
| 20 | 29 |
t.Fatal(err) |
| 21 | 30 |
} |
| 22 | 31 |
|
| 23 |
- expected_stats := map[string]int64{
|
|
| 24 |
- "nr_periods": 2000, |
|
| 25 |
- "nr_throttled": 200, |
|
| 26 |
- "throttled_time": 42424242424, |
|
| 27 |
- } |
|
| 28 |
- expectStats(t, expected_stats, stats) |
|
| 32 |
+ expectedStats := cgroups.ThrottlingData{
|
|
| 33 |
+ Periods: kNrPeriods, |
|
| 34 |
+ ThrottledPeriods: kNrThrottled, |
|
| 35 |
+ ThrottledTime: kThrottledTime} |
|
| 36 |
+ |
|
| 37 |
+ expectThrottlingDataEquals(t, expectedStats, actualStats.CpuStats.ThrottlingData) |
|
| 29 | 38 |
} |
| 30 | 39 |
|
| 31 | 40 |
func TestNoCpuStatFile(t *testing.T) {
|
| ... | ... |
@@ -33,7 +42,7 @@ func TestNoCpuStatFile(t *testing.T) {
|
| 33 | 33 |
defer helper.cleanup() |
| 34 | 34 |
|
| 35 | 35 |
cpu := &cpuGroup{}
|
| 36 |
- _, err := cpu.Stats(helper.CgroupData) |
|
| 36 |
+ err := cpu.GetStats(helper.CgroupData, &actualStats) |
|
| 37 | 37 |
if err == nil {
|
| 38 | 38 |
t.Fatal("Expected to fail, but did not.")
|
| 39 | 39 |
} |
| ... | ... |
@@ -50,7 +59,7 @@ func TestInvalidCpuStat(t *testing.T) {
|
| 50 | 50 |
}) |
| 51 | 51 |
|
| 52 | 52 |
cpu := &cpuGroup{}
|
| 53 |
- _, err := cpu.Stats(helper.CgroupData) |
|
| 53 |
+ err := cpu.GetStats(helper.CgroupData, &actualStats) |
|
| 54 | 54 |
if err == nil {
|
| 55 | 55 |
t.Fatal("Expected failed stat parsing.")
|
| 56 | 56 |
} |
| ... | ... |
@@ -15,8 +15,8 @@ import ( |
| 15 | 15 |
) |
| 16 | 16 |
|
| 17 | 17 |
var ( |
| 18 |
- cpuCount = int64(runtime.NumCPU()) |
|
| 19 |
- clockTicks = int64(system.GetClockTicks()) |
|
| 18 |
+ cpuCount = uint64(runtime.NumCPU()) |
|
| 19 |
+ clockTicks = uint64(system.GetClockTicks()) |
|
| 20 | 20 |
) |
| 21 | 21 |
|
| 22 | 22 |
type cpuacctGroup struct {
|
| ... | ... |
@@ -34,34 +34,33 @@ func (s *cpuacctGroup) Remove(d *data) error {
|
| 34 | 34 |
return removePath(d.path("cpuacct"))
|
| 35 | 35 |
} |
| 36 | 36 |
|
| 37 |
-func (s *cpuacctGroup) Stats(d *data) (map[string]int64, error) {
|
|
| 37 |
+func (s *cpuacctGroup) GetStats(d *data, stats *cgroups.Stats) error {
|
|
| 38 | 38 |
var ( |
| 39 |
- startCpu, lastCpu, startSystem, lastSystem, startUsage, lastUsage int64 |
|
| 40 |
- percentage int64 |
|
| 41 |
- paramData = make(map[string]int64) |
|
| 39 |
+ startCpu, lastCpu, startSystem, lastSystem, startUsage, lastUsage uint64 |
|
| 40 |
+ percentage uint64 |
|
| 42 | 41 |
) |
| 43 | 42 |
path, err := d.path("cpuacct")
|
| 44 | 43 |
if startCpu, err = s.getCpuUsage(d, path); err != nil {
|
| 45 |
- return nil, err |
|
| 44 |
+ return err |
|
| 46 | 45 |
} |
| 47 | 46 |
if startSystem, err = s.getSystemCpuUsage(d); err != nil {
|
| 48 |
- return nil, err |
|
| 47 |
+ return err |
|
| 49 | 48 |
} |
| 50 | 49 |
startUsageTime := time.Now() |
| 51 | 50 |
if startUsage, err = getCgroupParamInt(path, "cpuacct.usage"); err != nil {
|
| 52 |
- return nil, err |
|
| 51 |
+ return err |
|
| 53 | 52 |
} |
| 54 | 53 |
// sample for 100ms |
| 55 | 54 |
time.Sleep(100 * time.Millisecond) |
| 56 | 55 |
if lastCpu, err = s.getCpuUsage(d, path); err != nil {
|
| 57 |
- return nil, err |
|
| 56 |
+ return err |
|
| 58 | 57 |
} |
| 59 | 58 |
if lastSystem, err = s.getSystemCpuUsage(d); err != nil {
|
| 60 |
- return nil, err |
|
| 59 |
+ return err |
|
| 61 | 60 |
} |
| 62 | 61 |
usageSampleDuration := time.Since(startUsageTime) |
| 63 | 62 |
if lastUsage, err = getCgroupParamInt(path, "cpuacct.usage"); err != nil {
|
| 64 |
- return nil, err |
|
| 63 |
+ return err |
|
| 65 | 64 |
} |
| 66 | 65 |
|
| 67 | 66 |
var ( |
| ... | ... |
@@ -74,15 +73,14 @@ func (s *cpuacctGroup) Stats(d *data) (map[string]int64, error) {
|
| 74 | 74 |
} |
| 75 | 75 |
// NOTE: a percentage over 100% is valid for POSIX because that means the |
| 76 | 76 |
// processes is using multiple cores |
| 77 |
- paramData["percentage"] = percentage |
|
| 78 |
- |
|
| 77 |
+ stats.CpuStats.CpuUsage.PercentUsage = percentage |
|
| 79 | 78 |
// Delta usage is in nanoseconds of CPU time so get the usage (in cores) over the sample time. |
| 80 |
- paramData["usage"] = deltaUsage / usageSampleDuration.Nanoseconds() |
|
| 81 |
- return paramData, nil |
|
| 79 |
+ stats.CpuStats.CpuUsage.CurrentUsage = deltaUsage / uint64(usageSampleDuration.Nanoseconds()) |
|
| 80 |
+ return nil |
|
| 82 | 81 |
} |
| 83 | 82 |
|
| 84 | 83 |
// TODO(vmarmol): Use cgroups stats. |
| 85 |
-func (s *cpuacctGroup) getSystemCpuUsage(d *data) (int64, error) {
|
|
| 84 |
+func (s *cpuacctGroup) getSystemCpuUsage(d *data) (uint64, error) {
|
|
| 86 | 85 |
|
| 87 | 86 |
f, err := os.Open("/proc/stat")
|
| 88 | 87 |
if err != nil {
|
| ... | ... |
@@ -99,9 +97,9 @@ func (s *cpuacctGroup) getSystemCpuUsage(d *data) (int64, error) {
|
| 99 | 99 |
return 0, fmt.Errorf("invalid number of cpu fields")
|
| 100 | 100 |
} |
| 101 | 101 |
|
| 102 |
- var total int64 |
|
| 102 |
+ var total uint64 |
|
| 103 | 103 |
for _, i := range parts[1:8] {
|
| 104 |
- v, err := strconv.ParseInt(i, 10, 64) |
|
| 104 |
+ v, err := strconv.ParseUint(i, 10, 64) |
|
| 105 | 105 |
if err != nil {
|
| 106 | 106 |
return 0.0, fmt.Errorf("Unable to convert value %s to int: %s", i, err)
|
| 107 | 107 |
} |
| ... | ... |
@@ -115,8 +113,8 @@ func (s *cpuacctGroup) getSystemCpuUsage(d *data) (int64, error) {
|
| 115 | 115 |
return 0, fmt.Errorf("invalid stat format")
|
| 116 | 116 |
} |
| 117 | 117 |
|
| 118 |
-func (s *cpuacctGroup) getCpuUsage(d *data, path string) (int64, error) {
|
|
| 119 |
- cpuTotal := int64(0) |
|
| 118 |
+func (s *cpuacctGroup) getCpuUsage(d *data, path string) (uint64, error) {
|
|
| 119 |
+ cpuTotal := uint64(0) |
|
| 120 | 120 |
f, err := os.Open(filepath.Join(path, "cpuacct.stat")) |
| 121 | 121 |
if err != nil {
|
| 122 | 122 |
return 0, err |
| ... | ... |
@@ -6,6 +6,8 @@ import ( |
| 6 | 6 |
"os" |
| 7 | 7 |
"path/filepath" |
| 8 | 8 |
"strconv" |
| 9 |
+ |
|
| 10 |
+ "github.com/dotcloud/docker/pkg/libcontainer/cgroups" |
|
| 9 | 11 |
) |
| 10 | 12 |
|
| 11 | 13 |
type cpusetGroup struct {
|
| ... | ... |
@@ -38,8 +40,8 @@ func (s *cpusetGroup) Remove(d *data) error {
|
| 38 | 38 |
return removePath(d.path("cpuset"))
|
| 39 | 39 |
} |
| 40 | 40 |
|
| 41 |
-func (s *cpusetGroup) Stats(d *data) (map[string]int64, error) {
|
|
| 42 |
- return nil, ErrNotSupportStat |
|
| 41 |
+func (s *cpusetGroup) GetStats(d *data, stats *cgroups.Stats) error {
|
|
| 42 |
+ return nil |
|
| 43 | 43 |
} |
| 44 | 44 |
|
| 45 | 45 |
func (s *cpusetGroup) getSubsystemSettings(parent string) (cpus []byte, mems []byte, err error) {
|
| ... | ... |
@@ -1,5 +1,7 @@ |
| 1 | 1 |
package fs |
| 2 | 2 |
|
| 3 |
+import "github.com/dotcloud/docker/pkg/libcontainer/cgroups" |
|
| 4 |
+ |
|
| 3 | 5 |
type devicesGroup struct {
|
| 4 | 6 |
} |
| 5 | 7 |
|
| ... | ... |
@@ -55,6 +57,6 @@ func (s *devicesGroup) Remove(d *data) error {
|
| 55 | 55 |
return removePath(d.path("devices"))
|
| 56 | 56 |
} |
| 57 | 57 |
|
| 58 |
-func (s *devicesGroup) Stats(d *data) (map[string]int64, error) {
|
|
| 59 |
- return nil, ErrNotSupportStat |
|
| 58 |
+func (s *devicesGroup) GetStats(d *data, stats *cgroups.Stats) error {
|
|
| 59 |
+ return nil |
|
| 60 | 60 |
} |
| ... | ... |
@@ -1,11 +1,8 @@ |
| 1 | 1 |
package fs |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
- "fmt" |
|
| 5 | 4 |
"io/ioutil" |
| 6 |
- "os" |
|
| 7 | 5 |
"path/filepath" |
| 8 |
- "strconv" |
|
| 9 | 6 |
"strings" |
| 10 | 7 |
|
| 11 | 8 |
"github.com/dotcloud/docker/pkg/libcontainer/cgroups" |
| ... | ... |
@@ -35,39 +32,25 @@ func (s *freezerGroup) Remove(d *data) error {
|
| 35 | 35 |
return removePath(d.path("freezer"))
|
| 36 | 36 |
} |
| 37 | 37 |
|
| 38 |
-func (s *freezerGroup) Stats(d *data) (map[string]int64, error) {
|
|
| 39 |
- var ( |
|
| 40 |
- paramData = make(map[string]int64) |
|
| 41 |
- params = []string{
|
|
| 42 |
- "parent_freezing", |
|
| 43 |
- "self_freezing", |
|
| 44 |
- // comment out right now because this is string "state", |
|
| 45 |
- } |
|
| 46 |
- ) |
|
| 38 |
+func getFreezerFileData(path string) (string, error) {
|
|
| 39 |
+ data, err := ioutil.ReadFile(path) |
|
| 40 |
+ return strings.TrimSuffix(string(data), "\n"), err |
|
| 41 |
+} |
|
| 47 | 42 |
|
| 43 |
+func (s *freezerGroup) GetStats(d *data, stats *cgroups.Stats) error {
|
|
| 48 | 44 |
path, err := d.path("freezer")
|
| 49 | 45 |
if err != nil {
|
| 50 |
- return nil, err |
|
| 46 |
+ return err |
|
| 51 | 47 |
} |
| 52 |
- |
|
| 53 |
- // TODO(vmarmol): This currently outputs nothing since the output is a string, fix. |
|
| 54 |
- for _, param := range params {
|
|
| 55 |
- f, err := os.Open(filepath.Join(path, fmt.Sprintf("freezer.%s", param)))
|
|
| 56 |
- if err != nil {
|
|
| 57 |
- return nil, err |
|
| 58 |
- } |
|
| 59 |
- defer f.Close() |
|
| 60 |
- |
|
| 61 |
- data, err := ioutil.ReadAll(f) |
|
| 62 |
- if err != nil {
|
|
| 63 |
- return nil, err |
|
| 64 |
- } |
|
| 65 |
- |
|
| 66 |
- v, err := strconv.ParseInt(strings.TrimSuffix(string(data), "\n"), 10, 64) |
|
| 67 |
- if err != nil {
|
|
| 68 |
- return nil, err |
|
| 69 |
- } |
|
| 70 |
- paramData[param] = v |
|
| 48 |
+ var data string |
|
| 49 |
+ if data, err = getFreezerFileData(filepath.Join(path, "freezer.parent_freezing")); err != nil {
|
|
| 50 |
+ return err |
|
| 51 |
+ } |
|
| 52 |
+ stats.FreezerStats.ParentState = data |
|
| 53 |
+ if data, err = getFreezerFileData(filepath.Join(path, "freezer.self_freezing")); err != nil {
|
|
| 54 |
+ return err |
|
| 71 | 55 |
} |
| 72 |
- return paramData, nil |
|
| 56 |
+ stats.FreezerStats.SelfState = data |
|
| 57 |
+ |
|
| 58 |
+ return nil |
|
| 73 | 59 |
} |
| ... | ... |
@@ -2,10 +2,11 @@ package fs |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"bufio" |
| 5 |
- "fmt" |
|
| 6 | 5 |
"os" |
| 7 | 6 |
"path/filepath" |
| 8 | 7 |
"strconv" |
| 8 |
+ |
|
| 9 |
+ "github.com/dotcloud/docker/pkg/libcontainer/cgroups" |
|
| 9 | 10 |
) |
| 10 | 11 |
|
| 11 | 12 |
type memoryGroup struct {
|
| ... | ... |
@@ -50,17 +51,16 @@ func (s *memoryGroup) Remove(d *data) error {
|
| 50 | 50 |
return removePath(d.path("memory"))
|
| 51 | 51 |
} |
| 52 | 52 |
|
| 53 |
-func (s *memoryGroup) Stats(d *data) (map[string]int64, error) {
|
|
| 54 |
- paramData := make(map[string]int64) |
|
| 53 |
+func (s *memoryGroup) GetStats(d *data, stats *cgroups.Stats) error {
|
|
| 55 | 54 |
path, err := d.path("memory")
|
| 56 | 55 |
if err != nil {
|
| 57 |
- return nil, err |
|
| 56 |
+ return err |
|
| 58 | 57 |
} |
| 59 | 58 |
|
| 60 | 59 |
// Set stats from memory.stat. |
| 61 | 60 |
statsFile, err := os.Open(filepath.Join(path, "memory.stat")) |
| 62 | 61 |
if err != nil {
|
| 63 |
- return nil, err |
|
| 62 |
+ return err |
|
| 64 | 63 |
} |
| 65 | 64 |
defer statsFile.Close() |
| 66 | 65 |
|
| ... | ... |
@@ -68,23 +68,22 @@ func (s *memoryGroup) Stats(d *data) (map[string]int64, error) {
|
| 68 | 68 |
for sc.Scan() {
|
| 69 | 69 |
t, v, err := getCgroupParamKeyValue(sc.Text()) |
| 70 | 70 |
if err != nil {
|
| 71 |
- return nil, err |
|
| 71 |
+ return err |
|
| 72 | 72 |
} |
| 73 |
- paramData[t] = v |
|
| 73 |
+ stats.MemoryStats.Stats[t] = v |
|
| 74 | 74 |
} |
| 75 | 75 |
|
| 76 | 76 |
// Set memory usage and max historical usage. |
| 77 |
- params := []string{
|
|
| 78 |
- "usage_in_bytes", |
|
| 79 |
- "max_usage_in_bytes", |
|
| 77 |
+ value, err := getCgroupParamInt(path, "memory.usage_in_bytes") |
|
| 78 |
+ if err != nil {
|
|
| 79 |
+ return err |
|
| 80 | 80 |
} |
| 81 |
- for _, param := range params {
|
|
| 82 |
- value, err := getCgroupParamInt(path, fmt.Sprintf("memory.%s", param))
|
|
| 83 |
- if err != nil {
|
|
| 84 |
- return nil, err |
|
| 85 |
- } |
|
| 86 |
- paramData[param] = value |
|
| 81 |
+ stats.MemoryStats.Usage = value |
|
| 82 |
+ value, err = getCgroupParamInt(path, "memory.max_usage_in_bytes") |
|
| 83 |
+ if err != nil {
|
|
| 84 |
+ return err |
|
| 87 | 85 |
} |
| 86 |
+ stats.MemoryStats.MaxUsage = value |
|
| 88 | 87 |
|
| 89 |
- return paramData, nil |
|
| 88 |
+ return nil |
|
| 90 | 89 |
} |
| ... | ... |
@@ -2,6 +2,8 @@ package fs |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"testing" |
| 5 |
+ |
|
| 6 |
+ "github.com/dotcloud/docker/pkg/libcontainer/cgroups" |
|
| 5 | 7 |
) |
| 6 | 8 |
|
| 7 | 9 |
const ( |
| ... | ... |
@@ -21,12 +23,12 @@ func TestMemoryStats(t *testing.T) {
|
| 21 | 21 |
}) |
| 22 | 22 |
|
| 23 | 23 |
memory := &memoryGroup{}
|
| 24 |
- stats, err := memory.Stats(helper.CgroupData) |
|
| 24 |
+ err := memory.GetStats(helper.CgroupData, &actualStats) |
|
| 25 | 25 |
if err != nil {
|
| 26 | 26 |
t.Fatal(err) |
| 27 | 27 |
} |
| 28 |
- expectedStats := map[string]int64{"cache": 512, "rss": 1024, "usage_in_bytes": 2048, "max_usage_in_bytes": 4096}
|
|
| 29 |
- expectStats(t, expectedStats, stats) |
|
| 28 |
+ expectedStats := cgroups.MemoryStats{Usage: 2048, MaxUsage: 4096, Stats: map[string]uint64{"cache": 512, "rss": 1024}}
|
|
| 29 |
+ expectMemoryStatEquals(t, expectedStats, actualStats.MemoryStats) |
|
| 30 | 30 |
} |
| 31 | 31 |
|
| 32 | 32 |
func TestMemoryStatsNoStatFile(t *testing.T) {
|
| ... | ... |
@@ -38,7 +40,7 @@ func TestMemoryStatsNoStatFile(t *testing.T) {
|
| 38 | 38 |
}) |
| 39 | 39 |
|
| 40 | 40 |
memory := &memoryGroup{}
|
| 41 |
- _, err := memory.Stats(helper.CgroupData) |
|
| 41 |
+ err := memory.GetStats(helper.CgroupData, &actualStats) |
|
| 42 | 42 |
if err == nil {
|
| 43 | 43 |
t.Fatal("Expected failure")
|
| 44 | 44 |
} |
| ... | ... |
@@ -53,7 +55,7 @@ func TestMemoryStatsNoUsageFile(t *testing.T) {
|
| 53 | 53 |
}) |
| 54 | 54 |
|
| 55 | 55 |
memory := &memoryGroup{}
|
| 56 |
- _, err := memory.Stats(helper.CgroupData) |
|
| 56 |
+ err := memory.GetStats(helper.CgroupData, &actualStats) |
|
| 57 | 57 |
if err == nil {
|
| 58 | 58 |
t.Fatal("Expected failure")
|
| 59 | 59 |
} |
| ... | ... |
@@ -68,7 +70,7 @@ func TestMemoryStatsNoMaxUsageFile(t *testing.T) {
|
| 68 | 68 |
}) |
| 69 | 69 |
|
| 70 | 70 |
memory := &memoryGroup{}
|
| 71 |
- _, err := memory.Stats(helper.CgroupData) |
|
| 71 |
+ err := memory.GetStats(helper.CgroupData, &actualStats) |
|
| 72 | 72 |
if err == nil {
|
| 73 | 73 |
t.Fatal("Expected failure")
|
| 74 | 74 |
} |
| ... | ... |
@@ -84,7 +86,7 @@ func TestMemoryStatsBadStatFile(t *testing.T) {
|
| 84 | 84 |
}) |
| 85 | 85 |
|
| 86 | 86 |
memory := &memoryGroup{}
|
| 87 |
- _, err := memory.Stats(helper.CgroupData) |
|
| 87 |
+ err := memory.GetStats(helper.CgroupData, &actualStats) |
|
| 88 | 88 |
if err == nil {
|
| 89 | 89 |
t.Fatal("Expected failure")
|
| 90 | 90 |
} |
| ... | ... |
@@ -100,7 +102,7 @@ func TestMemoryStatsBadUsageFile(t *testing.T) {
|
| 100 | 100 |
}) |
| 101 | 101 |
|
| 102 | 102 |
memory := &memoryGroup{}
|
| 103 |
- _, err := memory.Stats(helper.CgroupData) |
|
| 103 |
+ err := memory.GetStats(helper.CgroupData, &actualStats) |
|
| 104 | 104 |
if err == nil {
|
| 105 | 105 |
t.Fatal("Expected failure")
|
| 106 | 106 |
} |
| ... | ... |
@@ -116,7 +118,7 @@ func TestMemoryStatsBadMaxUsageFile(t *testing.T) {
|
| 116 | 116 |
}) |
| 117 | 117 |
|
| 118 | 118 |
memory := &memoryGroup{}
|
| 119 |
- _, err := memory.Stats(helper.CgroupData) |
|
| 119 |
+ err := memory.GetStats(helper.CgroupData, &actualStats) |
|
| 120 | 120 |
if err == nil {
|
| 121 | 121 |
t.Fatal("Expected failure")
|
| 122 | 122 |
} |
| ... | ... |
@@ -19,6 +19,6 @@ func (s *perfEventGroup) Remove(d *data) error {
|
| 19 | 19 |
return removePath(d.path("perf_event"))
|
| 20 | 20 |
} |
| 21 | 21 |
|
| 22 |
-func (s *perfEventGroup) Stats(d *data) (map[string]int64, error) {
|
|
| 23 |
- return nil, ErrNotSupportStat |
|
| 22 |
+func (s *perfEventGroup) GetStats(d *data, stats *cgroups.Stats) error {
|
|
| 23 |
+ return nil |
|
| 24 | 24 |
} |
| 25 | 25 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,73 @@ |
| 0 |
+package fs |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "log" |
|
| 5 |
+ "testing" |
|
| 6 |
+ |
|
| 7 |
+ "github.com/dotcloud/docker/pkg/libcontainer/cgroups" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+func blkioStatEntryEquals(expected, actual []cgroups.BlkioStatEntry) error {
|
|
| 11 |
+ if len(expected) != len(actual) {
|
|
| 12 |
+ return fmt.Errorf("blkioStatEntries length do not match")
|
|
| 13 |
+ } |
|
| 14 |
+ for i, expValue := range expected {
|
|
| 15 |
+ actValue := actual[i] |
|
| 16 |
+ if expValue != actValue {
|
|
| 17 |
+ return fmt.Errorf("Expected blkio stat entry %v but found %v", expValue, actValue)
|
|
| 18 |
+ } |
|
| 19 |
+ } |
|
| 20 |
+ return nil |
|
| 21 |
+} |
|
| 22 |
+ |
|
| 23 |
+func expectBlkioStatsEquals(t *testing.T, expected, actual cgroups.BlkioStats) {
|
|
| 24 |
+ if err := blkioStatEntryEquals(expected.IoServiceBytesRecursive, actual.IoServiceBytesRecursive); err != nil {
|
|
| 25 |
+ log.Printf("blkio IoServiceBytesRecursive do not match - %s\n", err)
|
|
| 26 |
+ t.Fail() |
|
| 27 |
+ } |
|
| 28 |
+ |
|
| 29 |
+ if err := blkioStatEntryEquals(expected.IoServicedRecursive, actual.IoServicedRecursive); err != nil {
|
|
| 30 |
+ log.Printf("blkio IoServicedRecursive do not match - %s\n", err)
|
|
| 31 |
+ t.Fail() |
|
| 32 |
+ } |
|
| 33 |
+ |
|
| 34 |
+ if err := blkioStatEntryEquals(expected.IoQueuedRecursive, actual.IoQueuedRecursive); err != nil {
|
|
| 35 |
+ log.Printf("blkio IoQueuedRecursive do not match - %s\n", err)
|
|
| 36 |
+ t.Fail() |
|
| 37 |
+ } |
|
| 38 |
+ |
|
| 39 |
+ if err := blkioStatEntryEquals(expected.SectorsRecursive, actual.SectorsRecursive); err != nil {
|
|
| 40 |
+ log.Printf("blkio SectorsRecursive do not match - %s\n", err)
|
|
| 41 |
+ t.Fail() |
|
| 42 |
+ } |
|
| 43 |
+} |
|
| 44 |
+ |
|
| 45 |
+func expectThrottlingDataEquals(t *testing.T, expected, actual cgroups.ThrottlingData) {
|
|
| 46 |
+ if expected != actual {
|
|
| 47 |
+ log.Printf("Expected throttling data %v but found %v\n", expected, actual)
|
|
| 48 |
+ t.Fail() |
|
| 49 |
+ } |
|
| 50 |
+} |
|
| 51 |
+ |
|
| 52 |
+func expectMemoryStatEquals(t *testing.T, expected, actual cgroups.MemoryStats) {
|
|
| 53 |
+ if expected.Usage != actual.Usage {
|
|
| 54 |
+ log.Printf("Expected memory usage %d but found %d\n", expected.Usage, actual.Usage)
|
|
| 55 |
+ t.Fail() |
|
| 56 |
+ } |
|
| 57 |
+ if expected.MaxUsage != actual.MaxUsage {
|
|
| 58 |
+ log.Printf("Expected memory max usage %d but found %d\n", expected.MaxUsage, actual.MaxUsage)
|
|
| 59 |
+ t.Fail() |
|
| 60 |
+ } |
|
| 61 |
+ for key, expValue := range expected.Stats {
|
|
| 62 |
+ actValue, ok := actual.Stats[key] |
|
| 63 |
+ if !ok {
|
|
| 64 |
+ log.Printf("Expected memory stat key %s not found\n", key)
|
|
| 65 |
+ t.Fail() |
|
| 66 |
+ } |
|
| 67 |
+ if expValue != actValue {
|
|
| 68 |
+ log.Printf("Expected memory stat value %d but found %d\n", expValue, actValue)
|
|
| 69 |
+ t.Fail() |
|
| 70 |
+ } |
|
| 71 |
+ } |
|
| 72 |
+} |
| ... | ... |
@@ -8,7 +8,6 @@ package fs |
| 8 | 8 |
import ( |
| 9 | 9 |
"fmt" |
| 10 | 10 |
"io/ioutil" |
| 11 |
- "log" |
|
| 12 | 11 |
"os" |
| 13 | 12 |
"testing" |
| 14 | 13 |
) |
| ... | ... |
@@ -59,17 +58,3 @@ func (c *cgroupTestUtil) writeFileContents(fileContents map[string]string) {
|
| 59 | 59 |
} |
| 60 | 60 |
} |
| 61 | 61 |
} |
| 62 |
- |
|
| 63 |
-// Expect the specified stats. |
|
| 64 |
-func expectStats(t *testing.T, expected, actual map[string]int64) {
|
|
| 65 |
- for stat, expectedValue := range expected {
|
|
| 66 |
- actualValue, ok := actual[stat] |
|
| 67 |
- if !ok {
|
|
| 68 |
- log.Printf("Expected stat %s to exist: %s", stat, actual)
|
|
| 69 |
- t.Fail() |
|
| 70 |
- } else if actualValue != expectedValue {
|
|
| 71 |
- log.Printf("Expected stats %s to have value %f but had %f instead", stat, expectedValue, actualValue)
|
|
| 72 |
- t.Fail() |
|
| 73 |
- } |
|
| 74 |
- } |
|
| 75 |
-} |
| ... | ... |
@@ -16,11 +16,11 @@ var ( |
| 16 | 16 |
|
| 17 | 17 |
// Parses a cgroup param and returns as name, value |
| 18 | 18 |
// i.e. "io_service_bytes 1234" will return as io_service_bytes, 1234 |
| 19 |
-func getCgroupParamKeyValue(t string) (string, int64, error) {
|
|
| 19 |
+func getCgroupParamKeyValue(t string) (string, uint64, error) {
|
|
| 20 | 20 |
parts := strings.Fields(t) |
| 21 | 21 |
switch len(parts) {
|
| 22 | 22 |
case 2: |
| 23 |
- value, err := strconv.ParseInt(parts[1], 10, 64) |
|
| 23 |
+ value, err := strconv.ParseUint(parts[1], 10, 64) |
|
| 24 | 24 |
if err != nil {
|
| 25 | 25 |
return "", 0, fmt.Errorf("Unable to convert param value to int: %s", err)
|
| 26 | 26 |
} |
| ... | ... |
@@ -31,10 +31,10 @@ func getCgroupParamKeyValue(t string) (string, int64, error) {
|
| 31 | 31 |
} |
| 32 | 32 |
|
| 33 | 33 |
// Gets a single int64 value from the specified cgroup file. |
| 34 |
-func getCgroupParamInt(cgroupPath, cgroupFile string) (int64, error) {
|
|
| 34 |
+func getCgroupParamInt(cgroupPath, cgroupFile string) (uint64, error) {
|
|
| 35 | 35 |
contents, err := ioutil.ReadFile(filepath.Join(cgroupPath, cgroupFile)) |
| 36 | 36 |
if err != nil {
|
| 37 |
- return -1, err |
|
| 37 |
+ return 0, err |
|
| 38 | 38 |
} |
| 39 |
- return strconv.ParseInt(strings.TrimSpace(string(contents)), 10, 64) |
|
| 39 |
+ return strconv.ParseUint(strings.TrimSpace(string(contents)), 10, 64) |
|
| 40 | 40 |
} |
| ... | ... |
@@ -2,18 +2,18 @@ package cgroups |
| 2 | 2 |
|
| 3 | 3 |
type ThrottlingData struct {
|
| 4 | 4 |
// Number of periods with throttling active |
| 5 |
- Periods int64 `json:"periods,omitempty"` |
|
| 5 |
+ Periods uint64 `json:"periods,omitempty"` |
|
| 6 | 6 |
// Number of periods when the container hit its throttling limit. |
| 7 |
- ThrottledPeriods int64 `json:"throttled_periods,omitempty"` |
|
| 7 |
+ ThrottledPeriods uint64 `json:"throttled_periods,omitempty"` |
|
| 8 | 8 |
// Aggregate time the container was throttled for in nanoseconds. |
| 9 |
- ThrottledTime int64 `json:"throttled_time,omitempty"` |
|
| 9 |
+ ThrottledTime uint64 `json:"throttled_time,omitempty"` |
|
| 10 | 10 |
} |
| 11 | 11 |
|
| 12 | 12 |
type CpuUsage struct {
|
| 13 | 13 |
// percentage of available CPUs currently being used. |
| 14 |
- PercentUsage int64 `json:"percent_usage,omitempty"` |
|
| 14 |
+ PercentUsage uint64 `json:"percent_usage,omitempty"` |
|
| 15 | 15 |
// nanoseconds of cpu time consumed over the last 100 ms. |
| 16 |
- CurrentUsage int64 `json:"current_usage,omitempty"` |
|
| 16 |
+ CurrentUsage uint64 `json:"current_usage,omitempty"` |
|
| 17 | 17 |
} |
| 18 | 18 |
|
| 19 | 19 |
type CpuStats struct {
|
| ... | ... |
@@ -23,26 +23,27 @@ type CpuStats struct {
|
| 23 | 23 |
|
| 24 | 24 |
type MemoryStats struct {
|
| 25 | 25 |
// current res_counter usage for memory |
| 26 |
- Usage int64 `json:"usage,omitempty"` |
|
| 26 |
+ Usage uint64 `json:"usage,omitempty"` |
|
| 27 | 27 |
// maximum usage ever recorded. |
| 28 |
- MaxUsage int64 `json:"max_usage,omitempty"` |
|
| 28 |
+ MaxUsage uint64 `json:"max_usage,omitempty"` |
|
| 29 | 29 |
// TODO(vishh): Export these as stronger types. |
| 30 | 30 |
// all the stats exported via memory.stat. |
| 31 |
- Stats map[string]int64 `json:"stats,omitempty"` |
|
| 31 |
+ Stats map[string]uint64 `json:"stats,omitempty"` |
|
| 32 | 32 |
} |
| 33 | 33 |
|
| 34 | 34 |
type BlkioStatEntry struct {
|
| 35 |
- Major int64 `json:"major,omitempty"` |
|
| 36 |
- Minor int64 `json:"minor,omitempty"` |
|
| 35 |
+ Major uint64 `json:"major,omitempty"` |
|
| 36 |
+ Minor uint64 `json:"minor,omitempty"` |
|
| 37 | 37 |
Op string `json:"op,omitempty"` |
| 38 |
- Value int64 `json:"value,omitempty"` |
|
| 38 |
+ Value uint64 `json:"value,omitempty"` |
|
| 39 | 39 |
} |
| 40 | 40 |
|
| 41 |
-type BlockioStats struct {
|
|
| 41 |
+type BlkioStats struct {
|
|
| 42 | 42 |
// number of bytes tranferred to and from the block device |
| 43 | 43 |
IoServiceBytesRecursive []BlkioStatEntry `json:"io_service_bytes_recursive,omitempty"` |
| 44 | 44 |
IoServicedRecursive []BlkioStatEntry `json:"io_serviced_recusrive,omitempty"` |
| 45 | 45 |
IoQueuedRecursive []BlkioStatEntry `json:"io_queue_recursive,omitempty"` |
| 46 |
+ SectorsRecursive []BlkioStatEntry `json:"sectors_recursive,omitempty"` |
|
| 46 | 47 |
} |
| 47 | 48 |
|
| 48 | 49 |
// TODO(Vishh): Remove freezer from stats since it does not logically belong in stats. |
| ... | ... |
@@ -54,6 +55,11 @@ type FreezerStats struct {
|
| 54 | 54 |
type Stats struct {
|
| 55 | 55 |
CpuStats CpuStats `json:"cpu_stats,omitempty"` |
| 56 | 56 |
MemoryStats MemoryStats `json:"memory_stats,omitempty"` |
| 57 |
- BlockioStats BlockioStats `json:"blockio_stats,omitempty"` |
|
| 57 |
+ BlkioStats BlkioStats `json:"blkio_stats,omitempty"` |
|
| 58 | 58 |
FreezerStats FreezerStats `json:"freezer_stats,omitempty"` |
| 59 | 59 |
} |
| 60 |
+ |
|
| 61 |
+func NewStats() *Stats {
|
|
| 62 |
+ memoryStats := MemoryStats{Stats: make(map[string]uint64)}
|
|
| 63 |
+ return &Stats{MemoryStats: memoryStats}
|
|
| 64 |
+} |