@@ -6,6 +6,215 @@ import (
66 "github.com/opencontainers/cgroups"
77)
88
9+ // pointerTo returns a pointer to the given controller value.
10+ func pointerTo (c cgroups.Controller ) * cgroups.Controller {
11+ return & c
12+ }
13+
14+ func TestStats (t * testing.T ) {
15+ testCases := []struct {
16+ name string
17+ controller * cgroups.Controller
18+ subsystems map [string ]map [string ]string // subsystem -> file contents
19+ validate func (* testing.T , * cgroups.Stats )
20+ }{
21+ {
22+ name : "CPU stats" ,
23+ controller : pointerTo (cgroups .CPU ),
24+ subsystems : map [string ]map [string ]string {
25+ "cpu" : {
26+ "cpu.stat" : "nr_periods 2000\n nr_throttled 200\n throttled_time 18446744073709551615\n " ,
27+ },
28+ "cpuacct" : {
29+ "cpuacct.usage" : cpuAcctUsageContents ,
30+ "cpuacct.usage_percpu" : cpuAcctUsagePerCPUContents ,
31+ "cpuacct.stat" : cpuAcctStatContents ,
32+ },
33+ },
34+ validate : func (t * testing.T , stats * cgroups.Stats ) {
35+ // Verify throttling data from cpu.stat
36+ expectedThrottling := cgroups.ThrottlingData {
37+ Periods : 2000 ,
38+ ThrottledPeriods : 200 ,
39+ ThrottledTime : 18446744073709551615 ,
40+ }
41+ expectThrottlingDataEquals (t , expectedThrottling , stats .CpuStats .ThrottlingData )
42+
43+ // Verify total usage from cpuacct.usage
44+ if stats .CpuStats .CpuUsage .TotalUsage != 12262454190222160 {
45+ t .Errorf ("expected TotalUsage 12262454190222160, got %d" , stats .CpuStats .CpuUsage .TotalUsage )
46+ }
47+ },
48+ },
49+ {
50+ name : "Memory stats" ,
51+ controller : pointerTo (cgroups .Memory ),
52+ subsystems : map [string ]map [string ]string {
53+ "memory" : {
54+ "memory.stat" : memoryStatContents ,
55+ "memory.usage_in_bytes" : "2048" ,
56+ "memory.max_usage_in_bytes" : "4096" ,
57+ "memory.failcnt" : "100" ,
58+ "memory.limit_in_bytes" : "8192" ,
59+ "memory.use_hierarchy" : "1" ,
60+ },
61+ },
62+ validate : func (t * testing.T , stats * cgroups.Stats ) {
63+ expected := cgroups.MemoryData {Usage : 2048 , MaxUsage : 4096 , Failcnt : 100 , Limit : 8192 }
64+ expectMemoryDataEquals (t , expected , stats .MemoryStats .Usage )
65+ },
66+ },
67+ {
68+ name : "Pids stats" ,
69+ controller : pointerTo (cgroups .Pids ),
70+ subsystems : map [string ]map [string ]string {
71+ "pids" : {
72+ "pids.current" : "1337" ,
73+ "pids.max" : "1024" ,
74+ },
75+ },
76+ validate : func (t * testing.T , stats * cgroups.Stats ) {
77+ if stats .PidsStats .Current != 1337 {
78+ t .Errorf ("expected Current 1337, got %d" , stats .PidsStats .Current )
79+ }
80+ if stats .PidsStats .Limit != 1024 {
81+ t .Errorf ("expected Limit 1024, got %d" , stats .PidsStats .Limit )
82+ }
83+ },
84+ },
85+ {
86+ name : "IO stats" ,
87+ controller : pointerTo (cgroups .IO ),
88+ subsystems : map [string ]map [string ]string {
89+ "blkio" : blkioBFQStatsTestFiles ,
90+ },
91+ validate : func (t * testing.T , stats * cgroups.Stats ) {
92+ // Verify we have entries
93+ if len (stats .BlkioStats .IoServiceBytesRecursive ) == 0 {
94+ t .Error ("expected IoServiceBytesRecursive to have entries" )
95+ }
96+ if len (stats .BlkioStats .IoServicedRecursive ) == 0 {
97+ t .Error ("expected IoServicedRecursive to have entries" )
98+ }
99+ },
100+ },
101+ {
102+ name : "Multiple controllers - CPU+Pids" ,
103+ controller : pointerTo (cgroups .CPU | cgroups .Pids ),
104+ subsystems : map [string ]map [string ]string {
105+ "cpu" : {
106+ "cpu.stat" : "nr_periods 100\n nr_throttled 10\n throttled_time 5000\n " ,
107+ },
108+ "pids" : {
109+ "pids.current" : "42" ,
110+ "pids.max" : "1000" ,
111+ },
112+ },
113+ validate : func (t * testing.T , stats * cgroups.Stats ) {
114+ // Verify both are populated
115+ if stats .CpuStats .ThrottlingData .Periods != 100 {
116+ t .Errorf ("expected Periods 100, got %d" , stats .CpuStats .ThrottlingData .Periods )
117+ }
118+ if stats .PidsStats .Current != 42 {
119+ t .Errorf ("expected Current 42, got %d" , stats .PidsStats .Current )
120+ }
121+ if stats .PidsStats .Limit != 1000 {
122+ t .Errorf ("expected Limit 1000, got %d" , stats .PidsStats .Limit )
123+ }
124+ },
125+ },
126+ {
127+ name : "All controllers with nil options" ,
128+ controller : nil , // nil means all controllers (default behavior)
129+ subsystems : map [string ]map [string ]string {
130+ "cpu" : {
131+ "cpu.stat" : "nr_periods 2000\n nr_throttled 200\n throttled_time 18446744073709551615\n " ,
132+ },
133+ "cpuacct" : {
134+ "cpuacct.usage" : cpuAcctUsageContents ,
135+ "cpuacct.usage_percpu" : cpuAcctUsagePerCPUContents ,
136+ "cpuacct.stat" : cpuAcctStatContents ,
137+ },
138+ "memory" : {
139+ "memory.stat" : memoryStatContents ,
140+ "memory.usage_in_bytes" : "2048" ,
141+ "memory.max_usage_in_bytes" : "4096" ,
142+ "memory.failcnt" : "100" ,
143+ "memory.limit_in_bytes" : "8192" ,
144+ "memory.use_hierarchy" : "1" ,
145+ },
146+ "pids" : {
147+ "pids.current" : "1337" ,
148+ "pids.max" : "1024" ,
149+ },
150+ "blkio" : blkioBFQStatsTestFiles ,
151+ },
152+ validate : func (t * testing.T , stats * cgroups.Stats ) {
153+ // Verify CPU stats
154+ expectedThrottling := cgroups.ThrottlingData {
155+ Periods : 2000 ,
156+ ThrottledPeriods : 200 ,
157+ ThrottledTime : 18446744073709551615 ,
158+ }
159+ expectThrottlingDataEquals (t , expectedThrottling , stats .CpuStats .ThrottlingData )
160+ if stats .CpuStats .CpuUsage .TotalUsage != 12262454190222160 {
161+ t .Errorf ("expected TotalUsage 12262454190222160, got %d" , stats .CpuStats .CpuUsage .TotalUsage )
162+ }
163+
164+ // Verify Memory stats
165+ expectedMemory := cgroups.MemoryData {Usage : 2048 , MaxUsage : 4096 , Failcnt : 100 , Limit : 8192 }
166+ expectMemoryDataEquals (t , expectedMemory , stats .MemoryStats .Usage )
167+
168+ // Verify Pids stats
169+ if stats .PidsStats .Current != 1337 {
170+ t .Errorf ("expected Current 1337, got %d" , stats .PidsStats .Current )
171+ }
172+ if stats .PidsStats .Limit != 1024 {
173+ t .Errorf ("expected Limit 1024, got %d" , stats .PidsStats .Limit )
174+ }
175+
176+ // Verify IO stats
177+ if len (stats .BlkioStats .IoServiceBytesRecursive ) == 0 {
178+ t .Error ("expected IoServiceBytesRecursive to have entries" )
179+ }
180+ if len (stats .BlkioStats .IoServicedRecursive ) == 0 {
181+ t .Error ("expected IoServicedRecursive to have entries" )
182+ }
183+ },
184+ },
185+ }
186+
187+ for _ , tc := range testCases {
188+ t .Run (tc .name , func (t * testing.T ) {
189+ // Create temp directories for each subsystem and write files
190+ paths := make (map [string ]string )
191+ for subsystem , files := range tc .subsystems {
192+ path := tempDir (t , subsystem )
193+ writeFileContents (t , path , files )
194+ paths [subsystem ] = path
195+ }
196+ m := & Manager {
197+ cgroups : & cgroups.Cgroup {Resources : & cgroups.Resources {}},
198+ paths : paths ,
199+ }
200+
201+ var stats * cgroups.Stats
202+ var err error
203+ if tc .controller != nil {
204+ stats , err = m .Stats (& cgroups.StatsOptions {Controllers : * tc .controller })
205+ } else {
206+ stats , err = m .Stats (nil )
207+ }
208+ if err != nil {
209+ t .Fatal (err )
210+ }
211+
212+ // Validate the results
213+ tc .validate (t , stats )
214+ })
215+ }
216+ }
217+
9218func BenchmarkGetStats (b * testing.B ) {
10219 if cgroups .IsCgroup2UnifiedMode () {
11220 b .Skip ("cgroup v2 is not supported" )
0 commit comments