1 | /* |
---|
2 | * top - a top users display for Unix |
---|
3 | * |
---|
4 | * SYNOPSIS: Any SGI machine running IRIX 6.2 and up |
---|
5 | * |
---|
6 | * DESCRIPTION: |
---|
7 | * This is the machine-dependent module for IRIX as supplied by |
---|
8 | * engineers at SGI. |
---|
9 | * |
---|
10 | * CFLAGS: -DHAVE_GETOPT -D_OLD_TERMIOS -DORDER |
---|
11 | * |
---|
12 | * AUTHOR: Sandeep Cariapa <cariapa@sgi.com> |
---|
13 | * AUTHOR: Larry McVoy <lm@sgi.com> |
---|
14 | * Sandeep did all the hard work; I ported to 6.2 and fixed up some formats. |
---|
15 | * AUTHOR: John Schimmel <jes@sgi.com> |
---|
16 | * He did the all irix merge. |
---|
17 | * AUTHOR: Ariel Faigon <ariel@sgi.com> |
---|
18 | * Ported to Ficus/Kudzu (IRIX 6.4+). |
---|
19 | * Got rid of all nlist and different (elf64, elf32, COFF) kernel |
---|
20 | * dependencies |
---|
21 | * Various small fixes and enhancements: multiple CPUs, nicer formats. |
---|
22 | * Added -DORDER process display ordering |
---|
23 | * cleaned most -fullwarn'ings. |
---|
24 | * Need -D_OLD_TERMIOS when compiling on IRIX 6.4 to work on 6.2 systems |
---|
25 | * Support much bigger values in memory sizes (over Peta-byte) |
---|
26 | */ |
---|
27 | |
---|
28 | #define _KMEMUSER |
---|
29 | |
---|
30 | #include <sys/types.h> |
---|
31 | #include <sys/time.h> |
---|
32 | #include <sys/stat.h> |
---|
33 | #include <sys/swap.h> |
---|
34 | #include <sys/proc.h> |
---|
35 | #include <sys/procfs.h> |
---|
36 | #include <sys/sysinfo.h> |
---|
37 | #include <sys/sysmp.h> |
---|
38 | #include <sys/utsname.h> |
---|
39 | #include <sys/schedctl.h> /* for < 6.4 NDPHIMAX et al. */ |
---|
40 | #include <paths.h> |
---|
41 | #include <assert.h> |
---|
42 | #include <values.h> |
---|
43 | #include <dirent.h> |
---|
44 | #include <stdio.h> |
---|
45 | #include <unistd.h> |
---|
46 | #include <stdlib.h> |
---|
47 | #include <errno.h> |
---|
48 | #include <fcntl.h> |
---|
49 | #include <dlfcn.h> |
---|
50 | |
---|
51 | #include "top.h" |
---|
52 | #include "machine.h" |
---|
53 | |
---|
54 | #define KMEM "/dev/kmem" |
---|
55 | |
---|
56 | typedef double load_avg; |
---|
57 | #define loaddouble(la) (la) |
---|
58 | #define intload(i) ((double)(i)) |
---|
59 | |
---|
60 | /* |
---|
61 | * Structure for keeping track of CPU times from last time around |
---|
62 | * the program. We keep these things in a hash table, which is |
---|
63 | * recreated at every cycle. |
---|
64 | */ |
---|
65 | struct oldproc { |
---|
66 | pid_t oldpid; |
---|
67 | double oldtime; |
---|
68 | double oldpct; |
---|
69 | }; |
---|
70 | static int oldprocs; /* size of table */ |
---|
71 | static struct oldproc *oldbase; |
---|
72 | #define HASH(x) ((x << 1) % oldprocs) |
---|
73 | |
---|
74 | |
---|
75 | #define pagetok(pages) ((((uint64_t) pages) * pagesize) >> 10) |
---|
76 | |
---|
77 | /* |
---|
78 | * Ugly hack, save space and complexity of allocating and maintaining |
---|
79 | * parallel arrays to the prpsinfo array: use spare space (pr_fill area) |
---|
80 | * in prpsinfo structures to store %CPU calculated values |
---|
81 | */ |
---|
82 | #define D_align(addr) (((unsigned long)(addr) & ~0x0fU)) |
---|
83 | #define percent_cpu(pp) (* (double *) D_align(&((pp)->pr_fill[0]))) |
---|
84 | #define weighted_cpu(pp) (* (double *) D_align(&((pp)->pr_fill[4]))) |
---|
85 | |
---|
86 | |
---|
87 | /* Username field to fill in starts at: */ |
---|
88 | #define UNAME_START 16 |
---|
89 | |
---|
90 | /* |
---|
91 | * These definitions control the format of the per-process area |
---|
92 | */ |
---|
93 | static char header[] = |
---|
94 | " PID PGRP X PRI SIZE RES STATE TIME %WCPU %CPU COMMAND"; |
---|
95 | /* |
---|
96 | 012345678901234567890123456789012345678901234567890123456789012345678901234567 |
---|
97 | 10 20 30 40 50 60 70 |
---|
98 | */ |
---|
99 | |
---|
100 | /* PID PGRP USER PRI SIZE RES STATE TIME %WCPU %CPU CMD */ |
---|
101 | #define Proc_format \ |
---|
102 | "%7d %7d %-8.8s %4.4s %6.6s %5.5s %-6.6s %6.6s %5.2f %5.2f %-.10s" |
---|
103 | |
---|
104 | |
---|
105 | /* |
---|
106 | * these are for detailing the cpu states |
---|
107 | * Data is taken from the sysinfo structure (see <sys/sysinfo.h>) |
---|
108 | * We rely on the following values: |
---|
109 | * |
---|
110 | * #define CPU_IDLE 0 |
---|
111 | * #define CPU_USER 1 |
---|
112 | * #define CPU_KERNEL 2 |
---|
113 | * #define CPU_WAIT 3 |
---|
114 | * #define CPU_SXBRK 4 |
---|
115 | * #define CPU_INTR 5 |
---|
116 | */ |
---|
117 | #ifndef CPU_STATES /* defined only in 6.4 and up */ |
---|
118 | # define CPU_STATES 6 |
---|
119 | #endif |
---|
120 | |
---|
121 | int cpu_states[CPU_STATES]; |
---|
122 | char *cpustatenames[] = { |
---|
123 | "idle", "usr", "ker", "wait", "xbrk", "intr", |
---|
124 | NULL |
---|
125 | }; |
---|
126 | |
---|
127 | /* these are for detailing the memory statistics */ |
---|
128 | |
---|
129 | #define MEMSTATS 10 |
---|
130 | int memory_stats[MEMSTATS]; |
---|
131 | char *memorynames[] = { |
---|
132 | "K max, ", "K avail, ", "K free, ", "K swap, ", "K free swap", NULL |
---|
133 | }; |
---|
134 | |
---|
135 | char uname_str[40]; |
---|
136 | double load[3]; |
---|
137 | static char fmt[MAX_COLS + 2]; |
---|
138 | int numcpus; |
---|
139 | |
---|
140 | /* useful externals */ |
---|
141 | extern int errno; |
---|
142 | extern char *sys_errlist[]; |
---|
143 | |
---|
144 | extern char *myname; |
---|
145 | extern char *format_k(); |
---|
146 | extern char *format_time(); |
---|
147 | extern long percentages(); |
---|
148 | |
---|
149 | static int kmem; |
---|
150 | static unsigned long avenrun_offset; |
---|
151 | |
---|
152 | static float irix_ver; /* for easy numeric comparison */ |
---|
153 | |
---|
154 | static struct prpsinfo *pbase; |
---|
155 | static struct prpsinfo **pref; |
---|
156 | static struct oldproc *oldbase; |
---|
157 | static int oldprocs; /* size of table */ |
---|
158 | |
---|
159 | static DIR *procdir; |
---|
160 | |
---|
161 | static int ptable_size; /* allocated process table size */ |
---|
162 | static int nproc; /* estimated process table size */ |
---|
163 | static int pagesize; |
---|
164 | |
---|
165 | /* get_process_info passes back a handle. This is what it looks like: */ |
---|
166 | struct handle { |
---|
167 | struct prpsinfo **next_proc; /* points to next valid proc pointer */ |
---|
168 | int remaining; /* number of pointers remaining */ |
---|
169 | }; |
---|
170 | |
---|
171 | static struct handle handle; |
---|
172 | |
---|
173 | void getptable(struct prpsinfo *baseptr); |
---|
174 | void size(int fd, struct prpsinfo *ps); |
---|
175 | |
---|
176 | extern char *ordernames[]; |
---|
177 | |
---|
178 | /* |
---|
179 | * Process states letters are mapped into numbers |
---|
180 | * 6.5 seems to have changed the semantics of prpsinfo.pr_state |
---|
181 | * so we rely, (like ps does) on the char value pr_sname. |
---|
182 | * The order we use here is what may be most interesting |
---|
183 | * to top users: Most interesting state on top, least on bottom. |
---|
184 | * 'S' (sleeping) is the most common case so I put it _after_ |
---|
185 | * zombie, even though it is more "active" than zombie. |
---|
186 | * |
---|
187 | * State letters and their meanings: |
---|
188 | * |
---|
189 | * R Process is running (may not have a processor yet) |
---|
190 | * I Process is in intermediate state of creation |
---|
191 | * X Process is waiting for memory |
---|
192 | * T Process is stopped |
---|
193 | * Z Process is terminated and parent not waiting (zombie) |
---|
194 | * S Process is sleeping, waiting for a resource |
---|
195 | */ |
---|
196 | |
---|
197 | /* abbreviated process states */ |
---|
198 | static char *state_abbrev[] = |
---|
199 | { "", "sleep", "zomb", "stop", "swap", "start", "ready", "run", NULL }; |
---|
200 | |
---|
201 | /* Same but a little "wordier", used in CPU activity summary */ |
---|
202 | int process_states[8]; /* per state counters */ |
---|
203 | char *procstatenames[] = { |
---|
204 | /* ready to run is considered running here */ |
---|
205 | "", " sleeping, ", " zombie, ", " stopped, ", |
---|
206 | " swapped, ", " starting, ", " ready, ", " running, ", |
---|
207 | NULL |
---|
208 | }; |
---|
209 | |
---|
210 | #define S_RUNNING 7 |
---|
211 | #define S_READY 6 |
---|
212 | #define S_STARTING 5 |
---|
213 | #define S_SWAPPED 4 |
---|
214 | #define S_STOPPED 3 |
---|
215 | #define S_ZOMBIE 2 |
---|
216 | #define S_SLEEPING 1 |
---|
217 | |
---|
218 | #define IS_ACTIVE(pp) \ |
---|
219 | (first_screen ? proc_state(pp) >= S_STARTING : percent_cpu(pp) > 0.0) |
---|
220 | |
---|
221 | /* |
---|
222 | * proc_state |
---|
223 | * map the pr_sname value to an integer. |
---|
224 | * used as an index into state_abbrev[] |
---|
225 | * as well as an "order" key |
---|
226 | */ |
---|
227 | static int proc_state(struct prpsinfo *pp) |
---|
228 | { |
---|
229 | char psname = pp->pr_sname; |
---|
230 | |
---|
231 | switch (psname) { |
---|
232 | case 'R': return |
---|
233 | (pp->pr_sonproc >= 0 && pp->pr_sonproc < numcpus) ? |
---|
234 | S_RUNNING /* on a processor */ : S_READY; |
---|
235 | case 'I': return S_STARTING; |
---|
236 | case 'X': return S_SWAPPED; |
---|
237 | case 'T': return S_STOPPED; |
---|
238 | case 'Z': return S_ZOMBIE; |
---|
239 | case 'S': return S_SLEEPING; |
---|
240 | default : return 0; |
---|
241 | } |
---|
242 | } |
---|
243 | |
---|
244 | |
---|
245 | /* |
---|
246 | * To avoid nlist'ing the kernel (with all the different kernel type |
---|
247 | * complexities), we estimate the size of the needed working process |
---|
248 | * table by scanning /proc/pinfo and taking the number of entries |
---|
249 | * multiplied by some reasonable factor. |
---|
250 | * Assume current dir is _PATH_PROCFSPI |
---|
251 | */ |
---|
252 | static int active_proc_count() |
---|
253 | { |
---|
254 | DIR *dirp; |
---|
255 | int pcnt; |
---|
256 | |
---|
257 | if ((dirp = opendir(".")) == NULL) { |
---|
258 | (void) fprintf(stderr, "%s: Unable to open %s\n", |
---|
259 | myname, _PATH_PROCFSPI); |
---|
260 | exit(1); |
---|
261 | } |
---|
262 | for (pcnt = 0; readdir(dirp) != NULL; pcnt++) |
---|
263 | ; |
---|
264 | closedir(dirp); |
---|
265 | |
---|
266 | return pcnt; |
---|
267 | } |
---|
268 | |
---|
269 | /* |
---|
270 | * allocate space for: |
---|
271 | * proc structure array |
---|
272 | * array of pointers to the above (used for sorting) |
---|
273 | * array for storing per-process old CPU usage |
---|
274 | */ |
---|
275 | void |
---|
276 | allocate_proc_tables() |
---|
277 | { |
---|
278 | int n_active = active_proc_count(); |
---|
279 | |
---|
280 | if (pbase != NULL) /* && n_active < ptable_size */ |
---|
281 | return; |
---|
282 | |
---|
283 | /* Need to realloc if we exceed, but factor should be enough */ |
---|
284 | nproc = n_active * 5; |
---|
285 | oldprocs = 2 * nproc; |
---|
286 | |
---|
287 | pbase = (struct prpsinfo *) |
---|
288 | malloc(nproc * sizeof(struct prpsinfo)); |
---|
289 | pref = (struct prpsinfo **) |
---|
290 | malloc(nproc * sizeof(struct prpsinfo *)); |
---|
291 | oldbase = (struct oldproc *) |
---|
292 | malloc (oldprocs * sizeof(struct oldproc)); |
---|
293 | |
---|
294 | ptable_size = nproc; |
---|
295 | |
---|
296 | if (pbase == NULL || pref == NULL || oldbase == NULL) { |
---|
297 | (void) fprintf(stderr, "%s: malloc: out of memory\n", myname); |
---|
298 | exit (1); |
---|
299 | } |
---|
300 | } |
---|
301 | |
---|
302 | int |
---|
303 | machine_init(struct statics *statics) |
---|
304 | { |
---|
305 | struct oldproc *op, *endbase; |
---|
306 | int pcnt = 0; |
---|
307 | struct utsname utsname; |
---|
308 | char tmpbuf[20]; |
---|
309 | |
---|
310 | uname(&utsname); |
---|
311 | irix_ver = (float) atof((const char *)utsname.release); |
---|
312 | strncpy(tmpbuf, utsname.release, 9); |
---|
313 | tmpbuf[9] = '\0'; |
---|
314 | sprintf(uname_str, "%s %-.14s %s %s", |
---|
315 | utsname.sysname, utsname.nodename, |
---|
316 | tmpbuf, utsname.machine); |
---|
317 | |
---|
318 | pagesize = getpagesize(); |
---|
319 | |
---|
320 | if ((kmem = open(KMEM, O_RDONLY)) == -1) { |
---|
321 | perror(KMEM); |
---|
322 | return -1; |
---|
323 | } |
---|
324 | |
---|
325 | if (chdir(_PATH_PROCFSPI)) { |
---|
326 | /* handy for later on when we're reading it */ |
---|
327 | (void) fprintf(stderr, "%s: Unable to chdir to %s\n", |
---|
328 | myname, _PATH_PROCFSPI); |
---|
329 | return -1; |
---|
330 | } |
---|
331 | if ((procdir = opendir(".")) == NULL) { |
---|
332 | (void) fprintf(stderr, "%s: Unable to open %s\n", |
---|
333 | myname, _PATH_PROCFSPI); |
---|
334 | return -1; |
---|
335 | } |
---|
336 | |
---|
337 | if ((avenrun_offset = sysmp(MP_KERNADDR, MPKA_AVENRUN)) == -1) { |
---|
338 | perror("sysmp(MP_KERNADDR, MPKA_AVENRUN)"); |
---|
339 | return -1; |
---|
340 | } |
---|
341 | |
---|
342 | allocate_proc_tables(); |
---|
343 | |
---|
344 | oldprocs = 2 * nproc; |
---|
345 | endbase = oldbase + oldprocs; |
---|
346 | for (op = oldbase; op < endbase; op++) { |
---|
347 | op->oldpid = -1; |
---|
348 | } |
---|
349 | |
---|
350 | statics->cpustate_names = cpustatenames; |
---|
351 | statics->memory_names = memorynames; |
---|
352 | statics->order_names = ordernames; |
---|
353 | statics->procstate_names = procstatenames; |
---|
354 | |
---|
355 | return (0); |
---|
356 | } |
---|
357 | |
---|
358 | char * |
---|
359 | format_header(uname_field) |
---|
360 | register char *uname_field; |
---|
361 | { |
---|
362 | register char *ptr; |
---|
363 | |
---|
364 | ptr = header + UNAME_START; |
---|
365 | while (*uname_field != '\0') { |
---|
366 | *ptr++ = *uname_field++; |
---|
367 | } |
---|
368 | |
---|
369 | return (header); |
---|
370 | } |
---|
371 | |
---|
372 | void |
---|
373 | get_system_info(si) |
---|
374 | struct system_info *si; |
---|
375 | { |
---|
376 | int i; |
---|
377 | int avenrun[3]; |
---|
378 | struct rminfo realmem; |
---|
379 | struct sysinfo sysinfo; |
---|
380 | static time_t cp_old [CPU_STATES]; |
---|
381 | static time_t cp_diff[CPU_STATES]; /* for cpu state percentages */ |
---|
382 | off_t fswap; /* current free swap in blocks */ |
---|
383 | off_t tswap; /* total swap in blocks */ |
---|
384 | |
---|
385 | (void) getkval(avenrun_offset, (int *) avenrun, sizeof(avenrun), "avenrun"); |
---|
386 | |
---|
387 | for (i = 0; i < 3; i++) { |
---|
388 | si->load_avg[i] = loaddouble(avenrun[i]); |
---|
389 | si->load_avg[i] /= 1024.0; |
---|
390 | } |
---|
391 | |
---|
392 | if ((numcpus = sysmp(MP_NPROCS)) == -1) { |
---|
393 | perror("sysmp(MP_NPROCS)"); |
---|
394 | return; |
---|
395 | } |
---|
396 | |
---|
397 | if (sysmp(MP_SAGET, MPSA_RMINFO, &realmem, sizeof(realmem)) == -1) { |
---|
398 | perror("sysmp(MP_SAGET,MPSA_RMINFO, ...)"); |
---|
399 | return; |
---|
400 | } |
---|
401 | |
---|
402 | swapctl(SC_GETFREESWAP, &fswap); |
---|
403 | swapctl(SC_GETSWAPTOT, &tswap); |
---|
404 | |
---|
405 | memory_stats[0] = pagetok(realmem.physmem); |
---|
406 | memory_stats[1] = pagetok(realmem.availrmem); |
---|
407 | memory_stats[2] = pagetok(realmem.freemem); |
---|
408 | memory_stats[3] = tswap / 2; |
---|
409 | memory_stats[4] = fswap / 2; |
---|
410 | |
---|
411 | if (sysmp(MP_SAGET,MPSA_SINFO, &sysinfo,sizeof(struct sysinfo)) == -1) { |
---|
412 | perror("sysmp(MP_SAGET,MPSA_SINFO)"); |
---|
413 | return; |
---|
414 | } |
---|
415 | (void) percentages(CPU_STATES, cpu_states, sysinfo.cpu, cp_old, cp_diff); |
---|
416 | |
---|
417 | si->cpustates = cpu_states; |
---|
418 | si->memory = memory_stats; |
---|
419 | si->last_pid = -1; |
---|
420 | |
---|
421 | return; |
---|
422 | } |
---|
423 | |
---|
424 | caddr_t |
---|
425 | get_process_info(si, sel, compare) |
---|
426 | struct system_info *si; |
---|
427 | struct process_select *sel; |
---|
428 | int (*compare) (); |
---|
429 | |
---|
430 | { |
---|
431 | int i, total_procs, active_procs; |
---|
432 | struct prpsinfo **prefp; |
---|
433 | struct prpsinfo *pp; |
---|
434 | int show_uid; |
---|
435 | static char first_screen = 1; |
---|
436 | |
---|
437 | /* read all the proc structures */ |
---|
438 | getptable(pbase); |
---|
439 | |
---|
440 | /* get a pointer to the states summary array */ |
---|
441 | si->procstates = process_states; |
---|
442 | |
---|
443 | /* set up flags which define what we are going to select */ |
---|
444 | show_uid = sel->uid != -1; |
---|
445 | |
---|
446 | /* count up process states and get pointers to interesting procs */ |
---|
447 | total_procs = 0; |
---|
448 | active_procs = 0; |
---|
449 | (void) memset(process_states, 0, sizeof(process_states)); |
---|
450 | prefp = pref; |
---|
451 | |
---|
452 | for (pp = pbase, i = 0; i < nproc; pp++, i++) { |
---|
453 | /* |
---|
454 | * Place pointers to each valid proc structure in pref[]. |
---|
455 | * Process slots that are actually in use have a non-zero |
---|
456 | * status field. Processes with SSYS set are system |
---|
457 | * processes---these get ignored unless show_system is set. |
---|
458 | * Ariel: IRIX 6.4 had to redefine "system processes" |
---|
459 | * They do not exist outside the kernel in new kernels. |
---|
460 | * Now defining as uid==0 and ppid==1 (init children) |
---|
461 | */ |
---|
462 | if (pp->pr_state && |
---|
463 | (sel->system || !(pp->pr_uid==0 && pp->pr_ppid==1))) { |
---|
464 | total_procs++; |
---|
465 | process_states[proc_state(pp)]++; |
---|
466 | /* |
---|
467 | * zombies are actually interesting (to avoid) |
---|
468 | * although they are not active, so I leave them |
---|
469 | * displayed. |
---|
470 | */ |
---|
471 | if (/* (! pp->pr_zomb) && */ |
---|
472 | (sel->idle || IS_ACTIVE(pp)) && |
---|
473 | (! show_uid || pp->pr_uid == (uid_t) sel->uid)) { |
---|
474 | *prefp++ = pp; |
---|
475 | active_procs++; |
---|
476 | } |
---|
477 | } |
---|
478 | } |
---|
479 | first_screen = 0; |
---|
480 | |
---|
481 | /* if requested, sort the "interesting" processes */ |
---|
482 | if (compare != NULL) |
---|
483 | qsort((char *) pref, active_procs, sizeof(struct prpsinfo *), compare); |
---|
484 | |
---|
485 | /* remember active and total counts */ |
---|
486 | si->p_total = total_procs; |
---|
487 | si->p_active = active_procs; |
---|
488 | |
---|
489 | /* pass back a handle */ |
---|
490 | handle.next_proc = pref; |
---|
491 | handle.remaining = active_procs; |
---|
492 | return ((caddr_t) &handle); |
---|
493 | } |
---|
494 | |
---|
495 | /* |
---|
496 | * Added cpu_id to running processes, add 'ready' (to run) state |
---|
497 | */ |
---|
498 | static char * format_state(struct prpsinfo *pp) |
---|
499 | { |
---|
500 | static char state_str[16]; |
---|
501 | int state = proc_state(pp); |
---|
502 | |
---|
503 | if (state == S_RUNNING) { |
---|
504 | /* |
---|
505 | * Alert: 6.2 (MP only?) binary incompatibility |
---|
506 | * pp->pr_sonproc apparently (?) has a different |
---|
507 | * offset on 6.2 machines... I've seen cases where |
---|
508 | * a 6.4 compiled top running on 6.2 printed |
---|
509 | * a garbage CPU-id. To be safe, I print the CPU-id |
---|
510 | * only if it falls within range [0..numcpus-1] |
---|
511 | */ |
---|
512 | sprintf(state_str, "run/%d", pp->pr_sonproc); |
---|
513 | return state_str; |
---|
514 | } |
---|
515 | |
---|
516 | /* default */ |
---|
517 | return state_abbrev[state]; |
---|
518 | } |
---|
519 | |
---|
520 | static char * |
---|
521 | format_prio(pp) |
---|
522 | struct prpsinfo *pp; |
---|
523 | { |
---|
524 | static char prio_str[10]; |
---|
525 | |
---|
526 | if (irix_ver < 6.4) { |
---|
527 | /* |
---|
528 | * Note: this is _compiled_ on 6.x where x >= 4 but I would like |
---|
529 | * it to run on 6.2 6.3 as well (backward binary compatibility). |
---|
530 | * Scheduling is completely different between these IRIX versions |
---|
531 | * and some scheduling classes may even have different names. |
---|
532 | * |
---|
533 | * The solution: have more than one style of 'priority' depending |
---|
534 | * on the OS version. |
---|
535 | * |
---|
536 | * See npri(1) + nice(2) + realtime(5) for scheduling classes, |
---|
537 | * and priority values. |
---|
538 | */ |
---|
539 | if (pp->pr_pri <= NDPHIMIN) /* real time? */ |
---|
540 | sprintf(prio_str, "+%d", pp->pr_pri); |
---|
541 | else if (pp->pr_pri <= NDPNORMMIN) /* normal interactive */ |
---|
542 | sprintf(prio_str, "%d", pp->pr_pri); |
---|
543 | else /* batch: low prio */ |
---|
544 | sprintf(prio_str, "b%d", pp->pr_pri); |
---|
545 | |
---|
546 | } else { |
---|
547 | |
---|
548 | /* copied from Kostadis's code */ |
---|
549 | |
---|
550 | if (strcmp(pp->pr_clname, "RT") == 0) /* real time */ |
---|
551 | sprintf(prio_str, "+%d", pp->pr_pri); |
---|
552 | else if (strcmp(pp->pr_clname, "DL") == 0) /* unsupported ? */ |
---|
553 | sprintf(prio_str, "d%d", pp->pr_pri); |
---|
554 | else if (strcmp(pp->pr_clname, "GN") == 0) |
---|
555 | sprintf(prio_str, "g%d", pp->pr_pri); |
---|
556 | else if (strcmp(pp->pr_clname, "GB") == 0) |
---|
557 | sprintf(prio_str, "p%d", pp->pr_pri); |
---|
558 | |
---|
559 | else if (strcmp(pp->pr_clname, "WL") == 0) /* weightless */ |
---|
560 | return "w"; |
---|
561 | else if (strcmp(pp->pr_clname, "BC") == 0) |
---|
562 | return "bc"; /* batch critical */ |
---|
563 | else if (strcmp(pp->pr_clname, "B") == 0) |
---|
564 | return "b"; /* batch */ |
---|
565 | else |
---|
566 | sprintf(prio_str, "%d", pp->pr_pri); |
---|
567 | } |
---|
568 | return prio_str; |
---|
569 | } |
---|
570 | |
---|
571 | static double |
---|
572 | clip_percent(double pct) |
---|
573 | { |
---|
574 | if (pct < 0) { |
---|
575 | return 0.0; |
---|
576 | } else if (pct >= 100) { |
---|
577 | return 99.99; |
---|
578 | } |
---|
579 | return pct; |
---|
580 | } |
---|
581 | |
---|
582 | char * |
---|
583 | format_next_process(handle, get_userid) |
---|
584 | caddr_t handle; |
---|
585 | char *(*get_userid)(); |
---|
586 | { |
---|
587 | struct prpsinfo *pp; |
---|
588 | struct handle *hp; |
---|
589 | long cputime; |
---|
590 | |
---|
591 | /* find and remember the next proc structure */ |
---|
592 | hp = (struct handle *) handle; |
---|
593 | pp = *(hp->next_proc++); |
---|
594 | hp->remaining--; |
---|
595 | |
---|
596 | /* get the process cpu usage since startup */ |
---|
597 | cputime = pp->pr_time.tv_sec; |
---|
598 | |
---|
599 | /* format this entry */ |
---|
600 | sprintf(fmt, |
---|
601 | Proc_format, |
---|
602 | pp->pr_pid, |
---|
603 | pp->pr_pgrp, |
---|
604 | (*get_userid) (pp->pr_uid), |
---|
605 | format_prio(pp), |
---|
606 | format_k(pagetok(pp->pr_size)), |
---|
607 | format_k(pagetok(pp->pr_rssize)), |
---|
608 | format_state(pp), |
---|
609 | format_time(cputime), |
---|
610 | clip_percent(weighted_cpu(pp)), |
---|
611 | clip_percent(percent_cpu(pp)), |
---|
612 | pp->pr_fname); |
---|
613 | |
---|
614 | /* return the result */ |
---|
615 | return (fmt); |
---|
616 | } |
---|
617 | |
---|
618 | /* |
---|
619 | * getkval(offset, ptr, size, refstr) - get a value out of the kernel. |
---|
620 | * "offset" is the byte offset into the kernel for the desired value, |
---|
621 | * "ptr" points to a buffer into which the value is retrieved, |
---|
622 | * "size" is the size of the buffer (and the object to retrieve), |
---|
623 | * "refstr" is a reference string used when printing error meessages, |
---|
624 | * if "refstr" starts with a '!', then a failure on read will not |
---|
625 | * be fatal (this may seem like a silly way to do things, but I |
---|
626 | * really didn't want the overhead of another argument). |
---|
627 | * |
---|
628 | */ |
---|
629 | |
---|
630 | int |
---|
631 | getkval(offset, ptr, size, refstr) |
---|
632 | unsigned long offset; |
---|
633 | int *ptr; |
---|
634 | int size; |
---|
635 | char *refstr; |
---|
636 | |
---|
637 | { |
---|
638 | if (lseek(kmem, (long) offset, SEEK_SET) == -1) { |
---|
639 | if (*refstr == '!') |
---|
640 | refstr++; |
---|
641 | (void) fprintf(stderr, "%s: %s: lseek to %s: %s\n", |
---|
642 | myname, KMEM, refstr, strerror(errno)); |
---|
643 | exit(0); |
---|
644 | } |
---|
645 | if (read(kmem, (char *) ptr, size) == -1) { |
---|
646 | if (*refstr == '!') |
---|
647 | return (0); |
---|
648 | else { |
---|
649 | (void) fprintf(stderr, "%s: %s: reading %s: %s\n", |
---|
650 | myname, KMEM, refstr, strerror(errno)); |
---|
651 | exit(0); |
---|
652 | } |
---|
653 | } |
---|
654 | return (1); |
---|
655 | } |
---|
656 | |
---|
657 | /* |
---|
658 | * compare_K - comparison functions for "qsort" |
---|
659 | * Compares the resource consumption of two processes using five |
---|
660 | * distinct keys. The keys are: |
---|
661 | * percent cpu, cpu ticks, state, resident set size, total virtual |
---|
662 | * memory usage. The process states are ordered as follows (from least |
---|
663 | * to most important): WAIT, zombie, sleep, stop, idle, run. |
---|
664 | * Different comparison functions are used for different orderings. |
---|
665 | */ |
---|
666 | |
---|
667 | /* these are names given to allowed sorting orders -- first is default */ |
---|
668 | char *ordernames[] = { |
---|
669 | /* |
---|
670 | * Aliases for user convenience/friendliness: |
---|
671 | * mem == size |
---|
672 | * rss == res |
---|
673 | */ |
---|
674 | "cpu", "size", "mem", "res", "rss", |
---|
675 | "time", "state", "command", "prio", NULL |
---|
676 | }; |
---|
677 | |
---|
678 | /* forward definitions for comparison functions */ |
---|
679 | int compare_cpu(struct prpsinfo **pp1, struct prpsinfo **pp2); |
---|
680 | int compare_size(struct prpsinfo **pp1, struct prpsinfo **pp2); |
---|
681 | int compare_res(struct prpsinfo **pp1, struct prpsinfo **pp2); |
---|
682 | int compare_time(struct prpsinfo **pp1, struct prpsinfo **pp2); |
---|
683 | int compare_state(struct prpsinfo **pp1, struct prpsinfo **pp2); |
---|
684 | int compare_cmd(struct prpsinfo **pp1, struct prpsinfo **pp2); |
---|
685 | int compare_prio(struct prpsinfo **pp1, struct prpsinfo **pp2); |
---|
686 | |
---|
687 | int (*proc_compares[])() = { |
---|
688 | compare_cpu, |
---|
689 | compare_size, |
---|
690 | compare_size, |
---|
691 | compare_res, |
---|
692 | compare_res, |
---|
693 | compare_time, |
---|
694 | compare_state, |
---|
695 | compare_cmd, |
---|
696 | compare_prio, |
---|
697 | NULL |
---|
698 | }; |
---|
699 | |
---|
700 | |
---|
701 | /* |
---|
702 | * The possible comparison expressions. These are defined in such a way |
---|
703 | * that they can be merely listed in the source code to define the actual |
---|
704 | * desired ordering. |
---|
705 | */ |
---|
706 | |
---|
707 | #define ORDERKEY_PCTCPU \ |
---|
708 | if (dresult = percent_cpu(p2) - percent_cpu(p1),\ |
---|
709 | (result = dresult > 0.0 ? 1 : dresult < 0.0 ? -1 : 0) == 0) |
---|
710 | #define ORDERKEY_CPTICKS \ |
---|
711 | if ((result = p2->pr_time.tv_sec - p1->pr_time.tv_sec) == 0) |
---|
712 | #define ORDERKEY_STATE if ((result = proc_state(p2) - proc_state(p1)) == 0) |
---|
713 | #define ORDERKEY_PRIO if ((result = p2->pr_oldpri - p1->pr_oldpri) == 0) |
---|
714 | #define ORDERKEY_RSSIZE if ((result = p2->pr_rssize - p1->pr_rssize) == 0) |
---|
715 | #define ORDERKEY_MEM if ((result = (p2->pr_size - p1->pr_size)) == 0) |
---|
716 | #define ORDERKEY_CMD if ((result = strcmp(p1->pr_fname,p2->pr_fname)) == 0) |
---|
717 | |
---|
718 | int compare_cpu(struct prpsinfo **pp1, struct prpsinfo **pp2) |
---|
719 | { |
---|
720 | struct prpsinfo *p1, *p2; |
---|
721 | int result; |
---|
722 | double dresult; |
---|
723 | |
---|
724 | /* remove one level of indirection */ |
---|
725 | p1 = *pp1; |
---|
726 | p2 = *pp2; |
---|
727 | /* |
---|
728 | * order by various keys, resorting to the next one |
---|
729 | * whenever there's a tie in comparisons |
---|
730 | */ |
---|
731 | ORDERKEY_PCTCPU |
---|
732 | ORDERKEY_CPTICKS |
---|
733 | ORDERKEY_STATE |
---|
734 | ORDERKEY_PRIO |
---|
735 | ORDERKEY_RSSIZE |
---|
736 | ORDERKEY_MEM |
---|
737 | ; |
---|
738 | return (result); |
---|
739 | } |
---|
740 | |
---|
741 | int compare_size(struct prpsinfo **pp1, struct prpsinfo **pp2) |
---|
742 | { |
---|
743 | struct prpsinfo *p1, *p2; |
---|
744 | int result; |
---|
745 | double dresult; |
---|
746 | |
---|
747 | /* remove one level of indirection */ |
---|
748 | p1 = *pp1; |
---|
749 | p2 = *pp2; |
---|
750 | /* |
---|
751 | * order by various keys, resorting to the next one |
---|
752 | * whenever there's a tie in comparisons |
---|
753 | */ |
---|
754 | ORDERKEY_MEM |
---|
755 | ORDERKEY_RSSIZE |
---|
756 | ORDERKEY_PCTCPU |
---|
757 | ORDERKEY_CPTICKS |
---|
758 | ORDERKEY_STATE |
---|
759 | ORDERKEY_PRIO |
---|
760 | ; |
---|
761 | return (result); |
---|
762 | } |
---|
763 | |
---|
764 | int compare_res(struct prpsinfo **pp1, struct prpsinfo **pp2) |
---|
765 | { |
---|
766 | struct prpsinfo *p1, *p2; |
---|
767 | int result; |
---|
768 | double dresult; |
---|
769 | |
---|
770 | /* remove one level of indirection */ |
---|
771 | p1 = *pp1; |
---|
772 | p2 = *pp2; |
---|
773 | /* |
---|
774 | * order by various keys, resorting to the next one |
---|
775 | * whenever there's a tie in comparisons |
---|
776 | */ |
---|
777 | ORDERKEY_RSSIZE |
---|
778 | ORDERKEY_MEM |
---|
779 | ORDERKEY_PCTCPU |
---|
780 | ORDERKEY_CPTICKS |
---|
781 | ORDERKEY_STATE |
---|
782 | ORDERKEY_PRIO |
---|
783 | ; |
---|
784 | return (result); |
---|
785 | } |
---|
786 | |
---|
787 | int compare_time(struct prpsinfo **pp1, struct prpsinfo **pp2) |
---|
788 | { |
---|
789 | struct prpsinfo *p1, *p2; |
---|
790 | int result; |
---|
791 | double dresult; |
---|
792 | |
---|
793 | /* remove one level of indirection */ |
---|
794 | p1 = *pp1; |
---|
795 | p2 = *pp2; |
---|
796 | /* |
---|
797 | * order by various keys, resorting to the next one |
---|
798 | * whenever there's a tie in comparisons |
---|
799 | */ |
---|
800 | ORDERKEY_CPTICKS |
---|
801 | ORDERKEY_RSSIZE |
---|
802 | ORDERKEY_MEM |
---|
803 | ORDERKEY_PCTCPU |
---|
804 | ORDERKEY_STATE |
---|
805 | ORDERKEY_PRIO |
---|
806 | ; |
---|
807 | return (result); |
---|
808 | } |
---|
809 | |
---|
810 | int compare_cmd(struct prpsinfo **pp1, struct prpsinfo **pp2) |
---|
811 | { |
---|
812 | struct prpsinfo *p1, *p2; |
---|
813 | int result; |
---|
814 | double dresult; |
---|
815 | |
---|
816 | /* remove one level of indirection */ |
---|
817 | p1 = *pp1; |
---|
818 | p2 = *pp2; |
---|
819 | /* |
---|
820 | * order by various keys, resorting to the next one |
---|
821 | * whenever there's a tie in comparisons |
---|
822 | */ |
---|
823 | ORDERKEY_CMD |
---|
824 | ORDERKEY_PCTCPU |
---|
825 | ORDERKEY_CPTICKS |
---|
826 | ORDERKEY_RSSIZE |
---|
827 | ; |
---|
828 | return (result); |
---|
829 | } |
---|
830 | |
---|
831 | int compare_state(struct prpsinfo **pp1, struct prpsinfo **pp2) |
---|
832 | { |
---|
833 | struct prpsinfo *p1, *p2; |
---|
834 | int result; |
---|
835 | double dresult; |
---|
836 | |
---|
837 | /* remove one level of indirection */ |
---|
838 | p1 = *pp1; |
---|
839 | p2 = *pp2; |
---|
840 | /* |
---|
841 | * order by various keys, resorting to the next one |
---|
842 | * whenever there's a tie in comparisons |
---|
843 | */ |
---|
844 | ORDERKEY_STATE |
---|
845 | ORDERKEY_PCTCPU |
---|
846 | ORDERKEY_CPTICKS |
---|
847 | ORDERKEY_RSSIZE |
---|
848 | ; |
---|
849 | return (result); |
---|
850 | } |
---|
851 | |
---|
852 | int compare_prio(struct prpsinfo **pp1, struct prpsinfo **pp2) |
---|
853 | { |
---|
854 | struct prpsinfo *p1, *p2; |
---|
855 | int result; |
---|
856 | double dresult; |
---|
857 | |
---|
858 | /* remove one level of indirection */ |
---|
859 | p1 = *pp1; |
---|
860 | p2 = *pp2; |
---|
861 | /* |
---|
862 | * order by various keys, resorting to the next one |
---|
863 | * whenever there's a tie in comparisons |
---|
864 | */ |
---|
865 | ORDERKEY_PRIO |
---|
866 | ORDERKEY_PCTCPU |
---|
867 | ; |
---|
868 | return (result); |
---|
869 | } |
---|
870 | |
---|
871 | |
---|
872 | |
---|
873 | /* return the owner of the specified process. */ |
---|
874 | uid_t |
---|
875 | proc_owner(pid) |
---|
876 | pid_t pid; |
---|
877 | { |
---|
878 | register struct prpsinfo *p; |
---|
879 | int i; |
---|
880 | |
---|
881 | for (i = 0, p = pbase; i < nproc; i++, p++) |
---|
882 | if (p->pr_pid == pid) |
---|
883 | return (p->pr_uid); |
---|
884 | |
---|
885 | return (-1); |
---|
886 | } |
---|
887 | |
---|
888 | #ifdef DO_MAPSIZE |
---|
889 | static void |
---|
890 | size(int fd, struct prpsinfo *ps) |
---|
891 | { |
---|
892 | prmap_sgi_arg_t maparg; |
---|
893 | struct prmap_sgi maps[256]; |
---|
894 | int nmaps; |
---|
895 | double sz; |
---|
896 | int i; |
---|
897 | |
---|
898 | maparg.pr_vaddr = (caddr_t) maps; |
---|
899 | maparg.pr_size = sizeof maps; |
---|
900 | if ((nmaps = ioctl(fd, PIOCMAP_SGI, &maparg)) == -1) { |
---|
901 | /* XXX - this will be confusing */ |
---|
902 | return; |
---|
903 | } |
---|
904 | for (i = 0, sz = 0; i < nmaps; ++i) { |
---|
905 | sz += (double) maps[i].pr_wsize / MA_WSIZE_FRAC; |
---|
906 | } |
---|
907 | ps->pr_rssize = (long) sz; |
---|
908 | } |
---|
909 | #endif |
---|
910 | |
---|
911 | /* get process table */ |
---|
912 | void |
---|
913 | getptable(struct prpsinfo *baseptr) |
---|
914 | { |
---|
915 | struct prpsinfo *currproc; /* ptr to current proc struct */ |
---|
916 | int i, numprocs; |
---|
917 | struct dirent *direntp; |
---|
918 | struct oldproc *op, *endbase; |
---|
919 | static struct timeval lasttime, thistime; |
---|
920 | static double timediff, alpha, beta; |
---|
921 | |
---|
922 | /* measure time between last call to getptable and current call */ |
---|
923 | gettimeofday (&thistime, NULL); |
---|
924 | |
---|
925 | /* |
---|
926 | * To avoid divides, we keep times in nanoseconds. This is |
---|
927 | * scaled by 1e7 rather than 1e9 so that when we divide we |
---|
928 | * get percent. |
---|
929 | */ |
---|
930 | timediff = ((double) thistime.tv_sec * 1.0e7 - |
---|
931 | (double) lasttime.tv_sec * 1.0e7) |
---|
932 | + |
---|
933 | ((double) thistime.tv_usec * 10 - |
---|
934 | (double) lasttime.tv_usec * 10); |
---|
935 | |
---|
936 | /* |
---|
937 | * Under extreme load conditions, sca has experienced |
---|
938 | * an assert(timediff > 0) failure here. His guess is that |
---|
939 | * sometimes timed resets the time backwards and gettimeofday |
---|
940 | * returns a lower number on a later call. |
---|
941 | * To be on the safe side I fix it here by setting timediff |
---|
942 | * to some arbitrary small value (in nanoseconds). |
---|
943 | */ |
---|
944 | if (timediff <= 0.0) timediff = 100.0; |
---|
945 | |
---|
946 | lasttime = thistime; /* prepare for next round */ |
---|
947 | |
---|
948 | /* |
---|
949 | * constants for exponential decaying average. |
---|
950 | * avg = alpha * new + beta * avg |
---|
951 | * The goal is 50% decay in 30 sec. However if the sample period |
---|
952 | * is greater than 30 sec, there's not a lot we can do. |
---|
953 | */ |
---|
954 | if (timediff < 30.0e7) { |
---|
955 | alpha = 0.5 * (timediff / 15.0e7); |
---|
956 | beta = 1.0 - alpha; |
---|
957 | } else { |
---|
958 | alpha = 0.5; |
---|
959 | beta = 0.5; |
---|
960 | } |
---|
961 | assert(alpha >= 0); assert(alpha <= 1); |
---|
962 | assert(beta >= 0); assert(beta <= 1); |
---|
963 | |
---|
964 | endbase = oldbase + oldprocs; |
---|
965 | currproc = baseptr; |
---|
966 | |
---|
967 | for (numprocs = 0, rewinddir(procdir); direntp = readdir(procdir);) { |
---|
968 | int fd; |
---|
969 | |
---|
970 | if ((fd = open(direntp->d_name, O_RDONLY)) < 0) |
---|
971 | continue; |
---|
972 | |
---|
973 | currproc = baseptr + numprocs; |
---|
974 | |
---|
975 | if (ioctl(fd, PIOCPSINFO, currproc) < 0) { |
---|
976 | (void) close(fd); |
---|
977 | continue; |
---|
978 | } |
---|
979 | |
---|
980 | /* |
---|
981 | * SVR4 doesn't keep track of CPU% in the kernel, |
---|
982 | * so we have to do our own. |
---|
983 | * See if we've heard of this process before. |
---|
984 | * If so, compute % based on CPU since last time. |
---|
985 | */ |
---|
986 | op = oldbase + HASH (currproc->pr_pid); |
---|
987 | for (;;) { |
---|
988 | if (op->oldpid == -1) /* not there */ |
---|
989 | break; |
---|
990 | if (op->oldpid == currproc->pr_pid) { |
---|
991 | /* found old data */ |
---|
992 | percent_cpu(currproc) = |
---|
993 | ((currproc->pr_time.tv_sec * 1.0e9 + |
---|
994 | currproc->pr_time.tv_nsec) |
---|
995 | - op->oldtime) / timediff; |
---|
996 | |
---|
997 | weighted_cpu(currproc) = |
---|
998 | op->oldpct * beta + |
---|
999 | percent_cpu(currproc) * alpha; |
---|
1000 | |
---|
1001 | break; |
---|
1002 | } |
---|
1003 | op++; /* try next entry in hash table */ |
---|
1004 | if (op == endbase) /* table wrap around */ |
---|
1005 | op = oldbase; |
---|
1006 | } |
---|
1007 | |
---|
1008 | /* Otherwise, it's new, so use all of its CPU time */ |
---|
1009 | if (op->oldpid == -1) { |
---|
1010 | if (lasttime.tv_sec) { |
---|
1011 | percent_cpu(currproc) = |
---|
1012 | (currproc->pr_time.tv_sec * 1.0e9 + |
---|
1013 | currproc->pr_time.tv_nsec) / timediff; |
---|
1014 | |
---|
1015 | weighted_cpu(currproc) = percent_cpu(currproc); |
---|
1016 | } else { |
---|
1017 | /* first screen -- no difference is possible */ |
---|
1018 | percent_cpu(currproc) = 0.0; |
---|
1019 | weighted_cpu(currproc) = 0.0; |
---|
1020 | } |
---|
1021 | } |
---|
1022 | |
---|
1023 | #ifdef DO_MAPSIZE |
---|
1024 | size(fd, currproc); |
---|
1025 | #endif |
---|
1026 | numprocs++; |
---|
1027 | (void) close(fd); |
---|
1028 | |
---|
1029 | /* |
---|
1030 | * Bug: in case process count grew so dramatically |
---|
1031 | * as to exceed to table size. We give up on a full scan. |
---|
1032 | * the chances of this to happen are extremely slim due to |
---|
1033 | * the big factor we're using. getting nproc from nlist |
---|
1034 | * is not worth the headache. realloc wouldn't work either |
---|
1035 | * because we have pointers to the proc table so we cannot |
---|
1036 | * move it around. |
---|
1037 | */ |
---|
1038 | if (numprocs >= ptable_size) { |
---|
1039 | fprintf(stderr, |
---|
1040 | "preallocated proc table size (%d) exceeded, " |
---|
1041 | "skipping some processes\n", ptable_size); |
---|
1042 | break; |
---|
1043 | } |
---|
1044 | } |
---|
1045 | nproc = numprocs; |
---|
1046 | |
---|
1047 | /* |
---|
1048 | * Save current CPU time for next time around |
---|
1049 | * For the moment recreate the hash table each time, as the code |
---|
1050 | * is easier that way. |
---|
1051 | */ |
---|
1052 | oldprocs = 2 * nproc; |
---|
1053 | endbase = oldbase + oldprocs; |
---|
1054 | |
---|
1055 | for (op = oldbase; op < endbase; op++) |
---|
1056 | op->oldpid = -1; |
---|
1057 | |
---|
1058 | for (i = 0, currproc = baseptr; i < nproc; i++, currproc++) { |
---|
1059 | |
---|
1060 | /* find an empty spot */ |
---|
1061 | op = oldbase + HASH (currproc->pr_pid); |
---|
1062 | for (;;) { |
---|
1063 | if (op->oldpid == -1) |
---|
1064 | break; |
---|
1065 | op++; |
---|
1066 | if (op == endbase) |
---|
1067 | op = oldbase; |
---|
1068 | } |
---|
1069 | op->oldpid = currproc->pr_pid; |
---|
1070 | op->oldtime = (currproc->pr_time.tv_sec * 1.0e9 + |
---|
1071 | currproc->pr_time.tv_nsec); |
---|
1072 | op->oldpct = weighted_cpu(currproc); |
---|
1073 | } |
---|
1074 | } |
---|
1075 | |
---|