1 /* $OpenBSD: cpu.c,v 1.7 2018/09/26 17:23:13 cheloha 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 <errno.h> 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 }, 72 { "Spin", 10, 20, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0 }, 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) 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) 84 85 /* Define views */ 86 field_def *view_cpu_0[] = { 87 FLD_CPU_CPU, FLD_CPU_USR, FLD_CPU_NIC, FLD_CPU_SYS, FLD_CPU_SPIN, 88 FLD_CPU_INT, FLD_CPU_IDLE, NULL 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; 103 int *cpu_online; 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); 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; 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); 210 if ((cpu_online = calloc(cpu_count, sizeof(*cpu_online))) == NULL) 211 return (-1); 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)); \ 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])); \ 252 end_line(); \ 253 } \ 254 if (++cur >= end) \ 255 return; \ 256 } while (0) 257 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 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++) { 285 if (!cpu_online[c]) { 286 ADD_OFFLINE_CPU(c); 287 continue; 288 } 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 } 299