cpuacct.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. // +build linux
  2. package fs
  3. import (
  4. "fmt"
  5. "io/ioutil"
  6. "path/filepath"
  7. "strconv"
  8. "strings"
  9. "github.com/opencontainers/runc/libcontainer/cgroups"
  10. "github.com/opencontainers/runc/libcontainer/configs"
  11. "github.com/opencontainers/runc/libcontainer/system"
  12. )
  13. const (
  14. cgroupCpuacctStat = "cpuacct.stat"
  15. nanosecondsInSecond = 1000000000
  16. )
  17. var clockTicks = uint64(system.GetClockTicks())
  18. type CpuacctGroup struct {
  19. }
  20. func (s *CpuacctGroup) Name() string {
  21. return "cpuacct"
  22. }
  23. func (s *CpuacctGroup) Apply(d *cgroupData) error {
  24. // we just want to join this group even though we don't set anything
  25. if _, err := d.join("cpuacct"); err != nil && !cgroups.IsNotFound(err) {
  26. return err
  27. }
  28. return nil
  29. }
  30. func (s *CpuacctGroup) Set(path string, cgroup *configs.Cgroup) error {
  31. return nil
  32. }
  33. func (s *CpuacctGroup) Remove(d *cgroupData) error {
  34. return removePath(d.path("cpuacct"))
  35. }
  36. func (s *CpuacctGroup) GetStats(path string, stats *cgroups.Stats) error {
  37. userModeUsage, kernelModeUsage, err := getCpuUsageBreakdown(path)
  38. if err != nil {
  39. return err
  40. }
  41. totalUsage, err := getCgroupParamUint(path, "cpuacct.usage")
  42. if err != nil {
  43. return err
  44. }
  45. percpuUsage, err := getPercpuUsage(path)
  46. if err != nil {
  47. return err
  48. }
  49. stats.CpuStats.CpuUsage.TotalUsage = totalUsage
  50. stats.CpuStats.CpuUsage.PercpuUsage = percpuUsage
  51. stats.CpuStats.CpuUsage.UsageInUsermode = userModeUsage
  52. stats.CpuStats.CpuUsage.UsageInKernelmode = kernelModeUsage
  53. return nil
  54. }
  55. // Returns user and kernel usage breakdown in nanoseconds.
  56. func getCpuUsageBreakdown(path string) (uint64, uint64, error) {
  57. userModeUsage := uint64(0)
  58. kernelModeUsage := uint64(0)
  59. const (
  60. userField = "user"
  61. systemField = "system"
  62. )
  63. // Expected format:
  64. // user <usage in ticks>
  65. // system <usage in ticks>
  66. data, err := ioutil.ReadFile(filepath.Join(path, cgroupCpuacctStat))
  67. if err != nil {
  68. return 0, 0, err
  69. }
  70. fields := strings.Fields(string(data))
  71. if len(fields) != 4 {
  72. return 0, 0, fmt.Errorf("failure - %s is expected to have 4 fields", filepath.Join(path, cgroupCpuacctStat))
  73. }
  74. if fields[0] != userField {
  75. return 0, 0, fmt.Errorf("unexpected field %q in %q, expected %q", fields[0], cgroupCpuacctStat, userField)
  76. }
  77. if fields[2] != systemField {
  78. return 0, 0, fmt.Errorf("unexpected field %q in %q, expected %q", fields[2], cgroupCpuacctStat, systemField)
  79. }
  80. if userModeUsage, err = strconv.ParseUint(fields[1], 10, 64); err != nil {
  81. return 0, 0, err
  82. }
  83. if kernelModeUsage, err = strconv.ParseUint(fields[3], 10, 64); err != nil {
  84. return 0, 0, err
  85. }
  86. return (userModeUsage * nanosecondsInSecond) / clockTicks, (kernelModeUsage * nanosecondsInSecond) / clockTicks, nil
  87. }
  88. func getPercpuUsage(path string) ([]uint64, error) {
  89. percpuUsage := []uint64{}
  90. data, err := ioutil.ReadFile(filepath.Join(path, "cpuacct.usage_percpu"))
  91. if err != nil {
  92. return percpuUsage, err
  93. }
  94. for _, value := range strings.Fields(string(data)) {
  95. value, err := strconv.ParseUint(value, 10, 64)
  96. if err != nil {
  97. return percpuUsage, fmt.Errorf("Unable to convert param value to uint64: %s", err)
  98. }
  99. percpuUsage = append(percpuUsage, value)
  100. }
  101. return percpuUsage, nil
  102. }