1 /* $OpenBSD: cpu.c,v 1.11 2021/10/25 19:54:29 kn Exp $ */
2
3 /*
4 * Copyright (c) 2013 Reyk Floeter <reyk@openbsd.org>
5 * Copyright (c) 2001, 2007 Can Erkin Acar <canacar@openbsd.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 /* CPU percentages() function from usr.bin/top/util.c:
21 *
22 * Top users/processes display for Unix
23 * Version 3
24 *
25 * Copyright (c) 1984, 1989, William LeFebvre, Rice University
26 * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
27 *
28 * Redistribution and use in source and binary forms, with or without
29 * modification, are permitted provided that the following conditions
30 * are met:
31 * 1. Redistributions of source code must retain the above copyright
32 * notice, this list of conditions and the following disclaimer.
33 * 2. Redistributions in binary form must reproduce the above copyright
34 * notice, this list of conditions and the following disclaimer in the
35 * documentation and/or other materials provided with the distribution.
36 *
37 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
38 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
40 * IN NO EVENT SHALL THE AUTHOR OR HIS EMPLOYER BE LIABLE FOR ANY DIRECT, INDIRECT,
41 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
42 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
43 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
45 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
46 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
47 */
48
49 #include <sys/signal.h>
50 #include <sys/sched.h>
51 #include <sys/sysctl.h>
52
53 #include <stdlib.h>
54 #include <stdint.h>
55 #include <string.h>
56 #include <unistd.h>
57 #include "systat.h"
58
59 void print_cpu(void);
60 int read_cpu(void);
61 int select_cpu(void);
62 static void cpu_info(void);
63 static void print_fld_percentage(field_def *, double);
64 static int percentages(int, int64_t *, int64_t *, int64_t *, int64_t *);
65
66 field_def fields_cpu[] = {
67 { "CPU", 4, 8, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0 },
68 { "User", 10, 20, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0 },
69 { "Nice", 10, 20, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0 },
70 { "System", 10, 20, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0 },
71 { "Spin", 10, 20, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0 },
72 { "Interrupt", 10, 20, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0 },
73 { "Idle", 10, 20, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0 },
74 };
75
76 #define FLD_CPU_CPU FIELD_ADDR(fields_cpu, 0)
77 #define FLD_CPU_USR FIELD_ADDR(fields_cpu, 1)
78 #define FLD_CPU_NIC FIELD_ADDR(fields_cpu, 2)
79 #define FLD_CPU_SYS FIELD_ADDR(fields_cpu, 3)
80 #define FLD_CPU_SPIN FIELD_ADDR(fields_cpu, 4)
81 #define FLD_CPU_INT FIELD_ADDR(fields_cpu, 5)
82 #define FLD_CPU_IDLE FIELD_ADDR(fields_cpu, 6)
83
84 /* Define views */
85 field_def *view_cpu_0[] = {
86 FLD_CPU_CPU, FLD_CPU_USR, FLD_CPU_NIC, FLD_CPU_SYS, FLD_CPU_SPIN,
87 FLD_CPU_INT, FLD_CPU_IDLE, NULL
88 };
89
90 /* Define view managers */
91 struct view_manager cpu_mgr = {
92 "cpu", select_cpu, read_cpu, NULL, print_header,
93 print_cpu, keyboard_callback, NULL, NULL
94 };
95
96 field_view views_cpu[] = {
97 { view_cpu_0, "cpu", 'C', &cpu_mgr },
98 { NULL, NULL, 0, NULL }
99 };
100
101 int cpu_count;
102 int64_t *cpu_states;
103 struct cpustats *cpu_diff, *cpu_old, *cpu_tm;
104
105 /*
106 * percentages(cnt, out, new, old, diffs) - calculate percentage change
107 * between array "old" and "new", putting the percentages in "out".
108 * "cnt" is size of each array and "diffs" is used for scratch space.
109 * The array "old" is updated on each call.
110 * The routine assumes modulo arithmetic. This function is especially
111 * useful on BSD machines for calculating cpu state percentages.
112 */
113 static int
percentages(int cnt,int64_t * out,int64_t * new,int64_t * old,int64_t * diffs)114 percentages(int cnt, int64_t *out, int64_t *new, int64_t *old, int64_t *diffs)
115 {
116 int64_t change, total_change, *dp, half_total;
117 int i;
118
119 /* initialization */
120 total_change = 0;
121 dp = diffs;
122
123 /* calculate changes for each state and the overall change */
124 for (i = 0; i < cnt; i++) {
125 if ((change = *new - *old) < 0) {
126 /* this only happens when the counter wraps */
127 change = INT64_MAX - *old + *new;
128 }
129 total_change += (*dp++ = change);
130 *old++ = *new++;
131 }
132
133 /* avoid divide by zero potential */
134 if (total_change == 0)
135 total_change = 1;
136
137 /* calculate percentages based on overall change, rounding up */
138 half_total = total_change / 2l;
139 for (i = 0; i < cnt; i++)
140 *out++ = ((*diffs++ * 1000 + half_total) / total_change);
141
142 /* return the total in case the caller wants to use it */
143 return (total_change);
144 }
145
146 static void
cpu_info(void)147 cpu_info(void)
148 {
149 int cpustats_mib[] = { CTL_KERN, KERN_CPUSTATS, 0 }, i;
150 int64_t *tmpstate;
151 size_t size;
152
153 size = sizeof(*cpu_tm);
154 for (i = 0; i < cpu_count; i++) {
155 cpustats_mib[2] = i;
156 tmpstate = cpu_states + (CPUSTATES * i);
157 if (sysctl(cpustats_mib, 3, &cpu_tm[i], &size, NULL, 0) == -1)
158 error("sysctl KERN_CPUSTATS");
159 percentages(CPUSTATES, tmpstate, cpu_tm[i].cs_time,
160 cpu_old[i].cs_time, cpu_diff[i].cs_time);
161 }
162 }
163
164 static void
print_fld_percentage(field_def * fld,double val)165 print_fld_percentage(field_def *fld, double val)
166 {
167 if (fld == NULL)
168 return;
169
170 tb_start();
171 tbprintf(val >= 1000 ? "%4.0f%%" : "%4.1f%%", val / 10.);
172 print_fld_tb(fld);
173 tb_end();
174 }
175
176 int
select_cpu(void)177 select_cpu(void)
178 {
179 return (0);
180 }
181
182 int
read_cpu(void)183 read_cpu(void)
184 {
185 cpu_info();
186 num_disp = cpu_count;
187 return (0);
188 }
189
190 int
initcpu(void)191 initcpu(void)
192 {
193 field_view *v;
194 size_t size = sizeof(cpu_count);
195 int mib[2];
196
197 mib[0] = CTL_HW;
198 mib[1] = HW_NCPU;
199 if (sysctl(mib, 2, &cpu_count, &size, NULL, 0) == -1)
200 return (-1);
201 if ((cpu_states = calloc(cpu_count,
202 CPUSTATES * sizeof(int64_t))) == NULL)
203 return (-1);
204 if ((cpu_tm = calloc(cpu_count, sizeof(*cpu_tm))) == NULL ||
205 (cpu_old = calloc(cpu_count, sizeof(*cpu_old))) == NULL ||
206 (cpu_diff = calloc(cpu_count, sizeof(*cpu_diff))) == NULL)
207 return (-1);
208
209 for (v = views_cpu; v->name != NULL; v++)
210 add_view(v);
211
212 read_cpu();
213
214 return(1);
215 }
216
217 #define ADD_EMPTY_LINE \
218 do { \
219 if (cur >= dispstart && cur < end) \
220 end_line(); \
221 if (++cur >= end) \
222 return; \
223 } while (0)
224
225 #define ADD_LINE_CPU(v, _cs) do { \
226 if (cur >= dispstart && cur < end) { \
227 print_fld_size(FLD_CPU_CPU, (v)); \
228 if (cpu_tm[v].cs_flags & CPUSTATS_ONLINE) { \
229 print_fld_percentage(FLD_CPU_USR, _cs[CP_USER]);\
230 print_fld_percentage(FLD_CPU_NIC, _cs[CP_NICE]);\
231 print_fld_percentage(FLD_CPU_SYS, _cs[CP_SYS]); \
232 print_fld_percentage(FLD_CPU_SPIN, _cs[CP_SPIN]);\
233 print_fld_percentage(FLD_CPU_INT, _cs[CP_INTR]);\
234 print_fld_percentage(FLD_CPU_IDLE, _cs[CP_IDLE]);\
235 } else { \
236 print_fld_str(FLD_CPU_USR, "-"); \
237 print_fld_str(FLD_CPU_NIC, "-"); \
238 print_fld_str(FLD_CPU_SYS, "-"); \
239 print_fld_str(FLD_CPU_SPIN, "-"); \
240 print_fld_str(FLD_CPU_INT, "-"); \
241 print_fld_str(FLD_CPU_IDLE, "-"); \
242 } \
243 end_line(); \
244 } \
245 if (++cur >= end) \
246 return; \
247 } while (0)
248
249 void
print_cpu(void)250 print_cpu(void)
251 {
252 int cur = 0, c, i;
253 int end = dispstart + maxprint;
254 int64_t *states;
255 double value[CPUSTATES];
256
257 if (end > num_disp)
258 end = num_disp;
259
260 for (c = 0; c < cpu_count; c++) {
261 states = cpu_states + (CPUSTATES * c);
262
263 for (i = 0; i < CPUSTATES; i++)
264 value[i] = *states++;
265
266 ADD_LINE_CPU(c, value);
267 }
268
269 ADD_EMPTY_LINE;
270 }
271