66 "flag"
77 "fmt"
88 "io"
9+ "io/ioutil"
910 "os"
10- "path"
1111 "path/filepath"
1212 "strconv"
1313 "strings"
@@ -18,7 +18,6 @@ import (
1818 units "github.com/docker/go-units"
1919 "github.com/jessfraz/magneto/types"
2020 "github.com/jessfraz/magneto/version"
21- "github.com/opencontainers/runc/libcontainer/cgroups"
2221 "github.com/opencontainers/runc/libcontainer/system"
2322 specs "github.com/opencontainers/runtime-spec/specs-go"
2423 "github.com/sirupsen/logrus"
@@ -42,6 +41,8 @@ const (
4241 specFile = "config.json"
4342 stateFile = "state.json"
4443 defaultRoot = "/run/runc"
44+
45+ nanoSecondsPerSecond = 1e9
4546)
4647
4748var (
@@ -89,8 +90,8 @@ type containerStats struct {
8990 MemoryPercentage float64
9091 NetworkRx float64
9192 NetworkTx float64
92- BlockRead float64
93- BlockWrite float64
93+ BlockRead uint64
94+ BlockWrite uint64
9495 PidsCurrent uint64
9596 mu sync.RWMutex
9697 bufReader * bufio.Reader
@@ -112,12 +113,13 @@ func main() {
112113 clockTicksPerSecond : uint64 (system .GetClockTicks ()),
113114 bufReader : bufio .NewReaderSize (nil , 128 ),
114115 }
115- go s .Collect ()
116+
117+ go s .collect ()
116118
117119 for range time .Tick (5 * time .Second ) {
118120 printHeader ()
119121 if err := s .Display (w ); err != nil {
120- logrus .Errorf ( "Displaying stats failed: %v" , err )
122+ logrus .Error ( err )
121123 }
122124 w .Flush ()
123125 }
@@ -133,44 +135,42 @@ func usageAndExit(message string, exitCode int) {
133135 os .Exit (exitCode )
134136}
135137
136- func (s * containerStats ) Collect () {
138+ func (s * containerStats ) collect () {
137139 var (
138140 previousCPU uint64
139141 previousSystem uint64
140142 dec = json .NewDecoder (os .Stdin )
141143 u = make (chan error , 1 )
142144 )
145+
143146 go func () {
144147 for {
145- var e event
148+ var (
149+ e event
150+ memPercent , cpuPercent float64
151+ blkRead , blkWrite uint64 // Only used on Linux
152+ mem , memLimit float64
153+ netRx , netTx float64
154+ pidsCurrent uint64
155+ )
156+
146157 if err := dec .Decode (& e ); err != nil {
147158 u <- err
148- return
159+ time .Sleep (100 * time .Millisecond )
160+ continue
149161 }
162+
150163 if e .Type != "stats" {
151164 // do nothing since there are no other events yet
152- return
153- }
154-
155- var memPercent = 0.0
156- var cpuPercent = 0.0
157-
158- v := e .Data .CgroupStats
159- if v == nil {
160- return
165+ continue
161166 }
167+ v := e .Data
162168
163- resources , err := getContainerResources (e .ID )
169+ /* resources, err := getContainerResources(e.ID)
164170 if err != nil {
165171 u <- fmt.Errorf("Getting container's configured resources failed: %v", err)
166- return
167- }
168-
169- // MemoryStats.Limit will never be 0 unless the container is not running and we haven't
170- // got any data from cgroup
171- if int (* resources .Memory .Limit ) != 0 {
172- memPercent = float64 (v .MemoryStats .Usage .Usage ) / float64 (* resources .Memory .Limit ) * 100.0
173- }
172+ continue
173+ }*/
174174
175175 systemUsage , err := s .getSystemCPUUsage ()
176176 if err != nil {
@@ -179,68 +179,93 @@ func (s *containerStats) Collect() {
179179 }
180180
181181 cpuPercent = calculateCPUPercent (previousCPU , previousSystem , systemUsage , v )
182- previousCPU = v .CpuStats . CpuUsage . TotalUsage
182+ previousCPU = v .CPU . Usage . Total
183183 previousSystem = systemUsage
184- blkRead , blkWrite := calculateBlockIO (v .BlkioStats )
184+
185+ blkRead , blkWrite = calculateBlockIO (v .Blkio )
186+
187+ mem = calculateMemUsageNoCache (v .Memory )
188+ memLimit = float64 (v .Memory .Usage .Limit )
189+ memPercent = calculateMemPercentNoCache (s .MemoryLimit , s .Memory )
190+
191+ //netRx, netTx = calculateNetwork(e.Data.Interfaces)
192+
193+ pidsCurrent = v .Pids .Current
194+
195+ // set the stats
185196 s .mu .Lock ()
186197 s .CPUPercentage = cpuPercent
187- s .Memory = float64 (v .MemoryStats .Usage .Usage )
188- s .MemoryLimit = float64 (* resources .Memory .Limit )
198+ s .BlockRead = blkRead
199+ s .BlockWrite = blkWrite
200+ s .Memory = mem
201+ s .MemoryLimit = memLimit
189202 s .MemoryPercentage = memPercent
190- s .NetworkRx , s .NetworkTx = calculateNetwork (e .Data .Interfaces )
191- s .BlockRead = float64 (blkRead )
192- s .BlockWrite = float64 (blkWrite )
193- s .PidsCurrent = v .PidsStats .Current
203+ s .NetworkRx = netRx
204+ s .NetworkTx = netTx
205+ s .PidsCurrent = pidsCurrent
194206 s .mu .Unlock ()
207+
195208 u <- nil
196209 }
197210 }()
211+
198212 for {
199213 select {
200214 case err := <- u :
201- if err != nil {
202- s .mu .Lock ()
203- s .err = err
204- s .mu .Unlock ()
205- logrus .Fatal (err )
206- return
207- }
215+ s .setError (err )
216+ continue
208217 }
209218 }
210219}
211220
221+ var it = 0
222+
212223func (s * containerStats ) Display (w io.Writer ) error {
213224 s .mu .RLock ()
214225 defer s .mu .RUnlock ()
226+
227+ // check the error here
215228 if s .err != nil {
216229 return s .err
217230 }
218- fmt .Fprintf (w , "%.2f%%\t %s / %s\t %.2f%%\t %s / %s\t %s / %s\t %d\n " ,
231+
232+ fmt .Fprintf (w , "%.2f%%\t %s / %s\t %.2f%%\t %s / %s\t %d / %d\t %d\n " ,
219233 s .CPUPercentage ,
220234 units .HumanSize (s .Memory ), units .HumanSize (s .MemoryLimit ),
221235 s .MemoryPercentage ,
222236 units .HumanSize (s .NetworkRx ), units .HumanSize (s .NetworkTx ),
223- units . HumanSize ( s .BlockRead ), units . HumanSize ( s .BlockWrite ) ,
237+ s .BlockRead , s .BlockWrite ,
224238 s .PidsCurrent )
239+
240+ logrus .Infof ("displayed stats %d" , it )
241+ it ++
225242 return nil
226243}
227244
228- func calculateCPUPercent (previousCPU , previousSystem , systemUsage uint64 , v * cgroups.Stats ) float64 {
245+ // setError sets container statistics error
246+ func (s * containerStats ) setError (err error ) {
247+ s .mu .Lock ()
248+ defer s .mu .Unlock ()
249+ s .err = err
250+ }
251+
252+ func calculateCPUPercent (previousCPU , previousSystem , systemUsage uint64 , v types.Stats ) float64 {
229253 var (
230254 cpuPercent = 0.0
231255 // calculate the change for the cpu usage of the container in between readings
232- cpuDelta = float64 (v .CpuStats . CpuUsage . TotalUsage ) - float64 (previousCPU )
256+ cpuDelta = float64 (v .CPU . Usage . Total ) - float64 (previousCPU )
233257 // calculate the change for the entire system between readings
234258 systemDelta = float64 (systemUsage ) - float64 (previousSystem )
235259 )
236260
237261 if systemDelta > 0.0 && cpuDelta > 0.0 {
238- cpuPercent = (cpuDelta / systemDelta ) * float64 (len (v .CpuStats . CpuUsage . PercpuUsage )) * 100.0
262+ cpuPercent = (cpuDelta / systemDelta ) * float64 (len (v .CPU . Usage . Percpu )) * 100.0
239263 }
240264 return cpuPercent
241265}
242266
243- func calculateBlockIO (blkio cgroups.BlkioStats ) (blkRead uint64 , blkWrite uint64 ) {
267+ func calculateBlockIO (blkio types.Blkio ) (uint64 , uint64 ) {
268+ var blkRead , blkWrite uint64
244269 for _ , bioEntry := range blkio .IoServiceBytesRecursive {
245270 switch strings .ToLower (bioEntry .Op ) {
246271 case "read" :
@@ -249,7 +274,7 @@ func calculateBlockIO(blkio cgroups.BlkioStats) (blkRead uint64, blkWrite uint64
249274 blkWrite = blkWrite + bioEntry .Value
250275 }
251276 }
252- return
277+ return blkRead , blkWrite
253278}
254279
255280func calculateNetwork (network []* types.NetworkInterface ) (float64 , float64 ) {
@@ -262,7 +287,20 @@ func calculateNetwork(network []*types.NetworkInterface) (float64, float64) {
262287 return rx , tx
263288}
264289
265- const nanoSecondsPerSecond = 1e9
290+ // calculateMemUsageNoCache calculate memory usage of the container.
291+ // Page cache is intentionally excluded to avoid misinterpretation of the output.
292+ func calculateMemUsageNoCache (mem types.Memory ) float64 {
293+ return float64 (mem .Usage .Usage - mem .Cache )
294+ }
295+
296+ func calculateMemPercentNoCache (limit float64 , usedNoCache float64 ) float64 {
297+ // MemoryStats.Limit will never be 0 unless the container is not running and we haven't
298+ // got any data from cgroup
299+ if limit != 0 {
300+ return usedNoCache / limit * 100.0
301+ }
302+ return 0
303+ }
266304
267305// getSystemCPUUsage returns the host system's cpu usage in
268306// nanoseconds. An error is returned if the format of the underlying
@@ -293,21 +331,22 @@ func (s *containerStats) getSystemCPUUsage() (uint64, error) {
293331 switch parts [0 ] {
294332 case "cpu" :
295333 if len (parts ) < 8 {
296- return 0 , fmt .Errorf ("Bad CPU fields" )
334+ return 0 , fmt .Errorf ("invalid number of cpu fields" )
297335 }
298336 var totalClockTicks uint64
299337 for _ , i := range parts [1 :8 ] {
300338 v , err := strconv .ParseUint (i , 10 , 64 )
301339 if err != nil {
302- return 0 , fmt .Errorf ("Bad CPU int %s : %v " , i , err )
340+ return 0 , fmt .Errorf ("Unable to convert value %s to int : %s " , i , err )
303341 }
304342 totalClockTicks += v
305343 }
306344 return (totalClockTicks * nanoSecondsPerSecond ) /
307345 s .clockTicksPerSecond , nil
308346 }
309347 }
310- return 0 , fmt .Errorf ("Bad stat file format" )
348+
349+ return 0 , fmt .Errorf ("invalid stat format. Error trying to parse the '/proc/stat' file" )
311350}
312351
313352func getContainerResources (id string ) (* specs.LinuxResources , error ) {
@@ -317,7 +356,7 @@ func getContainerResources(id string) (*specs.LinuxResources, error) {
317356 }
318357
319358 // check to make sure a container exists with this ID
320- statePath := path .Join (abs , id , stateFile )
359+ statePath := filepath .Join (abs , id , stateFile )
321360
322361 // read the state.json for the container so we can find out the bundle path
323362 f , err := os .Open (statePath )
@@ -334,7 +373,7 @@ func getContainerResources(id string) (*specs.LinuxResources, error) {
334373 }
335374
336375 bundle := searchLabels (state .Config .Labels , "bundle" )
337- specPath := path .Join (bundle , specFile )
376+ specPath := filepath .Join (bundle , specFile )
338377
339378 // read the runtime.json for the container so we know things like limits set
340379 // this is only if a container ID is not passed we assume we are in a directory
@@ -351,6 +390,18 @@ func getContainerResources(id string) (*specs.LinuxResources, error) {
351390 if err = json .NewDecoder (f ).Decode (& spec ); err != nil {
352391 return nil , err
353392 }
393+ if spec .Linux .Resources .Memory .Limit == nil {
394+ // set the memory limit manually
395+ b , err := ioutil .ReadFile (filepath .Join (state .CgroupPaths ["memory" ], "memory.limit_in_bytes" ))
396+ if err != nil {
397+ return nil , err
398+ }
399+ i , err := units .RAMInBytes (strings .TrimSpace (string (b )) + "b" )
400+ if err != nil {
401+ return nil , err
402+ }
403+ spec .Linux .Resources .Memory .Limit = & i
404+ }
354405 return spec .Linux .Resources , nil
355406}
356407
0 commit comments