1
16
17 package cgroups
18
19 import (
20 "bufio"
21 "fmt"
22 "os"
23 "path/filepath"
24 "strconv"
25 "strings"
26
27 v1 "github.com/containerd/cgroups/stats/v1"
28 )
29
30 const nanosecondsInSecond = 1000000000
31
32 var clockTicks = getClockTicks()
33
34 func NewCpuacct(root string) *cpuacctController {
35 return &cpuacctController{
36 root: filepath.Join(root, string(Cpuacct)),
37 }
38 }
39
40 type cpuacctController struct {
41 root string
42 }
43
44 func (c *cpuacctController) Name() Name {
45 return Cpuacct
46 }
47
48 func (c *cpuacctController) Path(path string) string {
49 return filepath.Join(c.root, path)
50 }
51
52 func (c *cpuacctController) Stat(path string, stats *v1.Metrics) error {
53 user, kernel, err := c.getUsage(path)
54 if err != nil {
55 return err
56 }
57 total, err := readUint(filepath.Join(c.Path(path), "cpuacct.usage"))
58 if err != nil {
59 return err
60 }
61 percpu, err := c.percpuUsage(path)
62 if err != nil {
63 return err
64 }
65 stats.CPU.Usage.Total = total
66 stats.CPU.Usage.User = user
67 stats.CPU.Usage.Kernel = kernel
68 stats.CPU.Usage.PerCPU = percpu
69 return nil
70 }
71
72 func (c *cpuacctController) percpuUsage(path string) ([]uint64, error) {
73 var usage []uint64
74 data, err := os.ReadFile(filepath.Join(c.Path(path), "cpuacct.usage_percpu"))
75 if err != nil {
76 return nil, err
77 }
78 for _, v := range strings.Fields(string(data)) {
79 u, err := strconv.ParseUint(v, 10, 64)
80 if err != nil {
81 return nil, err
82 }
83 usage = append(usage, u)
84 }
85 return usage, nil
86 }
87
88 func (c *cpuacctController) getUsage(path string) (user uint64, kernel uint64, err error) {
89 statPath := filepath.Join(c.Path(path), "cpuacct.stat")
90 f, err := os.Open(statPath)
91 if err != nil {
92 return 0, 0, err
93 }
94 defer f.Close()
95 var (
96 raw = make(map[string]uint64)
97 sc = bufio.NewScanner(f)
98 )
99 for sc.Scan() {
100 key, v, err := parseKV(sc.Text())
101 if err != nil {
102 return 0, 0, err
103 }
104 raw[key] = v
105 }
106 if err := sc.Err(); err != nil {
107 return 0, 0, err
108 }
109 for _, t := range []struct {
110 name string
111 value *uint64
112 }{
113 {
114 name: "user",
115 value: &user,
116 },
117 {
118 name: "system",
119 value: &kernel,
120 },
121 } {
122 v, ok := raw[t.name]
123 if !ok {
124 return 0, 0, fmt.Errorf("expected field %q but not found in %q", t.name, statPath)
125 }
126 *t.value = v
127 }
128 return (user * nanosecondsInSecond) / clockTicks, (kernel * nanosecondsInSecond) / clockTicks, nil
129 }
130
View as plain text