1 // Copyright 2018 The Prometheus Authors
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
6 // http://www.apache.org/licenses/LICENSE-2.0
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
25 // CPUStat shows how much time the cpu spend in various stages.
39 // SoftIRQStat represent the softirq statistics as exported in the procfs stat file.
40 // A nice introduction can be found at https://0xax.gitbooks.io/linux-insides/content/interrupts/interrupts-9.html
41 // It is possible to get per-cpu stats by reading /proc/softirqs
42 type SoftIRQStat struct {
55 // Stat represents kernel/system statistics.
57 // Boot time in seconds since the Epoch.
59 // Summed up cpu statistics.
61 // Per-CPU statistics.
63 // Number of times interrupts were handled, which contains numbered and unnumbered IRQs.
65 // Number of times a numbered IRQ was triggered.
67 // Number of times a context switch happened.
68 ContextSwitches uint64
69 // Number of times a process was created.
71 // Number of processes currently running.
72 ProcessesRunning uint64
73 // Number of processes currently blocked (waiting for IO).
74 ProcessesBlocked uint64
75 // Number of times a softirq was scheduled.
77 // Detailed softirq statistics.
81 // NewStat returns kernel/system statistics read from /proc/stat.
82 func NewStat() (Stat, error) {
83 fs, err := NewFS(DefaultMountPoint)
91 // Parse a cpu statistics line and returns the CPUStat struct plus the cpu id (or -1 for the overall sum).
92 func parseCPUStat(line string) (CPUStat, int64, error) {
96 count, err := fmt.Sscanf(line, "%s %f %f %f %f %f %f %f %f %f %f",
98 &cpuStat.User, &cpuStat.Nice, &cpuStat.System, &cpuStat.Idle,
99 &cpuStat.Iowait, &cpuStat.IRQ, &cpuStat.SoftIRQ, &cpuStat.Steal,
100 &cpuStat.Guest, &cpuStat.GuestNice)
102 if err != nil && err != io.EOF {
103 return CPUStat{}, -1, fmt.Errorf("couldn't parse %s (cpu): %s", line, err)
106 return CPUStat{}, -1, fmt.Errorf("couldn't parse %s (cpu): 0 elements parsed", line)
109 cpuStat.User /= userHZ
110 cpuStat.Nice /= userHZ
111 cpuStat.System /= userHZ
112 cpuStat.Idle /= userHZ
113 cpuStat.Iowait /= userHZ
114 cpuStat.IRQ /= userHZ
115 cpuStat.SoftIRQ /= userHZ
116 cpuStat.Steal /= userHZ
117 cpuStat.Guest /= userHZ
118 cpuStat.GuestNice /= userHZ
121 return cpuStat, -1, nil
124 cpuID, err := strconv.ParseInt(cpu[3:], 10, 64)
126 return CPUStat{}, -1, fmt.Errorf("couldn't parse %s (cpu/cpuid): %s", line, err)
129 return cpuStat, cpuID, nil
132 // Parse a softirq line.
133 func parseSoftIRQStat(line string) (SoftIRQStat, uint64, error) {
134 softIRQStat := SoftIRQStat{}
138 _, err := fmt.Sscanf(line, "%s %d %d %d %d %d %d %d %d %d %d %d",
140 &softIRQStat.Hi, &softIRQStat.Timer, &softIRQStat.NetTx, &softIRQStat.NetRx,
141 &softIRQStat.Block, &softIRQStat.BlockIoPoll,
142 &softIRQStat.Tasklet, &softIRQStat.Sched,
143 &softIRQStat.Hrtimer, &softIRQStat.Rcu)
146 return SoftIRQStat{}, 0, fmt.Errorf("couldn't parse %s (softirq): %s", line, err)
149 return softIRQStat, total, nil
152 // NewStat returns an information about current kernel/system statistics.
153 func (fs FS) NewStat() (Stat, error) {
154 // See https://www.kernel.org/doc/Documentation/filesystems/proc.txt
156 f, err := os.Open(fs.Path("stat"))
164 scanner := bufio.NewScanner(f)
166 line := scanner.Text()
167 parts := strings.Fields(scanner.Text())
168 // require at least <key> <value>
173 case parts[0] == "btime":
174 if stat.BootTime, err = strconv.ParseUint(parts[1], 10, 64); err != nil {
175 return Stat{}, fmt.Errorf("couldn't parse %s (btime): %s", parts[1], err)
177 case parts[0] == "intr":
178 if stat.IRQTotal, err = strconv.ParseUint(parts[1], 10, 64); err != nil {
179 return Stat{}, fmt.Errorf("couldn't parse %s (intr): %s", parts[1], err)
181 numberedIRQs := parts[2:]
182 stat.IRQ = make([]uint64, len(numberedIRQs))
183 for i, count := range numberedIRQs {
184 if stat.IRQ[i], err = strconv.ParseUint(count, 10, 64); err != nil {
185 return Stat{}, fmt.Errorf("couldn't parse %s (intr%d): %s", count, i, err)
188 case parts[0] == "ctxt":
189 if stat.ContextSwitches, err = strconv.ParseUint(parts[1], 10, 64); err != nil {
190 return Stat{}, fmt.Errorf("couldn't parse %s (ctxt): %s", parts[1], err)
192 case parts[0] == "processes":
193 if stat.ProcessCreated, err = strconv.ParseUint(parts[1], 10, 64); err != nil {
194 return Stat{}, fmt.Errorf("couldn't parse %s (processes): %s", parts[1], err)
196 case parts[0] == "procs_running":
197 if stat.ProcessesRunning, err = strconv.ParseUint(parts[1], 10, 64); err != nil {
198 return Stat{}, fmt.Errorf("couldn't parse %s (procs_running): %s", parts[1], err)
200 case parts[0] == "procs_blocked":
201 if stat.ProcessesBlocked, err = strconv.ParseUint(parts[1], 10, 64); err != nil {
202 return Stat{}, fmt.Errorf("couldn't parse %s (procs_blocked): %s", parts[1], err)
204 case parts[0] == "softirq":
205 softIRQStats, total, err := parseSoftIRQStat(line)
209 stat.SoftIRQTotal = total
210 stat.SoftIRQ = softIRQStats
211 case strings.HasPrefix(parts[0], "cpu"):
212 cpuStat, cpuID, err := parseCPUStat(line)
217 stat.CPUTotal = cpuStat
219 for int64(len(stat.CPU)) <= cpuID {
220 stat.CPU = append(stat.CPU, CPUStat{})
222 stat.CPU[cpuID] = cpuStat
227 if err := scanner.Err(); err != nil {
228 return Stat{}, fmt.Errorf("couldn't parse %s: %s", f.Name(), err)