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