etc/protocols - sync with NetBSD-8
[minix.git] / external / bsd / top / dist / machine / m_linuxthr.c
blobeb79e5b6858bfd39f385c6bcb44c300acb2bb674
1 /*
2 * Copyright (c) 1984 through 2008, William LeFebvre
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
16 * * Neither the name of William LeFebvre nor the names of other
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 * top - a top users display for Unix
36 * SYNOPSIS: 2.x with thread eliding
38 * DESCRIPTION:
39 * This is the machine-dependent module for Linux 2.x that elides threads
40 * from the output.
42 * CFLAGS: -DHAVE_GETOPT -DHAVE_STRERROR -DORDER
44 * TERMCAP: -lcurses
46 * AUTHOR: Richard Henderson <rth@tamu.edu>
47 * Order support added by Alexey Klimkin <kad@klon.tme.mcst.ru>
48 * Ported to 2.4 by William LeFebvre
49 * Thread eliding by William LeFebvre
52 #include "config.h"
54 #include <sys/types.h>
55 #include <stdio.h>
56 #include <fcntl.h>
57 #include <unistd.h>
58 #include <stdlib.h>
59 #include <errno.h>
60 #include <dirent.h>
61 #include <string.h>
62 #include <math.h>
63 #include <ctype.h>
64 #include <sys/time.h>
65 #include <sys/stat.h>
66 #include <sys/vfs.h>
68 #include <sys/param.h> /* for HZ */
69 #include <asm/page.h> /* for PAGE_SHIFT */
71 #if 0
72 #include <linux/proc_fs.h> /* for PROC_SUPER_MAGIC */
73 #else
74 #define PROC_SUPER_MAGIC 0x9fa0
75 #endif
77 #include "top.h"
78 #include "machine.h"
79 #include "utils.h"
81 #define PROCFS "/proc"
82 extern char *myname;
84 /*=PROCESS INFORMATION==================================================*/
86 struct top_proc
88 pid_t pid;
89 pid_t ppid;
90 uid_t uid;
91 char *name;
92 int pri, nice;
93 unsigned long size, rss; /* in k */
94 int state;
95 unsigned long time;
96 unsigned long start_time;
97 unsigned long otime;
98 unsigned long start_code;
99 unsigned long end_code;
100 unsigned long start_stack;
101 unsigned int threads;
102 double pcpu, wcpu;
103 struct top_proc *next;
107 /*=STATE IDENT STRINGS==================================================*/
109 #define NPROCSTATES 7
110 static char *state_abbrev[NPROCSTATES+1] =
112 "", "run", "sleep", "disk", "zomb", "stop", "swap",
113 NULL
116 static char *procstatenames[NPROCSTATES+1] =
118 "", " running, ", " sleeping, ", " uninterruptable, ",
119 " zombie, ", " stopped, ", " swapping, ",
120 NULL
123 #define NCPUSTATES 4
124 static char *cpustatenames[NCPUSTATES+1] =
126 "user", "nice", "system", "idle",
127 NULL
130 #define MEMUSED 0
131 #define MEMFREE 1
132 #define MEMSHARED 2
133 #define MEMBUFFERS 3
134 #define MEMCACHED 4
135 #define NMEMSTATS 5
136 static char *memorynames[NMEMSTATS+1] =
138 "K used, ", "K free, ", "K shared, ", "K buffers, ", "K cached",
139 NULL
142 #define SWAPUSED 0
143 #define SWAPFREE 1
144 #define SWAPCACHED 2
145 #define NSWAPSTATS 3
146 static char *swapnames[NSWAPSTATS+1] =
148 "K used, ", "K free, ", "K cached",
149 NULL
152 static char fmt_header[] =
153 " PID X THR PRI NICE SIZE RES STATE TIME CPU COMMAND";
155 /* these are names given to allowed sorting orders -- first is default */
156 char *ordernames[] =
157 {"cpu", "size", "res", "time", "command", NULL};
159 /* forward definitions for comparison functions */
160 int compare_cpu();
161 int compare_size();
162 int compare_res();
163 int compare_time();
164 int compare_cmd();
166 int (*proc_compares[])() = {
167 compare_cpu,
168 compare_size,
169 compare_res,
170 compare_time,
171 compare_cmd,
172 NULL };
174 /*=SYSTEM STATE INFO====================================================*/
176 /* these are for calculating cpu state percentages */
178 static long cp_time[NCPUSTATES];
179 static long cp_old[NCPUSTATES];
180 static long cp_diff[NCPUSTATES];
182 /* for calculating the exponential average */
184 static struct timeval lasttime;
186 /* these are for keeping track of processes */
188 #define HASH_SIZE (1003)
189 #define INITIAL_ACTIVE_SIZE (256)
190 #define PROCBLOCK_SIZE (32)
191 static struct top_proc *ptable[HASH_SIZE];
192 static struct top_proc **pactive;
193 static struct top_proc **nextactive;
194 static unsigned int activesize = 0;
195 static time_t boottime = -1;
197 /* these are for passing data back to the machine independant portion */
199 static int cpu_states[NCPUSTATES];
200 static int process_states[NPROCSTATES];
201 static long memory_stats[NMEMSTATS];
202 static long swap_stats[NSWAPSTATS];
204 /* usefull macros */
205 #define bytetok(x) (((x) + 512) >> 10)
206 #define pagetok(x) ((x) << (PAGE_SHIFT - 10))
207 #define HASH(x) (((x) * 1686629713U) % HASH_SIZE)
209 /*======================================================================*/
211 static inline char *
212 skip_ws(const char *p)
214 while (isspace(*p)) p++;
215 return (char *)p;
218 static inline char *
219 skip_token(const char *p)
221 while (isspace(*p)) p++;
222 while (*p && !isspace(*p)) p++;
223 return (char *)p;
226 static void
227 xfrm_cmdline(char *p, int len)
229 while (--len > 0)
231 if (*p == '\0')
233 *p = ' ';
235 p++;
239 static void
240 update_procname(struct top_proc *proc, char *cmd)
243 printable(cmd);
245 if (proc->name == NULL)
247 proc->name = strdup(cmd);
249 else if (strcmp(proc->name, cmd) != 0)
251 free(proc->name);
252 proc->name = strdup(cmd);
260 * Process structures are allocated and freed as needed. Here we
261 * keep big pools of them, adding more pool as needed. When a
262 * top_proc structure is freed, it is added to a freelist and reused.
265 static struct top_proc *freelist = NULL;
266 static struct top_proc *procblock = NULL;
267 static struct top_proc *procmax = NULL;
269 static struct top_proc *
270 new_proc()
272 struct top_proc *p;
274 if (freelist)
276 p = freelist;
277 freelist = freelist->next;
279 else if (procblock)
281 p = procblock;
282 if (++procblock >= procmax)
284 procblock = NULL;
287 else
289 p = procblock = (struct top_proc *)calloc(PROCBLOCK_SIZE,
290 sizeof(struct top_proc));
291 procmax = procblock++ + PROCBLOCK_SIZE;
294 /* initialization */
295 if (p->name != NULL)
297 free(p->name);
298 p->name = NULL;
301 return p;
304 static void
305 free_proc(struct top_proc *proc)
307 proc->next = freelist;
308 freelist = proc;
313 machine_init(struct statics *statics)
316 /* make sure the proc filesystem is mounted */
318 struct statfs sb;
319 if (statfs(PROCFS, &sb) < 0 || sb.f_type != PROC_SUPER_MAGIC)
321 fprintf(stderr, "%s: proc filesystem not mounted on " PROCFS "\n",
322 myname);
323 return -1;
327 /* chdir to the proc filesystem to make things easier */
328 chdir(PROCFS);
330 /* get a boottime */
332 int fd;
333 char buff[64];
334 char *p;
335 unsigned long uptime;
336 struct timeval tv;
338 if ((fd = open("uptime", 0)) != -1)
340 if (read(fd, buff, sizeof(buff)) > 0)
342 uptime = strtoul(buff, &p, 10);
343 gettimeofday(&tv, 0);
344 boottime = tv.tv_sec - uptime;
346 close(fd);
350 /* fill in the statics information */
351 statics->procstate_names = procstatenames;
352 statics->cpustate_names = cpustatenames;
353 statics->memory_names = memorynames;
354 statics->swap_names = swapnames;
355 statics->order_names = ordernames;
356 statics->boottime = boottime;
357 statics->flags.fullcmds = 1;
358 statics->flags.warmup = 1;
360 /* allocate needed space */
361 pactive = (struct top_proc **)malloc(sizeof(struct top_proc *) * INITIAL_ACTIVE_SIZE);
362 activesize = INITIAL_ACTIVE_SIZE;
364 /* make sure the hash table is empty */
365 memset(ptable, 0, HASH_SIZE * sizeof(struct top_proc *));
367 /* all done! */
368 return 0;
372 void
373 get_system_info(struct system_info *info)
376 char buffer[4096+1];
377 int fd, len;
378 char *p;
380 /* get load averages */
382 if ((fd = open("loadavg", O_RDONLY)) != -1)
384 if ((len = read(fd, buffer, sizeof(buffer)-1)) > 0)
386 buffer[len] = '\0';
388 info->load_avg[0] = strtod(buffer, &p);
389 info->load_avg[1] = strtod(p, &p);
390 info->load_avg[2] = strtod(p, &p);
391 p = skip_token(p); /* skip running/tasks */
392 p = skip_ws(p);
393 if (*p)
395 info->last_pid = atoi(p);
397 else
399 info->last_pid = -1;
402 close(fd);
405 /* get the cpu time info */
406 if ((fd = open("stat", O_RDONLY)) != -1)
408 if ((len = read(fd, buffer, sizeof(buffer)-1)) > 0)
410 buffer[len] = '\0';
411 p = skip_token(buffer); /* "cpu" */
412 cp_time[0] = strtoul(p, &p, 0);
413 cp_time[1] = strtoul(p, &p, 0);
414 cp_time[2] = strtoul(p, &p, 0);
415 cp_time[3] = strtoul(p, &p, 0);
417 /* convert cp_time counts to percentages */
418 percentages(4, cpu_states, cp_time, cp_old, cp_diff);
420 close(fd);
423 /* get system wide memory usage */
424 if ((fd = open("meminfo", O_RDONLY)) != -1)
426 char *p;
427 int mem = 0;
428 int swap = 0;
429 unsigned long memtotal = 0;
430 unsigned long memfree = 0;
431 unsigned long swaptotal = 0;
433 if ((len = read(fd, buffer, sizeof(buffer)-1)) > 0)
435 buffer[len] = '\0';
436 p = buffer-1;
438 /* iterate thru the lines */
439 while (p != NULL)
441 p++;
442 if (p[0] == ' ' || p[0] == '\t')
444 /* skip */
446 else if (strncmp(p, "Mem:", 4) == 0)
448 p = skip_token(p); /* "Mem:" */
449 p = skip_token(p); /* total memory */
450 memory_stats[MEMUSED] = strtoul(p, &p, 10);
451 memory_stats[MEMFREE] = strtoul(p, &p, 10);
452 memory_stats[MEMSHARED] = strtoul(p, &p, 10);
453 memory_stats[MEMBUFFERS] = strtoul(p, &p, 10);
454 memory_stats[MEMCACHED] = strtoul(p, &p, 10);
455 memory_stats[MEMUSED] = bytetok(memory_stats[MEMUSED]);
456 memory_stats[MEMFREE] = bytetok(memory_stats[MEMFREE]);
457 memory_stats[MEMSHARED] = bytetok(memory_stats[MEMSHARED]);
458 memory_stats[MEMBUFFERS] = bytetok(memory_stats[MEMBUFFERS]);
459 memory_stats[MEMCACHED] = bytetok(memory_stats[MEMCACHED]);
460 mem = 1;
462 else if (strncmp(p, "Swap:", 5) == 0)
464 p = skip_token(p); /* "Swap:" */
465 p = skip_token(p); /* total swap */
466 swap_stats[SWAPUSED] = strtoul(p, &p, 10);
467 swap_stats[SWAPFREE] = strtoul(p, &p, 10);
468 swap_stats[SWAPUSED] = bytetok(swap_stats[SWAPUSED]);
469 swap_stats[SWAPFREE] = bytetok(swap_stats[SWAPFREE]);
470 swap = 1;
472 else if (!mem && strncmp(p, "MemTotal:", 9) == 0)
474 p = skip_token(p);
475 memtotal = strtoul(p, &p, 10);
477 else if (!mem && memtotal > 0 && strncmp(p, "MemFree:", 8) == 0)
479 p = skip_token(p);
480 memfree = strtoul(p, &p, 10);
481 memory_stats[MEMUSED] = memtotal - memfree;
482 memory_stats[MEMFREE] = memfree;
484 else if (!mem && strncmp(p, "MemShared:", 10) == 0)
486 p = skip_token(p);
487 memory_stats[MEMSHARED] = strtoul(p, &p, 10);
489 else if (!mem && strncmp(p, "Buffers:", 8) == 0)
491 p = skip_token(p);
492 memory_stats[MEMBUFFERS] = strtoul(p, &p, 10);
494 else if (!mem && strncmp(p, "Cached:", 7) == 0)
496 p = skip_token(p);
497 memory_stats[MEMCACHED] = strtoul(p, &p, 10);
499 else if (!swap && strncmp(p, "SwapTotal:", 10) == 0)
501 p = skip_token(p);
502 swaptotal = strtoul(p, &p, 10);
504 else if (!swap && swaptotal > 0 && strncmp(p, "SwapFree:", 9) == 0)
506 p = skip_token(p);
507 memfree = strtoul(p, &p, 10);
508 swap_stats[SWAPUSED] = swaptotal - memfree;
509 swap_stats[SWAPFREE] = memfree;
511 else if (!mem && strncmp(p, "SwapCached:", 11) == 0)
513 p = skip_token(p);
514 swap_stats[SWAPCACHED] = strtoul(p, &p, 10);
517 /* move to the next line */
518 p = strchr(p, '\n');
521 close(fd);
524 /* set arrays and strings */
525 info->cpustates = cpu_states;
526 info->memory = memory_stats;
527 info->swap = swap_stats;
531 static void
532 read_one_proc_stat(pid_t pid, struct top_proc *proc, struct process_select *sel)
534 char buffer[4096], *p, *q;
535 int fd, len;
536 int fullcmd;
538 /* if anything goes wrong, we return with proc->state == 0 */
539 proc->state = 0;
541 /* full cmd handling */
542 fullcmd = sel->fullcmd;
543 if (fullcmd)
545 sprintf(buffer, "%d/cmdline", pid);
546 if ((fd = open(buffer, O_RDONLY)) != -1)
548 /* read command line data */
549 /* (theres no sense in reading more than we can fit) */
550 if ((len = read(fd, buffer, MAX_COLS)) > 1)
552 buffer[len] = '\0';
553 xfrm_cmdline(buffer, len);
554 update_procname(proc, buffer);
556 else
558 fullcmd = 0;
560 close(fd);
562 else
564 fullcmd = 0;
568 /* grab the proc stat info in one go */
569 sprintf(buffer, "%d/stat", pid);
571 fd = open(buffer, O_RDONLY);
572 len = read(fd, buffer, sizeof(buffer)-1);
573 close(fd);
575 buffer[len] = '\0';
577 proc->uid = (uid_t)proc_owner((int)pid);
579 /* parse out the status */
581 /* skip pid and locate command, which is in parentheses */
582 if ((p = strchr(buffer, '(')) == NULL)
584 return;
586 if ((q = strrchr(++p, ')')) == NULL)
588 return;
591 /* set the procname */
592 *q = '\0';
593 if (!fullcmd)
595 update_procname(proc, p);
598 /* scan the rest of the line */
599 p = q+1;
600 p = skip_ws(p);
601 switch (*p++) /* state */
603 case 'R': proc->state = 1; break;
604 case 'S': proc->state = 2; break;
605 case 'D': proc->state = 3; break;
606 case 'Z': proc->state = 4; break;
607 case 'T': proc->state = 5; break;
608 case 'W': proc->state = 6; break;
609 case '\0': return;
612 proc->ppid = strtoul(p, &p, 10); /* ppid */
613 p = skip_token(p); /* skip pgrp */
614 p = skip_token(p); /* skip session */
615 p = skip_token(p); /* skip tty */
616 p = skip_token(p); /* skip tty pgrp */
617 p = skip_token(p); /* skip flags */
618 p = skip_token(p); /* skip min flt */
619 p = skip_token(p); /* skip cmin flt */
620 p = skip_token(p); /* skip maj flt */
621 p = skip_token(p); /* skip cmaj flt */
623 proc->time = strtoul(p, &p, 10); /* utime */
624 proc->time += strtoul(p, &p, 10); /* stime */
626 p = skip_token(p); /* skip cutime */
627 p = skip_token(p); /* skip cstime */
629 proc->pri = strtol(p, &p, 10); /* priority */
630 proc->nice = strtol(p, &p, 10); /* nice */
632 p = skip_token(p); /* skip timeout */
633 p = skip_token(p); /* skip it_real_val */
634 proc->start_time = strtoul(p, &p, 10); /* start_time */
636 proc->size = bytetok(strtoul(p, &p, 10)); /* vsize */
637 proc->rss = pagetok(strtoul(p, &p, 10)); /* rss */
639 p = skip_token(p); /* skip rlim */
640 proc->start_code = strtoul(p, &p, 10); /* start_code */
641 proc->end_code = strtoul(p, &p, 10); /* end_code */
642 proc->start_stack = strtoul(p, &p, 10); /* start_stack */
644 /* for the record, here are the rest of the fields */
645 #if 0
646 p = skip_token(p); /* skip sp */
647 p = skip_token(p); /* skip pc */
648 p = skip_token(p); /* skip signal */
649 p = skip_token(p); /* skip sigblocked */
650 p = skip_token(p); /* skip sigignore */
651 p = skip_token(p); /* skip sigcatch */
652 p = skip_token(p); /* skip wchan */
653 #endif
657 caddr_t
658 get_process_info(struct system_info *si,
659 struct process_select *sel,
660 int compare_index)
662 struct timeval thistime;
663 double timediff, alpha, beta;
664 struct top_proc *proc;
665 pid_t pid;
666 unsigned long now;
667 unsigned long elapsed;
668 int i;
670 /* calculate the time difference since our last check */
671 gettimeofday(&thistime, 0);
672 if (lasttime.tv_sec)
674 timediff = ((thistime.tv_sec - lasttime.tv_sec) +
675 (thistime.tv_usec - lasttime.tv_usec) * 1e-6);
677 else
679 timediff = 0;
681 lasttime = thistime;
683 /* round current time to a second */
684 now = (unsigned long)thistime.tv_sec;
685 if (thistime.tv_usec >= 500000)
687 now++;
690 /* calculate constants for the exponental average */
691 if (timediff > 0.0 && timediff < 30.0)
693 alpha = 0.5 * (timediff / 30.0);
694 beta = 1.0 - alpha;
696 else
697 alpha = beta = 0.5;
698 timediff *= HZ; /* convert to ticks */
700 /* mark all hash table entries as not seen */
701 for (i = 0; i < HASH_SIZE; ++i)
703 for (proc = ptable[i]; proc; proc = proc->next)
705 proc->state = 0;
709 /* read the process information */
711 DIR *dir = opendir(".");
712 struct dirent *ent;
713 int total_procs = 0;
714 struct top_proc **active;
716 int show_idle = sel->idle;
717 int show_uid = sel->uid != -1;
719 memset(process_states, 0, sizeof(process_states));
721 while ((ent = readdir(dir)) != NULL)
723 struct top_proc *pp;
725 if (!isdigit(ent->d_name[0]))
726 continue;
728 pid = atoi(ent->d_name);
730 /* look up hash table entry */
731 proc = pp = ptable[HASH(pid)];
732 while (proc && proc->pid != pid)
734 proc = proc->next;
737 /* if we came up empty, create a new entry */
738 if (proc == NULL)
740 proc = new_proc();
741 proc->pid = pid;
742 proc->next = pp;
743 ptable[HASH(pid)] = proc;
744 proc->time = proc->otime = 0;
747 read_one_proc_stat(pid, proc, sel);
748 proc->threads = 1;
750 if (proc->state == 0)
751 continue;
753 total_procs++;
754 process_states[proc->state]++;
756 /* calculate cpu percentage */
757 if (timediff > 0.0)
759 if ((proc->pcpu = (proc->time - proc->otime) / timediff) < 0.0001)
761 proc->pcpu = 0;
764 else if ((elapsed = (now - boottime)*HZ - proc->start_time) > 0)
766 if ((proc->pcpu = (double)proc->time / (double)elapsed) < 0.0001)
768 proc->pcpu;
771 else
773 proc->pcpu = 0.0;
776 /* remember time for next time */
777 proc->otime = proc->time;
779 closedir(dir);
781 /* make sure we have enough slots for the active procs */
782 if (activesize < total_procs)
784 pactive = (struct top_proc **)realloc(pactive,
785 sizeof(struct top_proc *) * total_procs);
786 activesize = total_procs;
789 /* set up the active procs and flush dead entries */
790 /* also coalesce threads */
791 active = pactive;
792 for (i = 0; i < HASH_SIZE; i++)
794 struct top_proc *last;
795 struct top_proc *ptmp;
796 struct top_proc *parent;
798 last = NULL;
799 proc = ptable[i];
800 while (proc != NULL)
802 if (proc->state == 0)
804 ptmp = proc;
805 if (last)
807 proc = last->next = proc->next;
809 else
811 proc = ptable[i] = proc->next;
813 free_proc(ptmp);
815 else
817 /* look up hash table entry for parent */
818 parent = proc;
819 do {
820 pid = parent->ppid;
821 parent = ptable[HASH(pid)];
822 while (parent && parent->pid != pid)
824 parent = parent->next;
826 } while (parent && parent->state == 0);
828 /* does this look like a thread of its parent? */
829 if (parent && proc->size == parent->size &&
830 proc->rss == parent->rss &&
831 proc->start_code == parent->start_code &&
832 proc->end_code == parent->end_code &&
833 proc->start_stack == parent->start_stack)
835 /* yes it does: roll up the cumulative numbers */
836 parent->threads += proc->threads;
837 parent->time += proc->time;
838 parent->pcpu += proc->pcpu;
840 /* mark this process as dead (undisplayable) */
841 proc->state = 0;
843 else if ((show_idle || proc->state == 1 || proc->pcpu) &&
844 (!show_uid || proc->uid == sel->uid))
846 *active++ = proc;
847 last = proc;
849 proc = proc->next;
854 si->p_active = active - pactive;
855 si->p_total = total_procs;
856 si->procstates = process_states;
859 /* if requested, sort the "active" procs */
860 if (si->p_active)
861 qsort(pactive, si->p_active, sizeof(struct top_proc *),
862 proc_compares[compare_index]);
864 /* don't even pretend that the return value thing here isn't bogus */
865 nextactive = pactive;
866 return (caddr_t)0;
870 char *
871 format_header(char *uname_field)
874 int uname_len = strlen(uname_field);
875 if (uname_len > 8)
876 uname_len = 8;
878 memcpy(strchr(fmt_header, 'X'), uname_field, uname_len);
880 return fmt_header;
884 char *
885 format_next_process(caddr_t handle, char *(*get_userid)(int))
888 static char fmt[MAX_COLS]; /* static area where result is built */
889 struct top_proc *p = *nextactive++;
891 snprintf(fmt, sizeof(fmt),
892 "%5d %-8.8s %3d %3d %4d %5s %5s %-5s %6s %5.2f%% %s",
893 p->pid,
894 (*get_userid)(p->uid),
895 p->threads,
896 p->pri < -99 ? -99 : p->pri,
897 p->nice,
898 format_k(p->size),
899 format_k(p->rss),
900 state_abbrev[p->state],
901 format_time(p->time / HZ),
902 p->pcpu * 100.0,
903 p->name);
905 /* return the result */
906 return (fmt);
909 /* comparison routines for qsort */
912 * There are currently four possible comparison routines. main selects
913 * one of these by indexing in to the array proc_compares.
915 * Possible keys are defined as macros below. Currently these keys are
916 * defined: percent cpu, cpu ticks, process state, resident set size,
917 * total virtual memory usage. The process states are ordered as follows
918 * (from least to most important): WAIT, zombie, sleep, stop, start, run.
919 * The array declaration below maps a process state index into a number
920 * that reflects this ordering.
923 /* First, the possible comparison keys. These are defined in such a way
924 that they can be merely listed in the source code to define the actual
925 desired ordering.
928 #define ORDERKEY_PCTCPU if (dresult = p2->pcpu - p1->pcpu,\
929 (result = dresult > 0.0 ? 1 : dresult < 0.0 ? -1 : 0) == 0)
930 #define ORDERKEY_CPTICKS if ((result = (long)p2->time - (long)p1->time) == 0)
931 #define ORDERKEY_STATE if ((result = (sort_state[p2->state] - \
932 sort_state[p1->state])) == 0)
933 #define ORDERKEY_PRIO if ((result = p2->pri - p1->pri) == 0)
934 #define ORDERKEY_RSSIZE if ((result = p2->rss - p1->rss) == 0)
935 #define ORDERKEY_MEM if ((result = p2->size - p1->size) == 0)
936 #define ORDERKEY_NAME if ((result = strcmp(p1->name, p2->name)) == 0)
938 /* Now the array that maps process state to a weight */
940 unsigned char sort_state[] =
942 0, /* empty */
943 6, /* run */
944 3, /* sleep */
945 5, /* disk wait */
946 1, /* zombie */
947 2, /* stop */
948 4 /* swap */
952 /* compare_cpu - the comparison function for sorting by cpu percentage */
955 compare_cpu (
956 struct top_proc **pp1,
957 struct top_proc **pp2)
959 register struct top_proc *p1;
960 register struct top_proc *p2;
961 register long result;
962 double dresult;
964 /* remove one level of indirection */
965 p1 = *pp1;
966 p2 = *pp2;
968 ORDERKEY_PCTCPU
969 ORDERKEY_CPTICKS
970 ORDERKEY_STATE
971 ORDERKEY_PRIO
972 ORDERKEY_RSSIZE
973 ORDERKEY_MEM
976 return result == 0 ? 0 : result < 0 ? -1 : 1;
979 /* compare_size - the comparison function for sorting by total memory usage */
982 compare_size (
983 struct top_proc **pp1,
984 struct top_proc **pp2)
986 register struct top_proc *p1;
987 register struct top_proc *p2;
988 register long result;
989 double dresult;
991 /* remove one level of indirection */
992 p1 = *pp1;
993 p2 = *pp2;
995 ORDERKEY_MEM
996 ORDERKEY_RSSIZE
997 ORDERKEY_PCTCPU
998 ORDERKEY_CPTICKS
999 ORDERKEY_STATE
1000 ORDERKEY_PRIO
1003 return result == 0 ? 0 : result < 0 ? -1 : 1;
1006 /* compare_res - the comparison function for sorting by resident set size */
1009 compare_res (
1010 struct top_proc **pp1,
1011 struct top_proc **pp2)
1013 register struct top_proc *p1;
1014 register struct top_proc *p2;
1015 register long result;
1016 double dresult;
1018 /* remove one level of indirection */
1019 p1 = *pp1;
1020 p2 = *pp2;
1022 ORDERKEY_RSSIZE
1023 ORDERKEY_MEM
1024 ORDERKEY_PCTCPU
1025 ORDERKEY_CPTICKS
1026 ORDERKEY_STATE
1027 ORDERKEY_PRIO
1030 return result == 0 ? 0 : result < 0 ? -1 : 1;
1033 /* compare_time - the comparison function for sorting by total cpu time */
1036 compare_time (
1037 struct top_proc **pp1,
1038 struct top_proc **pp2)
1040 register struct top_proc *p1;
1041 register struct top_proc *p2;
1042 register long result;
1043 double dresult;
1045 /* remove one level of indirection */
1046 p1 = *pp1;
1047 p2 = *pp2;
1049 ORDERKEY_CPTICKS
1050 ORDERKEY_PCTCPU
1051 ORDERKEY_STATE
1052 ORDERKEY_PRIO
1053 ORDERKEY_MEM
1054 ORDERKEY_RSSIZE
1057 return result == 0 ? 0 : result < 0 ? -1 : 1;
1060 /* compare_cmd - the comparison function for sorting by command name */
1063 compare_cmd (
1064 struct top_proc **pp1,
1065 struct top_proc **pp2)
1067 register struct top_proc *p1;
1068 register struct top_proc *p2;
1069 register long result;
1070 double dresult;
1072 /* remove one level of indirection */
1073 p1 = *pp1;
1074 p2 = *pp2;
1076 ORDERKEY_NAME
1077 ORDERKEY_PCTCPU
1078 ORDERKEY_CPTICKS
1079 ORDERKEY_STATE
1080 ORDERKEY_PRIO
1081 ORDERKEY_RSSIZE
1082 ORDERKEY_MEM
1085 return result == 0 ? 0 : result < 0 ? -1 : 1;
1090 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
1091 * the process does not exist.
1092 * It is EXTREMLY IMPORTANT that this function work correctly.
1093 * If top runs setuid root (as in SVR4), then this function
1094 * is the only thing that stands in the way of a serious
1095 * security problem. It validates requests for the "kill"
1096 * and "renice" commands.
1100 proc_owner(int pid)
1103 struct stat sb;
1104 char buffer[32];
1105 sprintf(buffer, "%d", pid);
1107 if (stat(buffer, &sb) < 0)
1108 return -1;
1109 else
1110 return (int)sb.st_uid;