1 |
|
|
/* $OpenBSD: cpu.c,v 1.5 2016/01/02 20:02:40 benno 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 |
|
|
{ "Interrupt", 10, 20, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0 }, |
72 |
|
|
{ "Idle", 10, 20, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0 }, |
73 |
|
|
}; |
74 |
|
|
|
75 |
|
|
#define FLD_CPU_CPU FIELD_ADDR(fields_cpu, 0) |
76 |
|
|
#define FLD_CPU_INT FIELD_ADDR(fields_cpu, 1) |
77 |
|
|
#define FLD_CPU_SYS FIELD_ADDR(fields_cpu, 2) |
78 |
|
|
#define FLD_CPU_USR FIELD_ADDR(fields_cpu, 3) |
79 |
|
|
#define FLD_CPU_NIC FIELD_ADDR(fields_cpu, 4) |
80 |
|
|
#define FLD_CPU_IDLE FIELD_ADDR(fields_cpu, 5) |
81 |
|
|
|
82 |
|
|
/* Define views */ |
83 |
|
|
field_def *view_cpu_0[] = { |
84 |
|
|
FLD_CPU_CPU, |
85 |
|
|
FLD_CPU_INT, FLD_CPU_SYS, FLD_CPU_USR, FLD_CPU_NIC, FLD_CPU_IDLE, NULL |
86 |
|
|
}; |
87 |
|
|
|
88 |
|
|
/* Define view managers */ |
89 |
|
|
struct view_manager cpu_mgr = { |
90 |
|
|
"cpu", select_cpu, read_cpu, NULL, print_header, |
91 |
|
|
print_cpu, keyboard_callback, NULL, NULL |
92 |
|
|
}; |
93 |
|
|
|
94 |
|
|
field_view views_cpu[] = { |
95 |
|
|
{ view_cpu_0, "cpu", 'C', &cpu_mgr }, |
96 |
|
|
{ NULL, NULL, 0, NULL } |
97 |
|
|
}; |
98 |
|
|
|
99 |
|
|
int cpu_count; |
100 |
|
|
int64_t *cpu_states; |
101 |
|
|
int64_t **cpu_tm; |
102 |
|
|
int64_t **cpu_old; |
103 |
|
|
int64_t **cpu_diff; |
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 |
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 |
147 |
|
|
cpu_info(void) |
148 |
|
|
{ |
149 |
|
|
int cpu_time_mib[] = { CTL_KERN, KERN_CPTIME2, 0 }, i; |
150 |
|
|
int64_t *tmpstate; |
151 |
|
|
size_t size; |
152 |
|
|
|
153 |
|
|
size = CPUSTATES * sizeof(int64_t); |
154 |
|
|
for (i = 0; i < cpu_count; i++) { |
155 |
|
|
cpu_time_mib[2] = i; |
156 |
|
|
tmpstate = cpu_states + (CPUSTATES * i); |
157 |
|
|
if (sysctl(cpu_time_mib, 3, cpu_tm[i], &size, NULL, 0) < 0) |
158 |
|
|
error("sysctl KERN_CPTIME2"); |
159 |
|
|
percentages(CPUSTATES, tmpstate, cpu_tm[i], |
160 |
|
|
cpu_old[i], cpu_diff[i]); |
161 |
|
|
} |
162 |
|
|
} |
163 |
|
|
|
164 |
|
|
static void |
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 |
177 |
|
|
select_cpu(void) |
178 |
|
|
{ |
179 |
|
|
return (0); |
180 |
|
|
} |
181 |
|
|
|
182 |
|
|
int |
183 |
|
|
read_cpu(void) |
184 |
|
|
{ |
185 |
|
|
cpu_info(); |
186 |
|
|
num_disp = cpu_count; |
187 |
|
|
return (0); |
188 |
|
|
} |
189 |
|
|
|
190 |
|
|
int |
191 |
|
|
initcpu(void) |
192 |
|
|
{ |
193 |
|
|
field_view *v; |
194 |
|
|
size_t size = sizeof(cpu_count); |
195 |
|
|
int mib[2], i; |
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(int64_t *))) == NULL || |
205 |
|
|
(cpu_old = calloc(cpu_count, sizeof(int64_t *))) == NULL || |
206 |
|
|
(cpu_diff = calloc(cpu_count, sizeof(int64_t *))) == NULL) |
207 |
|
|
return (-1); |
208 |
|
|
for (i = 0; i < cpu_count; i++) { |
209 |
|
|
if ((cpu_tm[i] = calloc(CPUSTATES, sizeof(int64_t))) == NULL || |
210 |
|
|
(cpu_old[i] = calloc(CPUSTATES, sizeof(int64_t))) == NULL || |
211 |
|
|
(cpu_diff[i] = calloc(CPUSTATES, sizeof(int64_t))) == NULL) |
212 |
|
|
return (-1); |
213 |
|
|
} |
214 |
|
|
|
215 |
|
|
for (v = views_cpu; v->name != NULL; v++) |
216 |
|
|
add_view(v); |
217 |
|
|
|
218 |
|
|
read_cpu(); |
219 |
|
|
|
220 |
|
|
return(1); |
221 |
|
|
} |
222 |
|
|
|
223 |
|
|
#define ADD_EMPTY_LINE \ |
224 |
|
|
do { \ |
225 |
|
|
if (cur >= dispstart && cur < end) \ |
226 |
|
|
end_line(); \ |
227 |
|
|
if (++cur >= end) \ |
228 |
|
|
return; \ |
229 |
|
|
} while (0) |
230 |
|
|
|
231 |
|
|
#define ADD_LINE_CPU(v, cs) \ |
232 |
|
|
do { \ |
233 |
|
|
if (cur >= dispstart && cur < end) { \ |
234 |
|
|
print_fld_size(FLD_CPU_CPU, (v)); \ |
235 |
|
|
print_fld_percentage(FLD_CPU_INT, (cs[0])); \ |
236 |
|
|
print_fld_percentage(FLD_CPU_SYS, (cs[1])); \ |
237 |
|
|
print_fld_percentage(FLD_CPU_USR, (cs[2])); \ |
238 |
|
|
print_fld_percentage(FLD_CPU_NIC, (cs[3])); \ |
239 |
|
|
print_fld_percentage(FLD_CPU_IDLE, (cs[4])); \ |
240 |
|
|
end_line(); \ |
241 |
|
|
} \ |
242 |
|
|
if (++cur >= end) \ |
243 |
|
|
return; \ |
244 |
|
|
} while (0) |
245 |
|
|
|
246 |
|
|
void |
247 |
|
|
print_cpu(void) |
248 |
|
|
{ |
249 |
|
|
int cur = 0, c, i; |
250 |
|
|
int end = dispstart + maxprint; |
251 |
|
|
int64_t *states; |
252 |
|
|
double value[CPUSTATES]; |
253 |
|
|
|
254 |
|
|
if (end > num_disp) |
255 |
|
|
end = num_disp; |
256 |
|
|
|
257 |
|
|
for (c = 0; c < cpu_count; c++) { |
258 |
|
|
states = cpu_states + (CPUSTATES * c); |
259 |
|
|
|
260 |
|
|
for (i = 0; i < CPUSTATES; i++) |
261 |
|
|
value[i] = *states++; |
262 |
|
|
|
263 |
|
|
ADD_LINE_CPU(c, value); |
264 |
|
|
} |
265 |
|
|
|
266 |
|
|
ADD_EMPTY_LINE; |
267 |
|
|
} |