| GCC Code Coverage Report | |||||||||||||||||||||
| 
 | |||||||||||||||||||||
| Line | Branch | Exec | Source | 
| 1 | /* $OpenBSD: machine.c,v 1.86 2016/05/11 08:11:27 edd Exp $ */ | ||
| 2 | |||
| 3 | /*- | ||
| 4 | * Copyright (c) 1994 Thorsten Lockert <tholo@sigmasoft.com> | ||
| 5 | * All rights reserved. | ||
| 6 | * | ||
| 7 | * Redistribution and use in source and binary forms, with or without | ||
| 8 | * modification, are permitted provided that the following conditions | ||
| 9 | * are met: | ||
| 10 | * 1. Redistributions of source code must retain the above copyright | ||
| 11 | * notice, this list of conditions and the following disclaimer. | ||
| 12 | * 2. Redistributions in binary form must reproduce the above copyright | ||
| 13 | * notice, this list of conditions and the following disclaimer in the | ||
| 14 | * documentation and/or other materials provided with the distribution. | ||
| 15 | * 3. The name of the author may not be used to endorse or promote products | ||
| 16 | * derived from this software without specific prior written permission. | ||
| 17 | * | ||
| 18 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, | ||
| 19 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY | ||
| 20 | * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL | ||
| 21 | * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| 22 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| 23 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
| 24 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
| 25 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
| 26 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | ||
| 27 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 28 | * | ||
| 29 | * AUTHOR: Thorsten Lockert <tholo@sigmasoft.com> | ||
| 30 | * Adapted from BSD4.4 by Christos Zoulas <christos@ee.cornell.edu> | ||
| 31 | * Patch for process wait display by Jarl F. Greipsland <jarle@idt.unit.no> | ||
| 32 | * Patch for -DORDER by Kenneth Stailey <kstailey@disclosure.com> | ||
| 33 | * Patch for new swapctl(2) by Tobias Weingartner <weingart@openbsd.org> | ||
| 34 | */ | ||
| 35 | |||
| 36 | #include <sys/param.h> /* DEV_BSIZE MAXCOMLEN PZERO */ | ||
| 37 | #include <sys/types.h> | ||
| 38 | #include <sys/signal.h> | ||
| 39 | #include <sys/mount.h> | ||
| 40 | #include <sys/proc.h> | ||
| 41 | #include <sys/sched.h> | ||
| 42 | #include <sys/swap.h> | ||
| 43 | #include <sys/sysctl.h> | ||
| 44 | |||
| 45 | #include <stdio.h> | ||
| 46 | #include <stdlib.h> | ||
| 47 | #include <string.h> | ||
| 48 | #include <unistd.h> | ||
| 49 | #include <err.h> | ||
| 50 | #include <errno.h> | ||
| 51 | |||
| 52 | #include "top.h" | ||
| 53 | #include "display.h" | ||
| 54 | #include "machine.h" | ||
| 55 | #include "utils.h" | ||
| 56 | |||
| 57 | static int swapmode(int *, int *); | ||
| 58 | static char *state_abbr(struct kinfo_proc *); | ||
| 59 | static char *format_comm(struct kinfo_proc *); | ||
| 60 | static int cmd_matches(struct kinfo_proc *, char *); | ||
| 61 | static char **get_proc_args(struct kinfo_proc *); | ||
| 62 | |||
| 63 | /* get_process_info passes back a handle. This is what it looks like: */ | ||
| 64 | |||
| 65 | struct handle { | ||
| 66 | struct kinfo_proc **next_proc; /* points to next valid proc pointer */ | ||
| 67 | int remaining; /* number of pointers remaining */ | ||
| 68 | }; | ||
| 69 | |||
| 70 | /* what we consider to be process size: */ | ||
| 71 | #define PROCSIZE(pp) ((pp)->p_vm_tsize + (pp)->p_vm_dsize + (pp)->p_vm_ssize) | ||
| 72 | |||
| 73 | /* | ||
| 74 | * These definitions control the format of the per-process area | ||
| 75 | */ | ||
| 76 | static char header[] = | ||
| 77 | " PID X PRI NICE SIZE RES STATE WAIT TIME CPU COMMAND"; | ||
| 78 | |||
| 79 | /* 0123456 -- field to fill in starts at header+6 */ | ||
| 80 | #define UNAME_START 6 | ||
| 81 | |||
| 82 | #define Proc_format \ | ||
| 83 | "%5d %-8.8s %3d %4d %5s %5s %-9s %-7.7s %6s %5.2f%% %s" | ||
| 84 | |||
| 85 | /* process state names for the "STATE" column of the display */ | ||
| 86 | /* | ||
| 87 | * the extra nulls in the string "run" are for adding a slash and the | ||
| 88 | * processor number when needed | ||
| 89 | */ | ||
| 90 | |||
| 91 | char	*state_abbrev[] = { | ||
| 92 | "", "start", "run", "sleep", "stop", "zomb", "dead", "onproc" | ||
| 93 | }; | ||
| 94 | |||
| 95 | /* these are for calculating cpu state percentages */ | ||
| 96 | static int64_t **cp_time; | ||
| 97 | static int64_t **cp_old; | ||
| 98 | static int64_t **cp_diff; | ||
| 99 | |||
| 100 | /* these are for detailing the process states */ | ||
| 101 | int process_states[8]; | ||
| 102 | char *procstatenames[] = { | ||
| 103 | "", " starting, ", " running, ", " idle, ", | ||
| 104 | " stopped, ", " zombie, ", " dead, ", " on processor, ", | ||
| 105 | NULL | ||
| 106 | }; | ||
| 107 | |||
| 108 | /* these are for detailing the cpu states */ | ||
| 109 | int64_t *cpu_states; | ||
| 110 | char *cpustatenames[] = { | ||
| 111 | "user", "nice", "system", "interrupt", "idle", NULL | ||
| 112 | }; | ||
| 113 | |||
| 114 | /* these are for detailing the memory statistics */ | ||
| 115 | int memory_stats[10]; | ||
| 116 | char *memorynames[] = { | ||
| 117 | "Real: ", "K/", "K act/tot ", "Free: ", "K ", | ||
| 118 | "Cache: ", "K ", | ||
| 119 | "Swap: ", "K/", "K", | ||
| 120 | NULL | ||
| 121 | }; | ||
| 122 | |||
| 123 | /* these are names given to allowed sorting orders -- first is default */ | ||
| 124 | char	*ordernames[] = { | ||
| 125 | "cpu", "size", "res", "time", "pri", "pid", "command", NULL | ||
| 126 | }; | ||
| 127 | |||
| 128 | /* these are for keeping track of the proc array */ | ||
| 129 | static int nproc; | ||
| 130 | static int onproc = -1; | ||
| 131 | static int pref_len; | ||
| 132 | static struct kinfo_proc *pbase; | ||
| 133 | static struct kinfo_proc **pref; | ||
| 134 | |||
| 135 | /* these are for getting the memory statistics */ | ||
| 136 | static int pageshift; /* log base 2 of the pagesize */ | ||
| 137 | |||
| 138 | /* define pagetok in terms of pageshift */ | ||
| 139 | #define pagetok(size) ((size) << pageshift) | ||
| 140 | |||
| 141 | int ncpu; | ||
| 142 | int fscale; | ||
| 143 | |||
| 144 | unsigned int maxslp; | ||
| 145 | |||
| 146 | int | ||
| 147 | getfscale(void) | ||
| 148 | 1 | { | |
| 149 | 1 | 	int mib[] = { CTL_KERN, KERN_FSCALE }; | |
| 150 | 1 | size_t size = sizeof(fscale); | |
| 151 | |||
| 152 | ✗✓ | 1 | if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), | 
| 153 | &fscale, &size, NULL, 0) < 0) | ||
| 154 | return (-1); | ||
| 155 | 1 | return fscale; | |
| 156 | } | ||
| 157 | |||
| 158 | int | ||
| 159 | getncpu(void) | ||
| 160 | 2 | { | |
| 161 | 2 | 	int mib[] = { CTL_HW, HW_NCPU }; | |
| 162 | int ncpu; | ||
| 163 | 2 | size_t size = sizeof(ncpu); | |
| 164 | |||
| 165 | ✗✓ | 2 | if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), | 
| 166 | &ncpu, &size, NULL, 0) == -1) | ||
| 167 | return (-1); | ||
| 168 | |||
| 169 | 2 | return (ncpu); | |
| 170 | } | ||
| 171 | |||
| 172 | int | ||
| 173 | machine_init(struct statics *statics) | ||
| 174 | 1 | { | |
| 175 | int pagesize, cpu; | ||
| 176 | |||
| 177 | 1 | ncpu = getncpu(); | |
| 178 | ✗✓ | 1 | if (ncpu == -1) | 
| 179 | return (-1); | ||
| 180 | ✗✓ | 1 | if (getfscale() == -1) | 
| 181 | return (-1); | ||
| 182 | 1 | cpu_states = calloc(ncpu, CPUSTATES * sizeof(int64_t)); | |
| 183 | ✗✓ | 1 | if (cpu_states == NULL) | 
| 184 | err(1, NULL); | ||
| 185 | 1 | cp_time = calloc(ncpu, sizeof(int64_t *)); | |
| 186 | 1 | cp_old = calloc(ncpu, sizeof(int64_t *)); | |
| 187 | 1 | cp_diff = calloc(ncpu, sizeof(int64_t *)); | |
| 188 | ✓✗✓✗ ✗✓ | 1 | if (cp_time == NULL || cp_old == NULL || cp_diff == NULL) | 
| 189 | err(1, NULL); | ||
| 190 | ✓✓ | 2 | 	for (cpu = 0; cpu < ncpu; cpu++) { | 
| 191 | 1 | cp_time[cpu] = calloc(CPUSTATES, sizeof(int64_t)); | |
| 192 | 1 | cp_old[cpu] = calloc(CPUSTATES, sizeof(int64_t)); | |
| 193 | 1 | cp_diff[cpu] = calloc(CPUSTATES, sizeof(int64_t)); | |
| 194 | ✓✗✓✗ ✗✓ | 1 | if (cp_time[cpu] == NULL || cp_old[cpu] == NULL || | 
| 195 | cp_diff[cpu] == NULL) | ||
| 196 | err(1, NULL); | ||
| 197 | } | ||
| 198 | |||
| 199 | 1 | pbase = NULL; | |
| 200 | 1 | pref = NULL; | |
| 201 | 1 | onproc = -1; | |
| 202 | 1 | nproc = 0; | |
| 203 | |||
| 204 | /* | ||
| 205 | * get the page size with "getpagesize" and calculate pageshift from | ||
| 206 | * it | ||
| 207 | */ | ||
| 208 | 1 | pagesize = getpagesize(); | |
| 209 | 1 | pageshift = 0; | |
| 210 | ✓✓ | 14 | 	while (pagesize > 1) { | 
| 211 | 12 | pageshift++; | |
| 212 | 12 | pagesize >>= 1; | |
| 213 | } | ||
| 214 | |||
| 215 | /* we only need the amount of log(2)1024 for our conversion */ | ||
| 216 | 1 | pageshift -= LOG1024; | |
| 217 | |||
| 218 | /* fill in the statics information */ | ||
| 219 | 1 | statics->procstate_names = procstatenames; | |
| 220 | 1 | statics->cpustate_names = cpustatenames; | |
| 221 | 1 | statics->memory_names = memorynames; | |
| 222 | 1 | statics->order_names = ordernames; | |
| 223 | 1 | return (0); | |
| 224 | } | ||
| 225 | |||
| 226 | char * | ||
| 227 | format_header(char *second_field, int show_threads) | ||
| 228 | 2 | { | |
| 229 | 2 | char *field_name, *thread_field = " TID"; | |
| 230 | char *ptr; | ||
| 231 | |||
| 232 | ✗✓ | 2 | if (show_threads) | 
| 233 | field_name = thread_field; | ||
| 234 | else | ||
| 235 | 2 | field_name = second_field; | |
| 236 | |||
| 237 | 2 | ptr = header + UNAME_START; | |
| 238 | ✓✓ | 20 | while (*field_name != '\0') | 
| 239 | 16 | *ptr++ = *field_name++; | |
| 240 | 2 | return (header); | |
| 241 | } | ||
| 242 | |||
| 243 | void | ||
| 244 | get_system_info(struct system_info *si) | ||
| 245 | 2 | { | |
| 246 | 	static int sysload_mib[] = {CTL_VM, VM_LOADAVG}; | ||
| 247 | 	static int uvmexp_mib[] = {CTL_VM, VM_UVMEXP}; | ||
| 248 | 	static int bcstats_mib[] = {CTL_VFS, VFS_GENERIC, VFS_BCACHESTAT}; | ||
| 249 | struct loadavg sysload; | ||
| 250 | struct uvmexp uvmexp; | ||
| 251 | struct bcachestats bcstats; | ||
| 252 | double *infoloadp; | ||
| 253 | size_t size; | ||
| 254 | int i; | ||
| 255 | int64_t *tmpstate; | ||
| 256 | |||
| 257 | ✗✓ | 2 | 	if (ncpu > 1) { | 
| 258 | 		int cp_time_mib[] = {CTL_KERN, KERN_CPTIME2, /*fillme*/0}; | ||
| 259 | |||
| 260 | size = CPUSTATES * sizeof(int64_t); | ||
| 261 | 		for (i = 0; i < ncpu; i++) { | ||
| 262 | cp_time_mib[2] = i; | ||
| 263 | tmpstate = cpu_states + (CPUSTATES * i); | ||
| 264 | if (sysctl(cp_time_mib, 3, cp_time[i], &size, NULL, 0) < 0) | ||
| 265 | 				warn("sysctl kern.cp_time2 failed"); | ||
| 266 | /* convert cp_time2 counts to percentages */ | ||
| 267 | (void) percentages(CPUSTATES, tmpstate, cp_time[i], | ||
| 268 | cp_old[i], cp_diff[i]); | ||
| 269 | } | ||
| 270 | 	} else { | ||
| 271 | 2 | 		int cp_time_mib[] = {CTL_KERN, KERN_CPTIME}; | |
| 272 | long cp_time_tmp[CPUSTATES]; | ||
| 273 | |||
| 274 | 2 | size = sizeof(cp_time_tmp); | |
| 275 | ✗✓ | 2 | if (sysctl(cp_time_mib, 2, cp_time_tmp, &size, NULL, 0) < 0) | 
| 276 | 			warn("sysctl kern.cp_time failed"); | ||
| 277 | ✓✓ | 12 | for (i = 0; i < CPUSTATES; i++) | 
| 278 | 10 | cp_time[0][i] = cp_time_tmp[i]; | |
| 279 | /* convert cp_time counts to percentages */ | ||
| 280 | 2 | (void) percentages(CPUSTATES, cpu_states, cp_time[0], | |
| 281 | cp_old[0], cp_diff[0]); | ||
| 282 | } | ||
| 283 | |||
| 284 | 2 | size = sizeof(sysload); | |
| 285 | ✗✓ | 2 | if (sysctl(sysload_mib, 2, &sysload, &size, NULL, 0) < 0) | 
| 286 | 		warn("sysctl failed"); | ||
| 287 | 2 | infoloadp = si->load_avg; | |
| 288 | ✓✓ | 8 | for (i = 0; i < 3; i++) | 
| 289 | 6 | *infoloadp++ = ((double) sysload.ldavg[i]) / sysload.fscale; | |
| 290 | |||
| 291 | |||
| 292 | /* get total -- systemwide main memory usage structure */ | ||
| 293 | 2 | size = sizeof(uvmexp); | |
| 294 | ✗✓ | 2 | 	if (sysctl(uvmexp_mib, 2, &uvmexp, &size, NULL, 0) < 0) { | 
| 295 | 		warn("sysctl failed"); | ||
| 296 | bzero(&uvmexp, sizeof(uvmexp)); | ||
| 297 | } | ||
| 298 | 2 | size = sizeof(bcstats); | |
| 299 | ✗✓ | 2 | 	if (sysctl(bcstats_mib, 3, &bcstats, &size, NULL, 0) < 0) { | 
| 300 | 		warn("sysctl failed"); | ||
| 301 | bzero(&bcstats, sizeof(bcstats)); | ||
| 302 | } | ||
| 303 | /* convert memory stats to Kbytes */ | ||
| 304 | 2 | memory_stats[0] = -1; | |
| 305 | 2 | memory_stats[1] = pagetok(uvmexp.active); | |
| 306 | 2 | memory_stats[2] = pagetok(uvmexp.npages - uvmexp.free); | |
| 307 | 2 | memory_stats[3] = -1; | |
| 308 | 2 | memory_stats[4] = pagetok(uvmexp.free); | |
| 309 | 2 | memory_stats[5] = -1; | |
| 310 | 2 | memory_stats[6] = pagetok(bcstats.numbufpages); | |
| 311 | 2 | memory_stats[7] = -1; | |
| 312 | |||
| 313 | ✗✓ | 2 | 	if (!swapmode(&memory_stats[8], &memory_stats[9])) { | 
| 314 | memory_stats[8] = 0; | ||
| 315 | memory_stats[9] = 0; | ||
| 316 | } | ||
| 317 | |||
| 318 | /* set arrays and strings */ | ||
| 319 | 2 | si->cpustates = cpu_states; | |
| 320 | 2 | si->memory = memory_stats; | |
| 321 | 2 | si->last_pid = -1; | |
| 322 | 2 | } | |
| 323 | |||
| 324 | static struct handle handle; | ||
| 325 | |||
| 326 | struct kinfo_proc * | ||
| 327 | getprocs(int op, int arg, int *cnt) | ||
| 328 | 2 | { | |
| 329 | size_t size; | ||
| 330 | 2 | 	int mib[6] = {CTL_KERN, KERN_PROC, 0, 0, sizeof(struct kinfo_proc), 0}; | |
| 331 | 	static int maxslp_mib[] = {CTL_VM, VM_MAXSLP}; | ||
| 332 | static struct kinfo_proc *procbase; | ||
| 333 | int st; | ||
| 334 | |||
| 335 | 2 | mib[2] = op; | |
| 336 | 2 | mib[3] = arg; | |
| 337 | |||
| 338 | 2 | size = sizeof(maxslp); | |
| 339 | ✗✓ | 2 | 	if (sysctl(maxslp_mib, 2, &maxslp, &size, NULL, 0) < 0) { | 
| 340 | 		warn("sysctl vm.maxslp failed"); | ||
| 341 | return (0); | ||
| 342 | } | ||
| 343 | 2 | retry: | |
| 344 | 2 | free(procbase); | |
| 345 | 2 | st = sysctl(mib, 6, NULL, &size, NULL, 0); | |
| 346 | ✗✓ | 2 | 	if (st == -1) { | 
| 347 | /* _kvm_syserr(kd, kd->program, "kvm_getprocs"); */ | ||
| 348 | return (0); | ||
| 349 | } | ||
| 350 | 2 | size = 5 * size / 4; /* extra slop */ | |
| 351 | ✗✓ | 2 | if ((procbase = malloc(size)) == NULL) | 
| 352 | return (0); | ||
| 353 | 2 | mib[5] = (int)(size / sizeof(struct kinfo_proc)); | |
| 354 | 2 | st = sysctl(mib, 6, procbase, &size, NULL, 0); | |
| 355 | ✗✓ | 2 | 	if (st == -1) { | 
| 356 | if (errno == ENOMEM) | ||
| 357 | goto retry; | ||
| 358 | /* _kvm_syserr(kd, kd->program, "kvm_getprocs"); */ | ||
| 359 | return (0); | ||
| 360 | } | ||
| 361 | 2 | *cnt = (int)(size / sizeof(struct kinfo_proc)); | |
| 362 | 2 | return (procbase); | |
| 363 | } | ||
| 364 | |||
| 365 | static char ** | ||
| 366 | get_proc_args(struct kinfo_proc *kp) | ||
| 367 | 24 | { | |
| 368 | static char **s; | ||
| 369 | 24 | size_t siz = 100; | |
| 370 | int mib[4]; | ||
| 371 | |||
| 372 | 2 | 	for (;; siz *= 2) { | |
| 373 | ✗✓ | 26 | if ((s = realloc(s, siz)) == NULL) | 
| 374 | err(1, NULL); | ||
| 375 | 26 | mib[0] = CTL_KERN; | |
| 376 | 26 | mib[1] = KERN_PROC_ARGS; | |
| 377 | 26 | mib[2] = kp->p_pid; | |
| 378 | 26 | mib[3] = KERN_PROC_ARGV; | |
| 379 | ✓✓ | 26 | if (sysctl(mib, 4, s, &siz, NULL, 0) == 0) | 
| 380 | 24 | break; | |
| 381 | ✗✓ | 2 | if (errno != ENOMEM) | 
| 382 | return NULL; | ||
| 383 | 2 | } | |
| 384 | 24 | return s; | |
| 385 | } | ||
| 386 | |||
| 387 | static int | ||
| 388 | cmd_matches(struct kinfo_proc *proc, char *term) | ||
| 389 | { | ||
| 390 | extern int show_args; | ||
| 391 | char **args = NULL; | ||
| 392 | |||
| 393 | 	if (!term) { | ||
| 394 | /* No command filter set */ | ||
| 395 | return 1; | ||
| 396 | 	} else { | ||
| 397 | /* Filter set, process name needs to contain term */ | ||
| 398 | if (strstr(proc->p_comm, term)) | ||
| 399 | return 1; | ||
| 400 | /* If showing arguments, search those as well */ | ||
| 401 | 		if (show_args) { | ||
| 402 | args = get_proc_args(proc); | ||
| 403 | |||
| 404 | 			if (args == NULL) { | ||
| 405 | /* Failed to get args, so can't search them */ | ||
| 406 | return 0; | ||
| 407 | } | ||
| 408 | |||
| 409 | 			while (*args != NULL) { | ||
| 410 | if (strstr(*args, term)) | ||
| 411 | return 1; | ||
| 412 | args++; | ||
| 413 | } | ||
| 414 | } | ||
| 415 | } | ||
| 416 | return 0; | ||
| 417 | } | ||
| 418 | |||
| 419 | caddr_t | ||
| 420 | get_process_info(struct system_info *si, struct process_select *sel, | ||
| 421 | int (*compare) (const void *, const void *)) | ||
| 422 | 2 | { | |
| 423 | int show_idle, show_system, show_threads, show_uid, show_pid, show_cmd; | ||
| 424 | int hide_uid; | ||
| 425 | int total_procs, active_procs; | ||
| 426 | struct kinfo_proc **prefp, *pp; | ||
| 427 | 2 | int what = KERN_PROC_KTHREAD; | |
| 428 | |||
| 429 | ✗✓ | 2 | if (sel->threads) | 
| 430 | what |= KERN_PROC_SHOW_THREADS; | ||
| 431 | |||
| 432 | ✗✓ | 2 | 	if ((pbase = getprocs(what, 0, &nproc)) == NULL) { | 
| 433 | 		/* warnx("%s", kvm_geterr(kd)); */ | ||
| 434 | quit(23); | ||
| 435 | } | ||
| 436 | ✓✓ | 2 | if (nproc > onproc) | 
| 437 | 1 | pref = reallocarray(pref, (onproc = nproc), | |
| 438 | sizeof(struct kinfo_proc *)); | ||
| 439 | ✗✓ | 2 | 	if (pref == NULL) { | 
| 440 | 		warnx("Out of memory."); | ||
| 441 | quit(23); | ||
| 442 | } | ||
| 443 | /* get a pointer to the states summary array */ | ||
| 444 | 2 | si->procstates = process_states; | |
| 445 | |||
| 446 | /* set up flags which define what we are going to select */ | ||
| 447 | 2 | show_idle = sel->idle; | |
| 448 | 2 | show_system = sel->system; | |
| 449 | 2 | show_threads = sel->threads; | |
| 450 | 2 | show_uid = sel->uid != (uid_t)-1; | |
| 451 | 2 | hide_uid = sel->huid != (uid_t)-1; | |
| 452 | 2 | show_pid = sel->pid != (pid_t)-1; | |
| 453 | 2 | show_cmd = sel->command != NULL; | |
| 454 | |||
| 455 | /* count up process states and get pointers to interesting procs */ | ||
| 456 | 2 | total_procs = 0; | |
| 457 | 2 | active_procs = 0; | |
| 458 | 2 | memset((char *) process_states, 0, sizeof(process_states)); | |
| 459 | 2 | prefp = pref; | |
| 460 | ✓✓ | 111 | 	for (pp = pbase; pp < &pbase[nproc]; pp++) { | 
| 461 | /* | ||
| 462 | * Place pointers to each valid proc structure in pref[]. | ||
| 463 | * Process slots that are actually in use have a non-zero | ||
| 464 | * status field. Processes with P_SYSTEM set are system | ||
| 465 | * processes---these get ignored unless show_system is set. | ||
| 466 | */ | ||
| 467 | ✗✓✗✗ | 109 | if (show_threads && pp->p_tid == -1) | 
| 468 | continue; | ||
| 469 | ✓✗✓✗ ✓✓✓✗ ✓✗ | 109 | if (pp->p_stat != 0 && | 
| 470 | (show_system || (pp->p_flag & P_SYSTEM) == 0) && | ||
| 471 | 		    (show_threads || (pp->p_flag & P_THREAD) == 0)) { | ||
| 472 | 73 | total_procs++; | |
| 473 | 73 | process_states[(unsigned char) pp->p_stat]++; | |
| 474 | ✓✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✓✗✗ ✗✓✗✗ ✗✓✗✗ | 73 | if ((pp->p_psflags & PS_ZOMBIE) == 0 && | 
| 475 | (show_idle || pp->p_pctcpu != 0 || | ||
| 476 | pp->p_stat == SRUN) && | ||
| 477 | (!hide_uid || pp->p_ruid != sel->huid) && | ||
| 478 | (!show_uid || pp->p_ruid == sel->uid) && | ||
| 479 | (!show_pid || pp->p_pid == sel->pid) && | ||
| 480 | 			    (!show_cmd || cmd_matches(pp, sel->command))) { | ||
| 481 | 73 | *prefp++ = pp; | |
| 482 | 73 | active_procs++; | |
| 483 | } | ||
| 484 | } | ||
| 485 | } | ||
| 486 | |||
| 487 | /* if requested, sort the "interesting" processes */ | ||
| 488 | ✓✗ | 2 | if (compare != NULL) | 
| 489 | 2 | qsort((char *) pref, active_procs, | |
| 490 | sizeof(struct kinfo_proc *), compare); | ||
| 491 | /* remember active and total counts */ | ||
| 492 | 2 | si->p_total = total_procs; | |
| 493 | 2 | si->p_active = pref_len = active_procs; | |
| 494 | |||
| 495 | /* pass back a handle */ | ||
| 496 | 2 | handle.next_proc = pref; | |
| 497 | 2 | handle.remaining = active_procs; | |
| 498 | 2 | return ((caddr_t) & handle); | |
| 499 | } | ||
| 500 | |||
| 501 | char fmt[MAX_COLS]; /* static area where result is built */ | ||
| 502 | |||
| 503 | static char * | ||
| 504 | state_abbr(struct kinfo_proc *pp) | ||
| 505 | 19 | { | |
| 506 | static char buf[10]; | ||
| 507 | |||
| 508 | ✗✓✗✗ | 19 | if (ncpu > 1 && pp->p_cpuid != KI_NOCPU) | 
| 509 | snprintf(buf, sizeof buf, "%s/%llu", | ||
| 510 | state_abbrev[(unsigned char)pp->p_stat], pp->p_cpuid); | ||
| 511 | else | ||
| 512 | 19 | snprintf(buf, sizeof buf, "%s", | |
| 513 | state_abbrev[(unsigned char)pp->p_stat]); | ||
| 514 | 19 | return buf; | |
| 515 | } | ||
| 516 | |||
| 517 | static char * | ||
| 518 | format_comm(struct kinfo_proc *kp) | ||
| 519 | 48 | { | |
| 520 | static char buf[MAX_COLS]; | ||
| 521 | char **p, **s; | ||
| 522 | extern int show_args; | ||
| 523 | |||
| 524 | ✓✓ | 48 | if (!show_args) | 
| 525 | 24 | return (kp->p_comm); | |
| 526 | |||
| 527 | 24 | s = get_proc_args(kp); | |
| 528 | ✗✓ | 24 | if (s == NULL) | 
| 529 | return kp->p_comm; | ||
| 530 | |||
| 531 | 24 | buf[0] = '\0'; | |
| 532 | ✓✓ | 58 | 	for (p = s; *p != NULL; p++) { | 
| 533 | ✓✓ | 34 | if (p != s) | 
| 534 | 10 | strlcat(buf, " ", sizeof(buf)); | |
| 535 | 34 | strlcat(buf, *p, sizeof(buf)); | |
| 536 | } | ||
| 537 | ✗✓ | 24 | if (buf[0] == '\0') | 
| 538 | return (kp->p_comm); | ||
| 539 | 24 | return (buf); | |
| 540 | } | ||
| 541 | |||
| 542 | char * | ||
| 543 | format_next_process(caddr_t handle, char *(*get_userid)(uid_t), pid_t *pid, | ||
| 544 | int show_threads) | ||
| 545 | 48 | { | |
| 546 | char *p_wait; | ||
| 547 | struct kinfo_proc *pp; | ||
| 548 | struct handle *hp; | ||
| 549 | int cputime; | ||
| 550 | double pct; | ||
| 551 | char buf[16]; | ||
| 552 | |||
| 553 | /* find and remember the next proc structure */ | ||
| 554 | 48 | hp = (struct handle *) handle; | |
| 555 | 48 | pp = *(hp->next_proc++); | |
| 556 | 48 | hp->remaining--; | |
| 557 | |||
| 558 | 48 | cputime = pp->p_rtime_sec + ((pp->p_rtime_usec + 500000) / 1000000); | |
| 559 | |||
| 560 | /* calculate the base for cpu percentages */ | ||
| 561 | 48 | pct = (double)pp->p_pctcpu / fscale; | |
| 562 | |||
| 563 | ✓✓ | 48 | if (pp->p_wmesg[0]) | 
| 564 | 43 | p_wait = pp->p_wmesg; | |
| 565 | else | ||
| 566 | 5 | p_wait = "-"; | |
| 567 | |||
| 568 | ✗✓ | 48 | if (show_threads) | 
| 569 | snprintf(buf, sizeof(buf), "%8d", pp->p_tid); | ||
| 570 | else | ||
| 571 | 48 | snprintf(buf, sizeof(buf), "%s", (*get_userid)(pp->p_ruid)); | |
| 572 | |||
| 573 | /* format this entry */ | ||
| 574 | ✓✓✓✓ | 48 | snprintf(fmt, sizeof(fmt), Proc_format, pp->p_pid, buf, | 
| 575 | pp->p_priority - PZERO, pp->p_nice - NZERO, | ||
| 576 | format_k(pagetok(PROCSIZE(pp))), | ||
| 577 | format_k(pagetok(pp->p_vm_rssize)), | ||
| 578 | (pp->p_stat == SSLEEP && pp->p_slptime > maxslp) ? | ||
| 579 | "idle" : state_abbr(pp), | ||
| 580 | p_wait, format_time(cputime), 100.0 * pct, | ||
| 581 | printable(format_comm(pp))); | ||
| 582 | |||
| 583 | 48 | *pid = pp->p_pid; | |
| 584 | /* return the result */ | ||
| 585 | 48 | return (fmt); | |
| 586 | } | ||
| 587 | |||
| 588 | /* comparison routine for qsort */ | ||
| 589 | static unsigned char sorted_state[] = | ||
| 590 | { | ||
| 591 | 0, /* not used */ | ||
| 592 | 4, /* start */ | ||
| 593 | 5, /* run */ | ||
| 594 | 2, /* sleep */ | ||
| 595 | 3, /* stop */ | ||
| 596 | 1 /* zombie */ | ||
| 597 | }; | ||
| 598 | |||
| 599 | /* | ||
| 600 | * proc_compares - comparison functions for "qsort" | ||
| 601 | */ | ||
| 602 | |||
| 603 | /* | ||
| 604 | * First, the possible comparison keys. These are defined in such a way | ||
| 605 | * that they can be merely listed in the source code to define the actual | ||
| 606 | * desired ordering. | ||
| 607 | */ | ||
| 608 | |||
| 609 | #define ORDERKEY_PCTCPU \ | ||
| 610 | if ((result = (int)(p2->p_pctcpu - p1->p_pctcpu)) == 0) | ||
| 611 | #define ORDERKEY_CPUTIME \ | ||
| 612 | if ((result = p2->p_rtime_sec - p1->p_rtime_sec) == 0) \ | ||
| 613 | if ((result = p2->p_rtime_usec - p1->p_rtime_usec) == 0) | ||
| 614 | #define ORDERKEY_STATE \ | ||
| 615 | if ((result = sorted_state[(unsigned char)p2->p_stat] - \ | ||
| 616 | sorted_state[(unsigned char)p1->p_stat]) == 0) | ||
| 617 | #define ORDERKEY_PRIO \ | ||
| 618 | if ((result = p2->p_priority - p1->p_priority) == 0) | ||
| 619 | #define ORDERKEY_RSSIZE \ | ||
| 620 | if ((result = p2->p_vm_rssize - p1->p_vm_rssize) == 0) | ||
| 621 | #define ORDERKEY_MEM \ | ||
| 622 | if ((result = PROCSIZE(p2) - PROCSIZE(p1)) == 0) | ||
| 623 | #define ORDERKEY_PID \ | ||
| 624 | if ((result = p1->p_pid - p2->p_pid) == 0) | ||
| 625 | #define ORDERKEY_CMD \ | ||
| 626 | if ((result = strcmp(p1->p_comm, p2->p_comm)) == 0) | ||
| 627 | |||
| 628 | /* compare_cpu - the comparison function for sorting by cpu percentage */ | ||
| 629 | static int | ||
| 630 | compare_cpu(const void *v1, const void *v2) | ||
| 631 | 350 | { | |
| 632 | 350 | struct proc **pp1 = (struct proc **) v1; | |
| 633 | 350 | struct proc **pp2 = (struct proc **) v2; | |
| 634 | struct kinfo_proc *p1, *p2; | ||
| 635 | int result; | ||
| 636 | |||
| 637 | /* remove one level of indirection */ | ||
| 638 | 350 | p1 = *(struct kinfo_proc **) pp1; | |
| 639 | 350 | p2 = *(struct kinfo_proc **) pp2; | |
| 640 | |||
| 641 | ✓✓ | 350 | ORDERKEY_PCTCPU | 
| 642 | ✓✓✗✓ | 295 | ORDERKEY_CPUTIME | 
| 643 | ORDERKEY_STATE | ||
| 644 | ORDERKEY_PRIO | ||
| 645 | ORDERKEY_RSSIZE | ||
| 646 | ORDERKEY_MEM | ||
| 647 | ; | ||
| 648 | 350 | return (result); | |
| 649 | } | ||
| 650 | |||
| 651 | /* compare_size - the comparison function for sorting by total memory usage */ | ||
| 652 | static int | ||
| 653 | compare_size(const void *v1, const void *v2) | ||
| 654 | { | ||
| 655 | struct proc **pp1 = (struct proc **) v1; | ||
| 656 | struct proc **pp2 = (struct proc **) v2; | ||
| 657 | struct kinfo_proc *p1, *p2; | ||
| 658 | int result; | ||
| 659 | |||
| 660 | /* remove one level of indirection */ | ||
| 661 | p1 = *(struct kinfo_proc **) pp1; | ||
| 662 | p2 = *(struct kinfo_proc **) pp2; | ||
| 663 | |||
| 664 | ORDERKEY_MEM | ||
| 665 | ORDERKEY_RSSIZE | ||
| 666 | ORDERKEY_PCTCPU | ||
| 667 | ORDERKEY_CPUTIME | ||
| 668 | ORDERKEY_STATE | ||
| 669 | ORDERKEY_PRIO | ||
| 670 | ; | ||
| 671 | return (result); | ||
| 672 | } | ||
| 673 | |||
| 674 | /* compare_res - the comparison function for sorting by resident set size */ | ||
| 675 | static int | ||
| 676 | compare_res(const void *v1, const void *v2) | ||
| 677 | { | ||
| 678 | struct proc **pp1 = (struct proc **) v1; | ||
| 679 | struct proc **pp2 = (struct proc **) v2; | ||
| 680 | struct kinfo_proc *p1, *p2; | ||
| 681 | int result; | ||
| 682 | |||
| 683 | /* remove one level of indirection */ | ||
| 684 | p1 = *(struct kinfo_proc **) pp1; | ||
| 685 | p2 = *(struct kinfo_proc **) pp2; | ||
| 686 | |||
| 687 | ORDERKEY_RSSIZE | ||
| 688 | ORDERKEY_MEM | ||
| 689 | ORDERKEY_PCTCPU | ||
| 690 | ORDERKEY_CPUTIME | ||
| 691 | ORDERKEY_STATE | ||
| 692 | ORDERKEY_PRIO | ||
| 693 | ; | ||
| 694 | return (result); | ||
| 695 | } | ||
| 696 | |||
| 697 | /* compare_time - the comparison function for sorting by CPU time */ | ||
| 698 | static int | ||
| 699 | compare_time(const void *v1, const void *v2) | ||
| 700 | { | ||
| 701 | struct proc **pp1 = (struct proc **) v1; | ||
| 702 | struct proc **pp2 = (struct proc **) v2; | ||
| 703 | struct kinfo_proc *p1, *p2; | ||
| 704 | int result; | ||
| 705 | |||
| 706 | /* remove one level of indirection */ | ||
| 707 | p1 = *(struct kinfo_proc **) pp1; | ||
| 708 | p2 = *(struct kinfo_proc **) pp2; | ||
| 709 | |||
| 710 | ORDERKEY_CPUTIME | ||
| 711 | ORDERKEY_PCTCPU | ||
| 712 | ORDERKEY_STATE | ||
| 713 | ORDERKEY_PRIO | ||
| 714 | ORDERKEY_MEM | ||
| 715 | ORDERKEY_RSSIZE | ||
| 716 | ; | ||
| 717 | return (result); | ||
| 718 | } | ||
| 719 | |||
| 720 | /* compare_prio - the comparison function for sorting by CPU time */ | ||
| 721 | static int | ||
| 722 | compare_prio(const void *v1, const void *v2) | ||
| 723 | { | ||
| 724 | struct proc **pp1 = (struct proc **) v1; | ||
| 725 | struct proc **pp2 = (struct proc **) v2; | ||
| 726 | struct kinfo_proc *p1, *p2; | ||
| 727 | int result; | ||
| 728 | |||
| 729 | /* remove one level of indirection */ | ||
| 730 | p1 = *(struct kinfo_proc **) pp1; | ||
| 731 | p2 = *(struct kinfo_proc **) pp2; | ||
| 732 | |||
| 733 | ORDERKEY_PRIO | ||
| 734 | ORDERKEY_PCTCPU | ||
| 735 | ORDERKEY_CPUTIME | ||
| 736 | ORDERKEY_STATE | ||
| 737 | ORDERKEY_RSSIZE | ||
| 738 | ORDERKEY_MEM | ||
| 739 | ; | ||
| 740 | return (result); | ||
| 741 | } | ||
| 742 | |||
| 743 | static int | ||
| 744 | compare_pid(const void *v1, const void *v2) | ||
| 745 | { | ||
| 746 | struct proc **pp1 = (struct proc **) v1; | ||
| 747 | struct proc **pp2 = (struct proc **) v2; | ||
| 748 | struct kinfo_proc *p1, *p2; | ||
| 749 | int result; | ||
| 750 | |||
| 751 | /* remove one level of indirection */ | ||
| 752 | p1 = *(struct kinfo_proc **) pp1; | ||
| 753 | p2 = *(struct kinfo_proc **) pp2; | ||
| 754 | |||
| 755 | ORDERKEY_PID | ||
| 756 | ORDERKEY_PCTCPU | ||
| 757 | ORDERKEY_CPUTIME | ||
| 758 | ORDERKEY_STATE | ||
| 759 | ORDERKEY_PRIO | ||
| 760 | ORDERKEY_RSSIZE | ||
| 761 | ORDERKEY_MEM | ||
| 762 | ; | ||
| 763 | return (result); | ||
| 764 | } | ||
| 765 | |||
| 766 | static int | ||
| 767 | compare_cmd(const void *v1, const void *v2) | ||
| 768 | { | ||
| 769 | struct proc **pp1 = (struct proc **) v1; | ||
| 770 | struct proc **pp2 = (struct proc **) v2; | ||
| 771 | struct kinfo_proc *p1, *p2; | ||
| 772 | int result; | ||
| 773 | |||
| 774 | /* remove one level of indirection */ | ||
| 775 | p1 = *(struct kinfo_proc **) pp1; | ||
| 776 | p2 = *(struct kinfo_proc **) pp2; | ||
| 777 | |||
| 778 | ORDERKEY_CMD | ||
| 779 | ORDERKEY_PCTCPU | ||
| 780 | ORDERKEY_CPUTIME | ||
| 781 | ORDERKEY_STATE | ||
| 782 | ORDERKEY_PRIO | ||
| 783 | ORDERKEY_RSSIZE | ||
| 784 | ORDERKEY_MEM | ||
| 785 | ; | ||
| 786 | return (result); | ||
| 787 | } | ||
| 788 | |||
| 789 | |||
| 790 | int (*proc_compares[])(const void *, const void *) = { | ||
| 791 | compare_cpu, | ||
| 792 | compare_size, | ||
| 793 | compare_res, | ||
| 794 | compare_time, | ||
| 795 | compare_prio, | ||
| 796 | compare_pid, | ||
| 797 | compare_cmd, | ||
| 798 | NULL | ||
| 799 | }; | ||
| 800 | |||
| 801 | /* | ||
| 802 | * proc_owner(pid) - returns the uid that owns process "pid", or -1 if | ||
| 803 | * the process does not exist. | ||
| 804 | * It is EXTREMELY IMPORTANT that this function work correctly. | ||
| 805 | * If top runs setuid root (as in SVR4), then this function | ||
| 806 | * is the only thing that stands in the way of a serious | ||
| 807 | * security problem. It validates requests for the "kill" | ||
| 808 | * and "renice" commands. | ||
| 809 | */ | ||
| 810 | uid_t | ||
| 811 | proc_owner(pid_t pid) | ||
| 812 | { | ||
| 813 | struct kinfo_proc **prefp, *pp; | ||
| 814 | int cnt; | ||
| 815 | |||
| 816 | prefp = pref; | ||
| 817 | cnt = pref_len; | ||
| 818 | 	while (--cnt >= 0) { | ||
| 819 | pp = *prefp++; | ||
| 820 | if (pp->p_pid == pid) | ||
| 821 | return ((uid_t)pp->p_ruid); | ||
| 822 | } | ||
| 823 | return (uid_t)(-1); | ||
| 824 | } | ||
| 825 | |||
| 826 | /* | ||
| 827 | * swapmode is rewritten by Tobias Weingartner <weingart@openbsd.org> | ||
| 828 | * to be based on the new swapctl(2) system call. | ||
| 829 | */ | ||
| 830 | static int | ||
| 831 | swapmode(int *used, int *total) | ||
| 832 | 2 | { | |
| 833 | struct swapent *swdev; | ||
| 834 | int nswap, rnswap, i; | ||
| 835 | |||
| 836 | 2 | nswap = swapctl(SWAP_NSWAP, 0, 0); | |
| 837 | ✗✓ | 2 | if (nswap == 0) | 
| 838 | return 0; | ||
| 839 | |||
| 840 | 2 | swdev = calloc(nswap, sizeof(*swdev)); | |
| 841 | ✗✓ | 2 | if (swdev == NULL) | 
| 842 | return 0; | ||
| 843 | |||
| 844 | 2 | rnswap = swapctl(SWAP_STATS, swdev, nswap); | |
| 845 | ✗✓ | 2 | 	if (rnswap == -1) { | 
| 846 | free(swdev); | ||
| 847 | return 0; | ||
| 848 | } | ||
| 849 | |||
| 850 | /* if rnswap != nswap, then what? */ | ||
| 851 | |||
| 852 | /* Total things up */ | ||
| 853 | 2 | *total = *used = 0; | |
| 854 | ✓✓ | 4 | 	for (i = 0; i < nswap; i++) { | 
| 855 | ✓✗ | 2 | 		if (swdev[i].se_flags & SWF_ENABLE) { | 
| 856 | 2 | *used += (swdev[i].se_inuse / (1024 / DEV_BSIZE)); | |
| 857 | 2 | *total += (swdev[i].se_nblks / (1024 / DEV_BSIZE)); | |
| 858 | } | ||
| 859 | } | ||
| 860 | 2 | free(swdev); | |
| 861 | 2 | return 1; | |
| 862 | } | 
| Generated by: GCOVR (Version 3.3) |