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