cpuacct.go (2878B)
1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package cgroup1 18 19 import ( 20 "bufio" 21 "fmt" 22 "os" 23 "path/filepath" 24 "strconv" 25 "strings" 26 27 v1 "github.com/containerd/cgroups/v3/cgroup1/stats" 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 }