| Commit | Line | Data |
|---|---|---|
| 984263bc MD |
1 | /* |
| 2 | * top - a top users display for Unix | |
| 3 | * | |
| 7af1b2bc | 4 | * SYNOPSIS: For DragonFly 2.x and later |
| 984263bc MD |
5 | * |
| 6 | * DESCRIPTION: | |
| 7 | * Originally written for BSD4.4 system by Christos Zoulas. | |
| 8 | * Ported to FreeBSD 2.x by Steven Wallace && Wolfram Schneider | |
| 9 | * Order support hacked in from top-3.5beta6/machine/m_aix41.c | |
| 10 | * by Monte Mitzelfelt (for latest top see http://www.groupsys.com/topinfo/) | |
| 11 | * | |
| 7af1b2bc JL |
12 | * This is the machine-dependent module for DragonFly 2.5.1 |
| 13 | * Should work for: | |
| 14 | * DragonFly 2.x and above | |
| 984263bc MD |
15 | * |
| 16 | * LIBS: -lkvm | |
| 17 | * | |
| 7af1b2bc JL |
18 | * AUTHOR: Jan Lentfer <Jan.Lentfer@web.de> |
| 19 | * This module has been put together from different sources and is based on the | |
| 20 | * work of many other people, e.g. Matthew Dillon, Simon Schubert, Jordan Gordeev. | |
| 984263bc MD |
21 | * |
| 22 | * $FreeBSD: src/usr.bin/top/machine.c,v 1.29.2.2 2001/07/31 20:27:05 tmm Exp $ | |
| 815943a9 | 23 | * $DragonFly: src/usr.bin/top/machine.c,v 1.26 2008/10/16 01:52:33 swildner Exp $ |
| 984263bc MD |
24 | */ |
| 25 | ||
| 26 | ||
| 27 | #include <sys/time.h> | |
| 28 | #include <sys/types.h> | |
| 29 | #include <sys/signal.h> | |
| 30 | #include <sys/param.h> | |
| 31 | ||
| 32 | #include "os.h" | |
| f5d21610 JS |
33 | #include <err.h> |
| 34 | #include <kvm.h> | |
| 984263bc | 35 | #include <stdio.h> |
| b552171b | 36 | #include <unistd.h> |
| 984263bc | 37 | #include <math.h> |
| 984263bc MD |
38 | #include <pwd.h> |
| 39 | #include <sys/errno.h> | |
| 40 | #include <sys/sysctl.h> | |
| 984263bc MD |
41 | #include <sys/file.h> |
| 42 | #include <sys/time.h> | |
| 984263bc MD |
43 | #include <sys/user.h> |
| 44 | #include <sys/vmmeter.h> | |
| 45 | #include <sys/resource.h> | |
| 46 | #include <sys/rtprio.h> | |
| 47 | ||
| 48 | /* Swap */ | |
| 49 | #include <stdlib.h> | |
| b552171b | 50 | #include <stdio.h> |
| 984263bc MD |
51 | #include <sys/conf.h> |
| 52 | ||
| 53 | #include <osreldate.h> /* for changes in kernel structures */ | |
| 54 | ||
| f5d21610 | 55 | #include <sys/kinfo.h> |
| 9169bd75 | 56 | #include <kinfo.h> |
| 984263bc | 57 | #include "top.h" |
| bec9f4e2 | 58 | #include "display.h" |
| 984263bc | 59 | #include "machine.h" |
| bec9f4e2 | 60 | #include "screen.h" |
| b552171b | 61 | #include "utils.h" |
| 984263bc | 62 | |
| b552171b | 63 | #if 0 |
| 1d1731fa HP |
64 | static int check_nlist(struct nlist *); |
| 65 | static int getkval(unsigned long, int *, int, char *); | |
| b552171b | 66 | #endif |
| 1d1731fa | 67 | int swapmode(int *retavail, int *retfree); |
| 984263bc MD |
68 | static int smpmode; |
| 69 | static int namelength; | |
| 70 | static int cmdlength; | |
| 71 | ||
| 7af1b2bc JL |
72 | int n_cpus = 0; |
| 73 | ||
| 95578ad0 HP |
74 | /* |
| 75 | * needs to be a global symbol, so wrapper can be | |
| 76 | * modified accordingly. | |
| 77 | */ | |
| 78 | static int show_threads = 0; | |
| 984263bc MD |
79 | |
| 80 | /* get_process_info passes back a handle. This is what it looks like: */ | |
| 81 | ||
| 82 | struct handle | |
| 83 | { | |
| 84 | struct kinfo_proc **next_proc; /* points to next valid proc pointer */ | |
| 85 | int remaining; /* number of pointers remaining */ | |
| 86 | }; | |
| 87 | ||
| 88 | /* declarations for load_avg */ | |
| 89 | #include "loadavg.h" | |
| 90 | ||
| 5dfd06ac SS |
91 | #define PP(pp, field) ((pp)->kp_ ## field) |
| 92 | #define LP(pp, field) ((pp)->kp_lwp.kl_ ## field) | |
| 93 | #define VP(pp, field) ((pp)->kp_vm_ ## field) | |
| 984263bc MD |
94 | |
| 95 | /* define what weighted cpu is. */ | |
| 08f2f1bb SS |
96 | #define weighted_cpu(pct, pp) (PP((pp), swtime) == 0 ? 0.0 : \ |
| 97 | ((pct) / (1.0 - exp(PP((pp), swtime) * logcpu)))) | |
| 984263bc MD |
98 | |
| 99 | /* what we consider to be process size: */ | |
| 5dfd06ac | 100 | #define PROCSIZE(pp) (VP((pp), map_size) / 1024) |
| 984263bc | 101 | |
| 984263bc MD |
102 | /* |
| 103 | * These definitions control the format of the per-process area | |
| 104 | */ | |
| 105 | ||
| 106 | static char smp_header[] = | |
| 107 | " PID %-*.*s PRI NICE SIZE RES STATE C TIME WCPU CPU COMMAND"; | |
| 108 | ||
| 109 | #define smp_Proc_format \ | |
| 110 | "%5d %-*.*s %3d %3d%7s %6s %-6.6s %1x%7s %5.2f%% %5.2f%% %.*s" | |
| 111 | ||
| 112 | static char up_header[] = | |
| 113 | " PID %-*.*s PRI NICE SIZE RES STATE TIME WCPU CPU COMMAND"; | |
| 114 | ||
| 115 | #define up_Proc_format \ | |
| 116 | "%5d %-*.*s %3d %3d%7s %6s %-6.6s%.0d%7s %5.2f%% %5.2f%% %.*s" | |
| 117 | ||
| 118 | ||
| 119 | ||
| 120 | /* process state names for the "STATE" column of the display */ | |
| 121 | /* the extra nulls in the string "run" are for adding a slash and | |
| 122 | the processor number when needed */ | |
| 123 | ||
| b552171b | 124 | const char *state_abbrev[] = |
| 984263bc | 125 | { |
| 164b8401 | 126 | "", "RUN\0\0\0", "STOP", "SLEEP", |
| 984263bc MD |
127 | }; |
| 128 | ||
| 129 | ||
| 130 | static kvm_t *kd; | |
| 131 | ||
| 132 | /* values that we stash away in _init and use in later routines */ | |
| 133 | ||
| 134 | static double logcpu; | |
| 135 | ||
| 984263bc | 136 | static long lastpid; |
| f5d21610 | 137 | static int ccpu; |
| 984263bc MD |
138 | |
| 139 | /* these are for calculating cpu state percentages */ | |
| 140 | ||
| edb881dd | 141 | static struct kinfo_cputime *cp_time, *cp_old; |
| 984263bc MD |
142 | |
| 143 | /* these are for detailing the process states */ | |
| 144 | ||
| 145 | int process_states[6]; | |
| 7af1b2bc | 146 | char *procstatenames[] = { |
| 984263bc MD |
147 | "", " starting, ", " running, ", " sleeping, ", " stopped, ", |
| 148 | " zombie, ", | |
| 149 | NULL | |
| 150 | }; | |
| 151 | ||
| 152 | /* these are for detailing the cpu states */ | |
| f5d21610 | 153 | #define CPU_STATES 5 |
| edb881dd | 154 | int *cpu_states; |
| 7af1b2bc | 155 | char *cpustatenames[CPU_STATES + 1] = { |
| 984263bc MD |
156 | "user", "nice", "system", "interrupt", "idle", NULL |
| 157 | }; | |
| 158 | ||
| 159 | /* these are for detailing the memory statistics */ | |
| 160 | ||
| 7af1b2bc JL |
161 | long memory_stats[7]; |
| 162 | char *memorynames[] = { | |
| 984263bc MD |
163 | "K Active, ", "K Inact, ", "K Wired, ", "K Cache, ", "K Buf, ", "K Free", |
| 164 | NULL | |
| 165 | }; | |
| 166 | ||
| 7af1b2bc JL |
167 | long swap_stats[7]; |
| 168 | char *swapnames[] = { | |
| 984263bc MD |
169 | /* 0 1 2 3 4 5 */ |
| 170 | "K Total, ", "K Used, ", "K Free, ", "% Inuse, ", "K In, ", "K Out", | |
| 171 | NULL | |
| 172 | }; | |
| 173 | ||
| 174 | ||
| 175 | /* these are for keeping track of the proc array */ | |
| 176 | ||
| 177 | static int nproc; | |
| 178 | static int onproc = -1; | |
| 179 | static int pref_len; | |
| 180 | static struct kinfo_proc *pbase; | |
| 181 | static struct kinfo_proc **pref; | |
| 182 | ||
| 183 | /* these are for getting the memory statistics */ | |
| 184 | ||
| 185 | static int pageshift; /* log base 2 of the pagesize */ | |
| 186 | ||
| 187 | /* define pagetok in terms of pageshift */ | |
| 188 | ||
| 189 | #define pagetok(size) ((size) << pageshift) | |
| 190 | ||
| 984263bc | 191 | /* sorting orders. first is default */ |
| 7af1b2bc | 192 | char *ordernames[] = { |
| 95578ad0 | 193 | "cpu", "size", "res", "time", "pri", "thr", NULL |
| 984263bc | 194 | }; |
| 7af1b2bc JL |
195 | |
| 196 | /* compare routines */ | |
| 197 | int proc_compare(), compare_size(), compare_res(), compare_time(), compare_prio(), compare_thr(); | |
| 198 | ||
| 199 | int (*proc_compares[])() = { | |
| 200 | proc_compare, | |
| 201 | compare_size, | |
| 202 | compare_res, | |
| 203 | compare_time, | |
| 204 | compare_prio, | |
| 205 | NULL | |
| 206 | }; | |
| 984263bc | 207 | |
| f5d21610 JS |
208 | static void |
| 209 | cputime_percentages(int out[CPU_STATES], struct kinfo_cputime *new, | |
| 210 | struct kinfo_cputime *old) | |
| 211 | { | |
| 212 | struct kinfo_cputime diffs; | |
| f5d21610 JS |
213 | uint64_t total_change, half_total; |
| 214 | ||
| 215 | /* initialization */ | |
| 216 | total_change = 0; | |
| 217 | ||
| 218 | diffs.cp_user = new->cp_user - old->cp_user; | |
| 219 | diffs.cp_nice = new->cp_nice - old->cp_nice; | |
| 220 | diffs.cp_sys = new->cp_sys - old->cp_sys; | |
| 221 | diffs.cp_intr = new->cp_intr - old->cp_intr; | |
| 222 | diffs.cp_idle = new->cp_idle - old->cp_idle; | |
| 223 | total_change = diffs.cp_user + diffs.cp_nice + diffs.cp_sys + | |
| 224 | diffs.cp_intr + diffs.cp_idle; | |
| 225 | old->cp_user = new->cp_user; | |
| 226 | old->cp_nice = new->cp_nice; | |
| 227 | old->cp_sys = new->cp_sys; | |
| 228 | old->cp_intr = new->cp_intr; | |
| 229 | old->cp_idle = new->cp_idle; | |
| 230 | ||
| 231 | /* avoid divide by zero potential */ | |
| 232 | if (total_change == 0) | |
| 233 | total_change = 1; | |
| 234 | ||
| 235 | /* calculate percentages based on overall change, rounding up */ | |
| 236 | half_total = total_change >> 1; | |
| 237 | ||
| 238 | out[0] = ((diffs.cp_user * 1000LL + half_total) / total_change); | |
| 239 | out[1] = ((diffs.cp_nice * 1000LL + half_total) / total_change); | |
| 240 | out[2] = ((diffs.cp_sys * 1000LL + half_total) / total_change); | |
| 241 | out[3] = ((diffs.cp_intr * 1000LL + half_total) / total_change); | |
| 242 | out[4] = ((diffs.cp_idle * 1000LL + half_total) / total_change); | |
| 243 | } | |
| 244 | ||
| 984263bc | 245 | int |
| 1d1731fa | 246 | machine_init(struct statics *statics) |
| 984263bc | 247 | { |
| 815943a9 | 248 | int pagesize; |
| 984263bc MD |
249 | size_t modelen; |
| 250 | struct passwd *pw; | |
| 7af1b2bc | 251 | struct timeval boottime; |
| 984263bc | 252 | |
| edb881dd YT |
253 | if (n_cpus < 1) { |
| 254 | if (kinfo_get_cpus(&n_cpus)) | |
| 255 | err(1, "kinfo_get_cpus failed"); | |
| 256 | } | |
| 7af1b2bc JL |
257 | |
| 258 | /* get boot time */ | |
| 259 | modelen = sizeof(boottime); | |
| 260 | if (sysctlbyname("kern.boottime", &boottime, &modelen, NULL, 0) == -1) { | |
| 261 | ||
| 262 | /* we have no boottime to report */ | |
| 263 | boottime.tv_sec = -1; | |
| 264 | } | |
| 265 | ||
| 984263bc MD |
266 | modelen = sizeof(smpmode); |
| 267 | if ((sysctlbyname("machdep.smp_active", &smpmode, &modelen, NULL, 0) < 0 && | |
| 268 | sysctlbyname("smp.smp_active", &smpmode, &modelen, NULL, 0) < 0) || | |
| 269 | modelen != sizeof(smpmode)) | |
| 270 | smpmode = 0; | |
| 271 | ||
| 272 | while ((pw = getpwent()) != NULL) { | |
| b552171b | 273 | if ((int)strlen(pw->pw_name) > namelength) |
| 984263bc MD |
274 | namelength = strlen(pw->pw_name); |
| 275 | } | |
| 276 | if (namelength < 8) | |
| 277 | namelength = 8; | |
| 278 | if (smpmode && namelength > 13) | |
| 279 | namelength = 13; | |
| 280 | else if (namelength > 15) | |
| 281 | namelength = 15; | |
| 282 | ||
| 283 | if ((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "kvm_open")) == NULL) | |
| 284 | return -1; | |
| 285 | ||
| f5d21610 JS |
286 | if (kinfo_get_sched_ccpu(&ccpu)) { |
| 287 | fprintf(stderr, "top: kinfo_get_sched_ccpu failed\n"); | |
| 984263bc MD |
288 | return(-1); |
| 289 | } | |
| 290 | ||
| 984263bc MD |
291 | /* this is used in calculating WCPU -- calculate it ahead of time */ |
| 292 | logcpu = log(loaddouble(ccpu)); | |
| 293 | ||
| 294 | pbase = NULL; | |
| 295 | pref = NULL; | |
| 296 | nproc = 0; | |
| 297 | onproc = -1; | |
| 298 | /* get the page size with "getpagesize" and calculate pageshift from it */ | |
| 299 | pagesize = getpagesize(); | |
| 300 | pageshift = 0; | |
| 301 | while (pagesize > 1) | |
| 302 | { | |
| 303 | pageshift++; | |
| 304 | pagesize >>= 1; | |
| 305 | } | |
| 306 | ||
| 307 | /* we only need the amount of log(2)1024 for our conversion */ | |
| 308 | pageshift -= LOG1024; | |
| 309 | ||
| 310 | /* fill in the statics information */ | |
| 311 | statics->procstate_names = procstatenames; | |
| 312 | statics->cpustate_names = cpustatenames; | |
| 313 | statics->memory_names = memorynames; | |
| 7af1b2bc | 314 | statics->boottime = boottime.tv_sec; |
| 984263bc | 315 | statics->swap_names = swapnames; |
| 984263bc | 316 | statics->order_names = ordernames; |
| 984263bc MD |
317 | |
| 318 | /* all done! */ | |
| 319 | return(0); | |
| 320 | } | |
| 321 | ||
| b552171b | 322 | char * |
| 7af1b2bc | 323 | format_header(char *uname_field) |
| 984263bc | 324 | { |
| 984263bc MD |
325 | static char Header[128]; |
| 326 | ||
| 327 | snprintf(Header, sizeof(Header), smpmode ? smp_header : up_header, | |
| 328 | namelength, namelength, uname_field); | |
| 329 | ||
| 95578ad0 HP |
330 | if (screen_width <= 79) |
| 331 | cmdlength = 80; | |
| 332 | else | |
| 333 | cmdlength = 89; | |
| 334 | ||
| 335 | cmdlength = cmdlength - strlen(Header) + 6; | |
| 984263bc MD |
336 | |
| 337 | return Header; | |
| 338 | } | |
| 339 | ||
| 340 | static int swappgsin = -1; | |
| 341 | static int swappgsout = -1; | |
| 342 | extern struct timeval timeout; | |
| 343 | ||
| 344 | void | |
| 1d1731fa | 345 | get_system_info(struct system_info *si) |
| 984263bc | 346 | { |
| edb881dd YT |
347 | size_t len; |
| 348 | int cpu; | |
| 349 | ||
| 350 | if (cpu_states == NULL) { | |
| 351 | cpu_states = malloc(sizeof(*cpu_states) * CPU_STATES * n_cpus); | |
| 352 | if (cpu_states == NULL) | |
| 353 | err(1, "malloc"); | |
| 354 | bzero(cpu_states, sizeof(*cpu_states) * CPU_STATES * n_cpus); | |
| 355 | } | |
| 356 | if (cp_time == NULL) { | |
| 357 | cp_time = malloc(2 * n_cpus * sizeof(cp_time[0])); | |
| 358 | if (cp_time == NULL) | |
| 359 | err(1, "cp_time"); | |
| 360 | cp_old = cp_time + n_cpus; | |
| 361 | ||
| 362 | len = n_cpus * sizeof(cp_old[0]); | |
| 363 | bzero(cp_time, len); | |
| 364 | if (sysctlbyname("kern.cputime", cp_old, &len, NULL, 0)) | |
| 365 | err(1, "kern.cputime"); | |
| 366 | } | |
| 984263bc | 367 | |
| edb881dd YT |
368 | len = n_cpus * sizeof(cp_time[0]); |
| 369 | bzero(cp_time, len); | |
| 370 | if (sysctlbyname("kern.cputime", cp_time, &len, NULL, 0)) | |
| 371 | err(1, "kern.cputime"); | |
| 984263bc | 372 | |
| f5d21610 | 373 | getloadavg(si->load_avg, 3); |
| 984263bc | 374 | |
| f5d21610 | 375 | lastpid = 0; |
| 984263bc MD |
376 | |
| 377 | /* convert cp_time counts to percentages */ | |
| edb881dd YT |
378 | for (cpu = 0; cpu < n_cpus; ++cpu) { |
| 379 | cputime_percentages(cpu_states + cpu * CPU_STATES, | |
| 380 | &cp_time[cpu], &cp_old[cpu]); | |
| 381 | } | |
| 984263bc MD |
382 | |
| 383 | /* sum memory & swap statistics */ | |
| 384 | { | |
| 393dfc63 MD |
385 | struct vmmeter vmm; |
| 386 | struct vmstats vms; | |
| e43bcaac JG |
387 | size_t vms_size = sizeof(vms); |
| 388 | size_t vmm_size = sizeof(vmm); | |
| 984263bc MD |
389 | static unsigned int swap_delay = 0; |
| 390 | static int swapavail = 0; | |
| 391 | static int swapfree = 0; | |
| 392 | static int bufspace = 0; | |
| 393 | ||
| 47e61ecd LF |
394 | if (sysctlbyname("vm.vmstats", &vms, &vms_size, NULL, 0)) |
| 395 | err(1, "sysctlbyname: vm.vmstats"); | |
| 396 | ||
| 397 | if (sysctlbyname("vm.vmmeter", &vmm, &vmm_size, NULL, 0)) | |
| 398 | err(1, "sysctlbyname: vm.vmmeter"); | |
| 399 | ||
| 400 | if (kinfo_get_vfs_bufspace(&bufspace)) | |
| 401 | err(1, "kinfo_get_vfs_bufspace"); | |
| 984263bc MD |
402 | |
| 403 | /* convert memory stats to Kbytes */ | |
| 393dfc63 MD |
404 | memory_stats[0] = pagetok(vms.v_active_count); |
| 405 | memory_stats[1] = pagetok(vms.v_inactive_count); | |
| 406 | memory_stats[2] = pagetok(vms.v_wire_count); | |
| 407 | memory_stats[3] = pagetok(vms.v_cache_count); | |
| 984263bc | 408 | memory_stats[4] = bufspace / 1024; |
| 393dfc63 | 409 | memory_stats[5] = pagetok(vms.v_free_count); |
| 984263bc MD |
410 | memory_stats[6] = -1; |
| 411 | ||
| 412 | /* first interval */ | |
| 413 | if (swappgsin < 0) { | |
| 414 | swap_stats[4] = 0; | |
| 415 | swap_stats[5] = 0; | |
| 416 | } | |
| 417 | ||
| 418 | /* compute differences between old and new swap statistic */ | |
| 419 | else { | |
| 393dfc63 MD |
420 | swap_stats[4] = pagetok(((vmm.v_swappgsin - swappgsin))); |
| 421 | swap_stats[5] = pagetok(((vmm.v_swappgsout - swappgsout))); | |
| 984263bc MD |
422 | } |
| 423 | ||
| 393dfc63 MD |
424 | swappgsin = vmm.v_swappgsin; |
| 425 | swappgsout = vmm.v_swappgsout; | |
| 984263bc MD |
426 | |
| 427 | /* call CPU heavy swapmode() only for changes */ | |
| 428 | if (swap_stats[4] > 0 || swap_stats[5] > 0 || swap_delay == 0) { | |
| 429 | swap_stats[3] = swapmode(&swapavail, &swapfree); | |
| 430 | swap_stats[0] = swapavail; | |
| 431 | swap_stats[1] = swapavail - swapfree; | |
| 432 | swap_stats[2] = swapfree; | |
| 433 | } | |
| 434 | swap_delay = 1; | |
| 435 | swap_stats[6] = -1; | |
| 436 | } | |
| 437 | ||
| 438 | /* set arrays and strings */ | |
| 439 | si->cpustates = cpu_states; | |
| 440 | si->memory = memory_stats; | |
| 441 | si->swap = swap_stats; | |
| 442 | ||
| 443 | ||
| 444 | if(lastpid > 0) { | |
| 445 | si->last_pid = lastpid; | |
| 446 | } else { | |
| 447 | si->last_pid = -1; | |
| 448 | } | |
| 984263bc MD |
449 | } |
| 450 | ||
| 7af1b2bc | 451 | |
| 984263bc MD |
452 | static struct handle handle; |
| 453 | ||
| 1d1731fa | 454 | caddr_t get_process_info(struct system_info *si, struct process_select *sel, |
| 7af1b2bc | 455 | int compare_index) |
| 984263bc | 456 | { |
| 815943a9 SW |
457 | int i; |
| 458 | int total_procs; | |
| 459 | int active_procs; | |
| 460 | struct kinfo_proc **prefp; | |
| 461 | struct kinfo_proc *pp; | |
| 984263bc MD |
462 | |
| 463 | /* these are copied out of sel for speed */ | |
| 464 | int show_idle; | |
| 984263bc MD |
465 | int show_system; |
| 466 | int show_uid; | |
| 984263bc MD |
467 | |
| 468 | ||
| 469 | pbase = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc); | |
| 470 | if (nproc > onproc) | |
| 471 | pref = (struct kinfo_proc **) realloc(pref, sizeof(struct kinfo_proc *) | |
| 472 | * (onproc = nproc)); | |
| 473 | if (pref == NULL || pbase == NULL) { | |
| 474 | (void) fprintf(stderr, "top: Out of memory.\n"); | |
| 475 | quit(23); | |
| 476 | } | |
| 477 | /* get a pointer to the states summary array */ | |
| 478 | si->procstates = process_states; | |
| 479 | ||
| 480 | /* set up flags which define what we are going to select */ | |
| 481 | show_idle = sel->idle; | |
| 984263bc MD |
482 | show_system = sel->system; |
| 483 | show_uid = sel->uid != -1; | |
| 984263bc MD |
484 | |
| 485 | /* count up process states and get pointers to interesting procs */ | |
| 486 | total_procs = 0; | |
| 487 | active_procs = 0; | |
| 488 | memset((char *)process_states, 0, sizeof(process_states)); | |
| 489 | prefp = pref; | |
| 490 | for (pp = pbase, i = 0; i < nproc; pp++, i++) | |
| 491 | { | |
| 492 | /* | |
| 493 | * Place pointers to each valid proc structure in pref[]. | |
| 494 | * Process slots that are actually in use have a non-zero | |
| 495 | * status field. Processes with P_SYSTEM set are system | |
| 496 | * processes---these get ignored unless show_sysprocs is set. | |
| 497 | */ | |
| 18170ab7 | 498 | if ((show_threads && (LP(pp, pid) == -1)) || |
| 7af1b2bc | 499 | (show_system || ((PP(pp, flags) & P_SYSTEM) == 0))) |
| 984263bc MD |
500 | { |
| 501 | total_procs++; | |
| 5dfd06ac | 502 | process_states[(unsigned char) PP(pp, stat)]++; |
| 18170ab7 | 503 | if ((show_threads && (LP(pp, pid) == -1)) || |
| 5dfd06ac | 504 | (show_idle || (LP(pp, pctcpu) != 0) || |
| 164b8401 | 505 | (LP(pp, stat) == LSRUN)) && |
| 7af1b2bc | 506 | (!show_uid || PP(pp, ruid) == (uid_t)sel->uid)) |
| 984263bc MD |
507 | { |
| 508 | *prefp++ = pp; | |
| 509 | active_procs++; | |
| 510 | } | |
| 511 | } | |
| 512 | } | |
| 513 | ||
| 7af1b2bc JL |
514 | qsort((char *)pref, active_procs, sizeof(struct kinfo_proc *), |
| 515 | proc_compares[compare_index]); | |
| 984263bc MD |
516 | |
| 517 | /* remember active and total counts */ | |
| 518 | si->p_total = total_procs; | |
| 519 | si->p_active = pref_len = active_procs; | |
| 520 | ||
| 521 | /* pass back a handle */ | |
| 522 | handle.next_proc = pref; | |
| 523 | handle.remaining = active_procs; | |
| 524 | return((caddr_t)&handle); | |
| 525 | } | |
| 526 | ||
| 527 | char fmt[128]; /* static area where result is built */ | |
| 528 | ||
| b552171b | 529 | char * |
| 7af1b2bc | 530 | format_next_process(caddr_t xhandle, char *(*get_userid)(int)) |
| 984263bc | 531 | { |
| 6ac7b760 MD |
532 | struct kinfo_proc *pp; |
| 533 | long cputime; | |
| 534 | double pct; | |
| 984263bc MD |
535 | struct handle *hp; |
| 536 | char status[16]; | |
| 95578ad0 | 537 | char const *wrapper; |
| 984263bc | 538 | int state; |
| b552171b | 539 | int xnice; |
| 984263bc MD |
540 | |
| 541 | /* find and remember the next proc structure */ | |
| b552171b | 542 | hp = (struct handle *)xhandle; |
| 984263bc MD |
543 | pp = *(hp->next_proc++); |
| 544 | hp->remaining--; | |
| 545 | ||
| 95578ad0 | 546 | /* set the wrapper for the process/thread name */ |
| 5dfd06ac | 547 | if ((PP(pp, flags) & P_SWAPPEDOUT)) |
| 95578ad0 | 548 | wrapper = "[]"; /* swapped process [pname] */ |
| 18170ab7 | 549 | else if (((PP(pp, flags) & P_SYSTEM) != 0) && (LP(pp, pid) > 0)) |
| 95578ad0 | 550 | wrapper = "()"; /* system process (pname) */ |
| 18170ab7 | 551 | else if (show_threads && (LP(pp, pid) == -1)) |
| 95578ad0 HP |
552 | wrapper = "<>"; /* pure kernel threads <thread> */ |
| 553 | else | |
| 554 | wrapper = NULL; | |
| 555 | ||
| 984263bc | 556 | /* get the process's command name */ |
| 95578ad0 | 557 | if (wrapper != NULL) { |
| 5dfd06ac SS |
558 | char *comm = PP(pp, comm); |
| 559 | #define COMSIZ sizeof(PP(pp, comm)) | |
| 984263bc MD |
560 | char buf[COMSIZ]; |
| 561 | (void) strncpy(buf, comm, COMSIZ); | |
| 95578ad0 | 562 | comm[0] = wrapper[0]; |
| 984263bc MD |
563 | (void) strncpy(&comm[1], buf, COMSIZ - 2); |
| 564 | comm[COMSIZ - 2] = '\0'; | |
| 95578ad0 | 565 | (void) strncat(comm, &wrapper[1], COMSIZ - 1); |
| 984263bc MD |
566 | comm[COMSIZ - 1] = '\0'; |
| 567 | } | |
| 568 | ||
| 569 | /* | |
| 570 | * Convert the process's runtime from microseconds to seconds. This | |
| 571 | * time includes the interrupt time although that is not wanted here. | |
| 572 | * ps(1) is similarly sloppy. | |
| 573 | */ | |
| 5dfd06ac | 574 | cputime = (LP(pp, uticks) + LP(pp, sticks)) / 1000000; |
| 984263bc MD |
575 | |
| 576 | /* calculate the base for cpu percentages */ | |
| 5dfd06ac | 577 | pct = pctdouble(LP(pp, pctcpu)); |
| 984263bc MD |
578 | |
| 579 | /* generate "STATE" field */ | |
| 164b8401 SS |
580 | switch (state = LP(pp, stat)) { |
| 581 | case LSRUN: | |
| 5dfd06ac SS |
582 | if (smpmode && LP(pp, tdflags) & TDF_RUNNING) |
| 583 | sprintf(status, "CPU%d", LP(pp, cpuid)); | |
| 984263bc MD |
584 | else |
| 585 | strcpy(status, "RUN"); | |
| 586 | break; | |
| 164b8401 | 587 | case LSSLEEP: |
| 5dfd06ac SS |
588 | if (LP(pp, wmesg) != NULL) { |
| 589 | sprintf(status, "%.6s", LP(pp, wmesg)); | |
| 984263bc MD |
590 | break; |
| 591 | } | |
| 592 | /* fall through */ | |
| 593 | default: | |
| 594 | ||
| 595 | if (state >= 0 && | |
| b552171b | 596 | (unsigned)state < sizeof(state_abbrev) / sizeof(*state_abbrev)) |
| 984263bc MD |
597 | sprintf(status, "%.6s", state_abbrev[(unsigned char) state]); |
| 598 | else | |
| 599 | sprintf(status, "?%5d", state); | |
| 600 | break; | |
| 601 | } | |
| 602 | ||
| 416d05d7 | 603 | if (PP(pp, stat) == SZOMB) |
| 164b8401 SS |
604 | strcpy(status, "ZOMB"); |
| 605 | ||
| 17c88c3a MD |
606 | /* |
| 607 | * idle time 0 - 31 -> nice value +21 - +52 | |
| 608 | * normal time -> nice value -20 - +20 | |
| 609 | * real time 0 - 31 -> nice value -52 - -21 | |
| 610 | * thread 0 - 31 -> nice value -53 - | |
| 611 | */ | |
| 5dfd06ac | 612 | switch(LP(pp, rtprio.type)) { |
| 17c88c3a | 613 | case RTP_PRIO_REALTIME: |
| b552171b | 614 | xnice = PRIO_MIN - 1 - RTP_PRIO_MAX + LP(pp, rtprio.prio); |
| 17c88c3a MD |
615 | break; |
| 616 | case RTP_PRIO_IDLE: | |
| b552171b | 617 | xnice = PRIO_MAX + 1 + LP(pp, rtprio.prio); |
| 17c88c3a MD |
618 | break; |
| 619 | case RTP_PRIO_THREAD: | |
| b552171b | 620 | xnice = PRIO_MIN - 1 - RTP_PRIO_MAX - LP(pp, rtprio.prio); |
| 17c88c3a MD |
621 | break; |
| 622 | default: | |
| b552171b | 623 | xnice = PP(pp, nice); |
| 17c88c3a MD |
624 | break; |
| 625 | } | |
| 626 | ||
| 984263bc | 627 | /* format this entry */ |
| 95578ad0 | 628 | snprintf(fmt, sizeof(fmt), |
| 984263bc | 629 | smpmode ? smp_Proc_format : up_Proc_format, |
| b552171b | 630 | (int)PP(pp, pid), |
| 984263bc | 631 | namelength, namelength, |
| b552171b MD |
632 | get_userid(PP(pp, ruid)), |
| 633 | (int)((show_threads && (LP(pp, pid) == -1)) ? | |
| 634 | LP(pp, tdprio) : LP(pp, prio)), | |
| 635 | (int)xnice, | |
| 7af1b2bc JL |
636 | format_k(PROCSIZE(pp)), |
| 637 | format_k(pagetok(VP(pp, rssize))), | |
| 984263bc | 638 | status, |
| b552171b | 639 | (int)(smpmode ? LP(pp, cpuid) : 0), |
| 984263bc MD |
640 | format_time(cputime), |
| 641 | 100.0 * weighted_cpu(pct, pp), | |
| 642 | 100.0 * pct, | |
| 643 | cmdlength, | |
| 5dfd06ac | 644 | printable(PP(pp, comm))); |
| 984263bc MD |
645 | |
| 646 | /* return the result */ | |
| 647 | return(fmt); | |
| 648 | } | |
| 649 | ||
| b552171b | 650 | #if 0 |
| 984263bc MD |
651 | /* |
| 652 | * check_nlist(nlst) - checks the nlist to see if any symbols were not | |
| 653 | * found. For every symbol that was not found, a one-line | |
| 654 | * message is printed to stderr. The routine returns the | |
| 655 | * number of symbols NOT found. | |
| 656 | */ | |
| b552171b MD |
657 | static int |
| 658 | check_nlist(struct nlist *nlst) | |
| 984263bc | 659 | { |
| 815943a9 | 660 | int i; |
| 984263bc MD |
661 | |
| 662 | /* check to see if we got ALL the symbols we requested */ | |
| 663 | /* this will write one line to stderr for every symbol not found */ | |
| 664 | ||
| 665 | i = 0; | |
| 666 | while (nlst->n_name != NULL) | |
| 667 | { | |
| 668 | if (nlst->n_type == 0) | |
| 669 | { | |
| 670 | /* this one wasn't found */ | |
| 671 | (void) fprintf(stderr, "kernel: no symbol named `%s'\n", | |
| 672 | nlst->n_name); | |
| 673 | i = 1; | |
| 674 | } | |
| 675 | nlst++; | |
| 676 | } | |
| 677 | ||
| 678 | return(i); | |
| 679 | } | |
| b552171b | 680 | #endif |
| 984263bc | 681 | |
| 984263bc MD |
682 | /* comparison routines for qsort */ |
| 683 | ||
| 684 | /* | |
| 685 | * proc_compare - comparison function for "qsort" | |
| 686 | * Compares the resource consumption of two processes using five | |
| 687 | * distinct keys. The keys (in descending order of importance) are: | |
| 688 | * percent cpu, cpu ticks, state, resident set size, total virtual | |
| 689 | * memory usage. The process states are ordered as follows (from least | |
| 690 | * to most important): WAIT, zombie, sleep, stop, start, run. The | |
| 691 | * array declaration below maps a process state index into a number | |
| 692 | * that reflects this ordering. | |
| 693 | */ | |
| 694 | ||
| 695 | static unsigned char sorted_state[] = | |
| 696 | { | |
| 697 | 0, /* not used */ | |
| 698 | 3, /* sleep */ | |
| 699 | 1, /* ABANDONED (WAIT) */ | |
| 700 | 6, /* run */ | |
| 701 | 5, /* start */ | |
| 702 | 2, /* zombie */ | |
| 703 | 4 /* stop */ | |
| 704 | }; | |
| 705 | ||
| 706 | ||
| 707 | #define ORDERKEY_PCTCPU \ | |
| 5dfd06ac | 708 | if (lresult = (long) LP(p2, pctcpu) - (long) LP(p1, pctcpu), \ |
| 984263bc MD |
709 | (result = lresult > 0 ? 1 : lresult < 0 ? -1 : 0) == 0) |
| 710 | ||
| 5dfd06ac | 711 | #define CPTICKS(p) (LP(p, uticks) + LP(p, sticks)) |
| 6ac7b760 | 712 | |
| 984263bc | 713 | #define ORDERKEY_CPTICKS \ |
| 6ac7b760 MD |
714 | if ((result = CPTICKS(p2) > CPTICKS(p1) ? 1 : \ |
| 715 | CPTICKS(p2) < CPTICKS(p1) ? -1 : 0) == 0) | |
| 984263bc MD |
716 | |
| 717 | #define ORDERKEY_STATE \ | |
| 5dfd06ac SS |
718 | if ((result = sorted_state[(unsigned char) PP(p2, stat)] - \ |
| 719 | sorted_state[(unsigned char) PP(p1, stat)]) == 0) | |
| 984263bc MD |
720 | |
| 721 | #define ORDERKEY_PRIO \ | |
| 5dfd06ac | 722 | if ((result = LP(p2, prio) - LP(p1, prio)) == 0) |
| 984263bc | 723 | |
| 95578ad0 | 724 | #define ORDERKEY_KTHREADS \ |
| 5dfd06ac | 725 | if ((result = (LP(p1, pid) == 0) - (LP(p2, pid) == 0)) == 0) |
| 95578ad0 HP |
726 | |
| 727 | #define ORDERKEY_KTHREADS_PRIO \ | |
| 5dfd06ac | 728 | if ((result = LP(p2, tdprio) - LP(p1, tdprio)) == 0) |
| 95578ad0 | 729 | |
| 984263bc | 730 | #define ORDERKEY_RSSIZE \ |
| 5dfd06ac | 731 | if ((result = VP(p2, rssize) - VP(p1, rssize)) == 0) |
| 984263bc MD |
732 | |
| 733 | #define ORDERKEY_MEM \ | |
| 734 | if ( (result = PROCSIZE(p2) - PROCSIZE(p1)) == 0 ) | |
| 735 | ||
| 736 | /* compare_cpu - the comparison function for sorting by cpu percentage */ | |
| 737 | ||
| 738 | int | |
| b552171b | 739 | proc_compare(const void *arg1, const void *arg2) |
| 984263bc | 740 | { |
| b552171b MD |
741 | const struct proc *const*pp1 = arg1; |
| 742 | const struct proc *const*pp2 = arg2; | |
| 743 | const struct kinfo_proc *p1; | |
| 744 | const struct kinfo_proc *p2; | |
| 815943a9 SW |
745 | int result; |
| 746 | pctcpu lresult; | |
| 984263bc MD |
747 | |
| 748 | /* remove one level of indirection */ | |
| b552171b MD |
749 | p1 = *(const struct kinfo_proc *const *) pp1; |
| 750 | p2 = *(const struct kinfo_proc *const *) pp2; | |
| 984263bc MD |
751 | |
| 752 | ORDERKEY_PCTCPU | |
| 753 | ORDERKEY_CPTICKS | |
| 754 | ORDERKEY_STATE | |
| 755 | ORDERKEY_PRIO | |
| 756 | ORDERKEY_RSSIZE | |
| 757 | ORDERKEY_MEM | |
| 6d765439 | 758 | {} |
| 984263bc MD |
759 | |
| 760 | return(result); | |
| 761 | } | |
| 762 | ||
| 984263bc MD |
763 | /* compare_size - the comparison function for sorting by total memory usage */ |
| 764 | ||
| 765 | int | |
| b552171b | 766 | compare_size(const void *arg1, const void *arg2) |
| 984263bc | 767 | { |
| b552171b MD |
768 | struct proc *const *pp1 = arg1; |
| 769 | struct proc *const *pp2 = arg2; | |
| 815943a9 SW |
770 | struct kinfo_proc *p1; |
| 771 | struct kinfo_proc *p2; | |
| 772 | int result; | |
| 773 | pctcpu lresult; | |
| 984263bc MD |
774 | |
| 775 | /* remove one level of indirection */ | |
| b552171b MD |
776 | p1 = *(struct kinfo_proc *const*) pp1; |
| 777 | p2 = *(struct kinfo_proc *const*) pp2; | |
| 984263bc MD |
778 | |
| 779 | ORDERKEY_MEM | |
| 780 | ORDERKEY_RSSIZE | |
| 781 | ORDERKEY_PCTCPU | |
| 782 | ORDERKEY_CPTICKS | |
| 783 | ORDERKEY_STATE | |
| 784 | ORDERKEY_PRIO | |
| 6d765439 | 785 | {} |
| 984263bc MD |
786 | |
| 787 | return(result); | |
| 788 | } | |
| 789 | ||
| 790 | /* compare_res - the comparison function for sorting by resident set size */ | |
| 791 | ||
| 792 | int | |
| b552171b | 793 | compare_res(const void *arg1, const void *arg2) |
| 984263bc | 794 | { |
| b552171b MD |
795 | struct proc *const *pp1 = arg1; |
| 796 | struct proc *const *pp2 = arg2; | |
| 815943a9 SW |
797 | struct kinfo_proc *p1; |
| 798 | struct kinfo_proc *p2; | |
| 799 | int result; | |
| 800 | pctcpu lresult; | |
| 984263bc MD |
801 | |
| 802 | /* remove one level of indirection */ | |
| b552171b MD |
803 | p1 = *(struct kinfo_proc *const*) pp1; |
| 804 | p2 = *(struct kinfo_proc *const*) pp2; | |
| 984263bc MD |
805 | |
| 806 | ORDERKEY_RSSIZE | |
| 807 | ORDERKEY_MEM | |
| 808 | ORDERKEY_PCTCPU | |
| 809 | ORDERKEY_CPTICKS | |
| 810 | ORDERKEY_STATE | |
| 811 | ORDERKEY_PRIO | |
| 6d765439 | 812 | {} |
| 984263bc MD |
813 | |
| 814 | return(result); | |
| 815 | } | |
| 816 | ||
| 817 | /* compare_time - the comparison function for sorting by total cpu time */ | |
| 818 | ||
| 819 | int | |
| b552171b | 820 | compare_time(const void *arg1, const void *arg2) |
| 984263bc | 821 | { |
| b552171b MD |
822 | struct proc *const *pp1 = arg1; |
| 823 | struct proc *const *pp2 = arg2; | |
| 824 | const struct kinfo_proc *p1; | |
| 825 | const struct kinfo_proc *p2; | |
| 815943a9 SW |
826 | int result; |
| 827 | pctcpu lresult; | |
| 984263bc MD |
828 | |
| 829 | /* remove one level of indirection */ | |
| b552171b MD |
830 | p1 = *(struct kinfo_proc *const*) pp1; |
| 831 | p2 = *(struct kinfo_proc *const*) pp2; | |
| 984263bc MD |
832 | |
| 833 | ORDERKEY_CPTICKS | |
| 834 | ORDERKEY_PCTCPU | |
| 95578ad0 HP |
835 | ORDERKEY_KTHREADS |
| 836 | ORDERKEY_KTHREADS_PRIO | |
| 984263bc MD |
837 | ORDERKEY_STATE |
| 838 | ORDERKEY_PRIO | |
| 839 | ORDERKEY_RSSIZE | |
| 840 | ORDERKEY_MEM | |
| 6d765439 | 841 | {} |
| 984263bc MD |
842 | |
| 843 | return(result); | |
| 844 | } | |
| 845 | ||
| 846 | /* compare_prio - the comparison function for sorting by cpu percentage */ | |
| 847 | ||
| 848 | int | |
| b552171b | 849 | compare_prio(const void *arg1, const void *arg2) |
| 984263bc | 850 | { |
| b552171b MD |
851 | struct proc *const *pp1 = arg1; |
| 852 | struct proc *const *pp2 = arg2; | |
| 853 | const struct kinfo_proc *p1; | |
| 854 | const struct kinfo_proc *p2; | |
| 815943a9 SW |
855 | int result; |
| 856 | pctcpu lresult; | |
| 984263bc MD |
857 | |
| 858 | /* remove one level of indirection */ | |
| b552171b MD |
859 | p1 = *(struct kinfo_proc *const*) pp1; |
| 860 | p2 = *(struct kinfo_proc *const*) pp2; | |
| 984263bc | 861 | |
| 95578ad0 HP |
862 | ORDERKEY_KTHREADS |
| 863 | ORDERKEY_KTHREADS_PRIO | |
| 984263bc MD |
864 | ORDERKEY_PRIO |
| 865 | ORDERKEY_CPTICKS | |
| 866 | ORDERKEY_PCTCPU | |
| 867 | ORDERKEY_STATE | |
| 868 | ORDERKEY_RSSIZE | |
| 869 | ORDERKEY_MEM | |
| 6d765439 | 870 | {} |
| 984263bc MD |
871 | |
| 872 | return(result); | |
| 873 | } | |
| 95578ad0 HP |
874 | |
| 875 | int | |
| b552171b | 876 | compare_thr(const void *arg1, const void *arg2) |
| 95578ad0 | 877 | { |
| b552171b MD |
878 | struct proc *const *pp1 = arg1; |
| 879 | struct proc *const *pp2 = arg2; | |
| 880 | const struct kinfo_proc *p1; | |
| 881 | const struct kinfo_proc *p2; | |
| 815943a9 SW |
882 | int result; |
| 883 | pctcpu lresult; | |
| 95578ad0 HP |
884 | |
| 885 | /* remove one level of indirection */ | |
| b552171b MD |
886 | p1 = *(struct kinfo_proc *const*) pp1; |
| 887 | p2 = *(struct kinfo_proc *const*) pp2; | |
| 95578ad0 HP |
888 | |
| 889 | ORDERKEY_KTHREADS | |
| 890 | ORDERKEY_KTHREADS_PRIO | |
| 891 | ORDERKEY_CPTICKS | |
| 892 | ORDERKEY_PCTCPU | |
| 893 | ORDERKEY_STATE | |
| 894 | ORDERKEY_RSSIZE | |
| 895 | ORDERKEY_MEM | |
| 6d765439 | 896 | {} |
| 95578ad0 HP |
897 | |
| 898 | return(result); | |
| 899 | } | |
| 900 | ||
| 984263bc MD |
901 | /* |
| 902 | * proc_owner(pid) - returns the uid that owns process "pid", or -1 if | |
| 903 | * the process does not exist. | |
| 904 | * It is EXTREMLY IMPORTANT that this function work correctly. | |
| 905 | * If top runs setuid root (as in SVR4), then this function | |
| 906 | * is the only thing that stands in the way of a serious | |
| 907 | * security problem. It validates requests for the "kill" | |
| 908 | * and "renice" commands. | |
| 909 | */ | |
| 910 | ||
| b552171b MD |
911 | int |
| 912 | proc_owner(int pid) | |
| 984263bc | 913 | { |
| b552171b | 914 | int xcnt; |
| 815943a9 SW |
915 | struct kinfo_proc **prefp; |
| 916 | struct kinfo_proc *pp; | |
| 984263bc MD |
917 | |
| 918 | prefp = pref; | |
| b552171b MD |
919 | xcnt = pref_len; |
| 920 | while (--xcnt >= 0) | |
| 984263bc MD |
921 | { |
| 922 | pp = *prefp++; | |
| 5dfd06ac | 923 | if (PP(pp, pid) == (pid_t)pid) |
| 984263bc | 924 | { |
| 5dfd06ac | 925 | return((int)PP(pp, ruid)); |
| 984263bc MD |
926 | } |
| 927 | } | |
| 928 | return(-1); | |
| 929 | } | |
| 930 | ||
| 931 | ||
| 932 | /* | |
| 933 | * swapmode is based on a program called swapinfo written | |
| 934 | * by Kevin Lahey <kml@rokkaku.atl.ga.us>. | |
| 935 | */ | |
| 984263bc | 936 | int |
| 1d1731fa | 937 | swapmode(int *retavail, int *retfree) |
| 984263bc MD |
938 | { |
| 939 | int n; | |
| 940 | int pagesize = getpagesize(); | |
| 941 | struct kvm_swap swapary[1]; | |
| 942 | ||
| 943 | *retavail = 0; | |
| 944 | *retfree = 0; | |
| 945 | ||
| 946 | #define CONVERT(v) ((quad_t)(v) * pagesize / 1024) | |
| 947 | ||
| 948 | n = kvm_getswapinfo(kd, swapary, 1, 0); | |
| 949 | if (n < 0 || swapary[0].ksw_total == 0) | |
| 950 | return(0); | |
| 951 | ||
| 952 | *retavail = CONVERT(swapary[0].ksw_total); | |
| 953 | *retfree = CONVERT(swapary[0].ksw_total - swapary[0].ksw_used); | |
| 954 | ||
| 955 | n = (int)((double)swapary[0].ksw_used * 100.0 / | |
| 956 | (double)swapary[0].ksw_total); | |
| 957 | return(n); | |
| 958 | } |