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