Annotation of src/usr.bin/systat/cpu.c, Revision 1.7
1.7 ! cheloha 1: /* $OpenBSD: cpu.c,v 1.6 2018/05/14 12:31:21 mpi Exp $ */
1.1 reyk 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:
1.4 deraadt 49: #include <sys/signal.h>
1.3 miod 50: #include <sys/sched.h>
1.1 reyk 51: #include <sys/sysctl.h>
52:
1.7 ! cheloha 53: #include <errno.h>
1.1 reyk 54: #include <stdlib.h>
55: #include <stdint.h>
56: #include <string.h>
57: #include <unistd.h>
58: #include "systat.h"
59:
60: void print_cpu(void);
61: int read_cpu(void);
62: int select_cpu(void);
63: static void cpu_info(void);
64: static void print_fld_percentage(field_def *, double);
65: static int percentages(int, int64_t *, int64_t *, int64_t *, int64_t *);
66:
67: field_def fields_cpu[] = {
68: { "CPU", 4, 8, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0 },
69: { "User", 10, 20, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0 },
70: { "Nice", 10, 20, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0 },
71: { "System", 10, 20, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0 },
1.6 mpi 72: { "Spin", 10, 20, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0 },
1.1 reyk 73: { "Interrupt", 10, 20, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0 },
74: { "Idle", 10, 20, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0 },
75: };
76:
77: #define FLD_CPU_CPU FIELD_ADDR(fields_cpu, 0)
1.6 mpi 78: #define FLD_CPU_USR FIELD_ADDR(fields_cpu, 1)
79: #define FLD_CPU_NIC FIELD_ADDR(fields_cpu, 2)
80: #define FLD_CPU_SYS FIELD_ADDR(fields_cpu, 3)
81: #define FLD_CPU_SPIN FIELD_ADDR(fields_cpu, 4)
82: #define FLD_CPU_INT FIELD_ADDR(fields_cpu, 5)
83: #define FLD_CPU_IDLE FIELD_ADDR(fields_cpu, 6)
1.1 reyk 84:
85: /* Define views */
86: field_def *view_cpu_0[] = {
1.6 mpi 87: FLD_CPU_CPU, FLD_CPU_USR, FLD_CPU_NIC, FLD_CPU_SYS, FLD_CPU_SPIN,
88: FLD_CPU_INT, FLD_CPU_IDLE, NULL
1.1 reyk 89: };
90:
91: /* Define view managers */
92: struct view_manager cpu_mgr = {
93: "cpu", select_cpu, read_cpu, NULL, print_header,
94: print_cpu, keyboard_callback, NULL, NULL
95: };
96:
97: field_view views_cpu[] = {
98: { view_cpu_0, "cpu", 'C', &cpu_mgr },
99: { NULL, NULL, 0, NULL }
100: };
101:
102: int cpu_count;
1.7 ! cheloha 103: int *cpu_online;
1.1 reyk 104: int64_t *cpu_states;
105: int64_t **cpu_tm;
106: int64_t **cpu_old;
107: int64_t **cpu_diff;
108:
109: /*
110: * percentages(cnt, out, new, old, diffs) - calculate percentage change
111: * between array "old" and "new", putting the percentages in "out".
112: * "cnt" is size of each array and "diffs" is used for scratch space.
113: * The array "old" is updated on each call.
114: * The routine assumes modulo arithmetic. This function is especially
115: * useful on BSD machines for calculating cpu state percentages.
116: */
117: static int
118: percentages(int cnt, int64_t *out, int64_t *new, int64_t *old, int64_t *diffs)
119: {
120: int64_t change, total_change, *dp, half_total;
121: int i;
122:
123: /* initialization */
124: total_change = 0;
125: dp = diffs;
126:
127: /* calculate changes for each state and the overall change */
128: for (i = 0; i < cnt; i++) {
129: if ((change = *new - *old) < 0) {
130: /* this only happens when the counter wraps */
131: change = INT64_MAX - *old + *new;
132: }
133: total_change += (*dp++ = change);
134: *old++ = *new++;
135: }
136:
137: /* avoid divide by zero potential */
138: if (total_change == 0)
139: total_change = 1;
140:
141: /* calculate percentages based on overall change, rounding up */
142: half_total = total_change / 2l;
143: for (i = 0; i < cnt; i++)
144: *out++ = ((*diffs++ * 1000 + half_total) / total_change);
145:
146: /* return the total in case the caller wants to use it */
147: return (total_change);
148: }
149:
150: static void
151: cpu_info(void)
152: {
153: int cpu_time_mib[] = { CTL_KERN, KERN_CPTIME2, 0 }, i;
154: int64_t *tmpstate;
155: size_t size;
156:
157: size = CPUSTATES * sizeof(int64_t);
158: for (i = 0; i < cpu_count; i++) {
159: cpu_time_mib[2] = i;
160: tmpstate = cpu_states + (CPUSTATES * i);
1.7 ! cheloha 161: if (sysctl(cpu_time_mib, 3, cpu_tm[i], &size, NULL, 0) < 0) {
! 162: if (errno != ENODEV)
! 163: error("sysctl KERN_CPTIME2");
! 164: cpu_online[i] = 0;
! 165: continue;
! 166: }
! 167: cpu_online[i] = 1;
1.1 reyk 168: percentages(CPUSTATES, tmpstate, cpu_tm[i],
169: cpu_old[i], cpu_diff[i]);
170: }
171: }
172:
173: static void
174: print_fld_percentage(field_def *fld, double val)
175: {
176: if (fld == NULL)
177: return;
178:
179: tb_start();
180: tbprintf(val >= 1000 ? "%4.0f%%" : "%4.1f%%", val / 10.);
181: print_fld_tb(fld);
182: tb_end();
183: }
184:
185: int
186: select_cpu(void)
187: {
188: return (0);
189: }
190:
191: int
192: read_cpu(void)
193: {
194: cpu_info();
195: num_disp = cpu_count;
196: return (0);
197: }
198:
199: int
200: initcpu(void)
201: {
202: field_view *v;
203: size_t size = sizeof(cpu_count);
204: int mib[2], i;
205:
206: mib[0] = CTL_HW;
207: mib[1] = HW_NCPU;
208: if (sysctl(mib, 2, &cpu_count, &size, NULL, 0) == -1)
209: return (-1);
1.7 ! cheloha 210: if ((cpu_online = calloc(cpu_count, sizeof(*cpu_online))) == NULL)
! 211: return (-1);
1.1 reyk 212: if ((cpu_states = calloc(cpu_count,
213: CPUSTATES * sizeof(int64_t))) == NULL)
214: return (-1);
215: if ((cpu_tm = calloc(cpu_count, sizeof(int64_t *))) == NULL ||
216: (cpu_old = calloc(cpu_count, sizeof(int64_t *))) == NULL ||
217: (cpu_diff = calloc(cpu_count, sizeof(int64_t *))) == NULL)
218: return (-1);
219: for (i = 0; i < cpu_count; i++) {
220: if ((cpu_tm[i] = calloc(CPUSTATES, sizeof(int64_t))) == NULL ||
221: (cpu_old[i] = calloc(CPUSTATES, sizeof(int64_t))) == NULL ||
222: (cpu_diff[i] = calloc(CPUSTATES, sizeof(int64_t))) == NULL)
223: return (-1);
224: }
225:
226: for (v = views_cpu; v->name != NULL; v++)
227: add_view(v);
228:
229: read_cpu();
230:
231: return(1);
232: }
233:
234: #define ADD_EMPTY_LINE \
235: do { \
236: if (cur >= dispstart && cur < end) \
237: end_line(); \
238: if (++cur >= end) \
239: return; \
240: } while (0)
241:
242: #define ADD_LINE_CPU(v, cs) \
243: do { \
244: if (cur >= dispstart && cur < end) { \
245: print_fld_size(FLD_CPU_CPU, (v)); \
1.6 mpi 246: print_fld_percentage(FLD_CPU_USR, (cs[CP_USER]));\
247: print_fld_percentage(FLD_CPU_NIC, (cs[CP_NICE]));\
248: print_fld_percentage(FLD_CPU_SYS, (cs[CP_SYS]));\
249: print_fld_percentage(FLD_CPU_SPIN, (cs[CP_SPIN]));\
250: print_fld_percentage(FLD_CPU_INT, (cs[CP_INTR]));\
251: print_fld_percentage(FLD_CPU_IDLE, (cs[CP_IDLE])); \
1.1 reyk 252: end_line(); \
253: } \
254: if (++cur >= end) \
255: return; \
256: } while (0)
257:
1.7 ! cheloha 258: #define ADD_OFFLINE_CPU(v) do { \
! 259: if (cur >= dispstart && cur < end) { \
! 260: print_fld_size(FLD_CPU_CPU, (v)); \
! 261: print_fld_str(FLD_CPU_USR, "-"); \
! 262: print_fld_str(FLD_CPU_NIC, "-"); \
! 263: print_fld_str(FLD_CPU_SYS, "-"); \
! 264: print_fld_str(FLD_CPU_SPIN, "-"); \
! 265: print_fld_str(FLD_CPU_INT, "-"); \
! 266: print_fld_str(FLD_CPU_IDLE, "-"); \
! 267: end_line(); \
! 268: } \
! 269: if (++cur >= end) \
! 270: return; \
! 271: } while (0)
! 272:
1.1 reyk 273: void
274: print_cpu(void)
275: {
276: int cur = 0, c, i;
277: int end = dispstart + maxprint;
278: int64_t *states;
279: double value[CPUSTATES];
280:
281: if (end > num_disp)
282: end = num_disp;
283:
284: for (c = 0; c < cpu_count; c++) {
1.7 ! cheloha 285: if (!cpu_online[c]) {
! 286: ADD_OFFLINE_CPU(c);
! 287: continue;
! 288: }
1.1 reyk 289: states = cpu_states + (CPUSTATES * c);
290:
291: for (i = 0; i < CPUSTATES; i++)
292: value[i] = *states++;
293:
294: ADD_LINE_CPU(c, value);
295: }
296:
297: ADD_EMPTY_LINE;
298: }