2 * Copyright (c) 1984 through 2008, William LeFebvre
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
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
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: Linux 1.2.x, 1.3.x, 2.x, using the /proc filesystem
39 * This is the machine-dependent module for Linux 1.2.x, 1.3.x or 2.x.
43 * CFLAGS: -DHAVE_GETOPT -DHAVE_STRERROR -DORDER
47 * AUTHOR: Richard Henderson <rth@tamu.edu>
48 * Order support added by Alexey Klimkin <kad@klon.tme.mcst.ru>
49 * Ported to 2.4 by William LeFebvre
50 * Additions for 2.6 by William LeFebvre
55 #include <sys/types.h>
70 #include <sys/param.h> /* for HZ */
71 #include <asm/page.h> /* for PAGE_SHIFT */
74 #include <linux/proc_fs.h> /* for PROC_SUPER_MAGIC */
76 #define PROC_SUPER_MAGIC 0x9fa0
85 #define PROCFS "/proc"
88 /*=PROCESS INFORMATION==================================================*/
95 int pri
, nice
, threads
;
96 unsigned long size
, rss
, shared
; /* in k */
99 unsigned long start_time
;
101 struct top_proc
*next
;
105 /*=STATE IDENT STRINGS==================================================*/
107 #define NPROCSTATES 7
108 static char *state_abbrev
[NPROCSTATES
+1] =
110 "", "run", "sleep", "disk", "zomb", "stop", "swap",
114 static char *procstatenames
[NPROCSTATES
+1] =
116 "", " running, ", " sleeping, ", " uninterruptable, ",
117 " zombie, ", " stopped, ", " swapping, ",
122 static char *cpustatenames
[NCPUSTATES
+1] =
124 "user", "nice", "system", "idle", "iowait",
127 static int show_iowait
= 0;
132 #define KERNELNEWPROC 3
133 #define NKERNELSTATS 4
134 static char *kernelnames
[NKERNELSTATS
+1] =
136 " ctxsw, ", " flt, ", " intr, ", " newproc",
146 static char *memorynames
[NMEMSTATS
+1] =
148 "K used, ", "K free, ", "K shared, ", "K buffers, ", "K cached",
156 static char *swapnames
[NSWAPSTATS
+1] =
158 "K used, ", "K free, ", "K cached",
162 static char fmt_header
[] =
163 " PID X THR PRI NICE SIZE RES STATE TIME CPU COMMAND";
165 static char proc_header_thr
[] =
166 " PID %-9s THR PRI NICE SIZE RES SHR STATE TIME CPU COMMAND";
168 static char proc_header_nothr
[] =
169 " PID %-9s PRI NICE SIZE RES SHR STATE TIME CPU COMMAND";
171 /* these are names given to allowed sorting orders -- first is default */
173 {"cpu", "size", "res", "time", "command", NULL
};
175 /* forward definitions for comparison functions */
182 int (*proc_compares
[])() = {
190 /*=SYSTEM STATE INFO====================================================*/
192 /* these are for calculating cpu state percentages */
194 static long cp_time
[NCPUSTATES
];
195 static long cp_old
[NCPUSTATES
];
196 static long cp_diff
[NCPUSTATES
];
198 /* for calculating the exponential average */
200 static struct timeval lasttime
= { 0, 0 };
201 static struct timeval timediff
= { 0, 0 };
202 static long elapsed_msecs
;
204 /* these are for keeping track of processes and tasks */
206 #define HASH_SIZE (1003)
207 #define INITIAL_ACTIVE_SIZE (256)
208 #define PROCBLOCK_SIZE (32)
209 static hash_table
*ptable
;
210 static hash_table
*tasktable
;
211 static struct top_proc
**pactive
;
212 static struct top_proc
**nextactive
;
213 static unsigned int activesize
= 0;
214 static time_t boottime
= -1;
215 static int have_task
= 0;
217 /* these are counters that need to be track */
218 static unsigned long last_ctxt
= 0;
219 static unsigned long last_intr
= 0;
220 static unsigned long last_newproc
= 0;
221 static unsigned long last_flt
= 0;
223 /* these are for passing data back to the machine independant portion */
225 static int cpu_states
[NCPUSTATES
];
226 static int process_states
[NPROCSTATES
];
227 static int kernel_stats
[NKERNELSTATS
];
228 static long memory_stats
[NMEMSTATS
];
229 static long swap_stats
[NSWAPSTATS
];
232 #define bytetok(x) (((x) + 512) >> 10)
233 #define pagetok(x) ((x) << (PAGE_SHIFT - 10))
234 #define HASH(x) (((x) * 1686629713U) % HASH_SIZE)
236 /* calculate a per-second rate using milliseconds */
237 #define per_second(n, msec) (((n) * 1000) / (msec))
239 /*======================================================================*/
242 skip_ws(const char *p
)
244 while (isspace(*p
)) p
++;
249 skip_token(const char *p
)
251 while (isspace(*p
)) p
++;
252 while (*p
&& !isspace(*p
)) p
++;
257 xfrm_cmdline(char *p
, int len
)
270 update_procname(struct top_proc
*proc
, char *cmd
)
275 if (proc
->name
== NULL
)
277 proc
->name
= strdup(cmd
);
279 else if (strcmp(proc
->name
, cmd
) != 0)
282 proc
->name
= strdup(cmd
);
287 * Process structures are allocated and freed as needed. Here we
288 * keep big pools of them, adding more pool as needed. When a
289 * top_proc structure is freed, it is added to a freelist and reused.
292 static struct top_proc
*freelist
= NULL
;
293 static struct top_proc
*procblock
= NULL
;
294 static struct top_proc
*procmax
= NULL
;
296 static struct top_proc
*
304 freelist
= freelist
->next
;
309 if (++procblock
>= procmax
)
316 p
= procblock
= (struct top_proc
*)calloc(PROCBLOCK_SIZE
,
317 sizeof(struct top_proc
));
318 procmax
= procblock
++ + PROCBLOCK_SIZE
;
332 free_proc(struct top_proc
*proc
)
334 proc
->next
= freelist
;
340 machine_init(struct statics
*statics
)
343 /* make sure the proc filesystem is mounted */
346 if (statfs(PROCFS
, &sb
) < 0 || sb
.f_type
!= PROC_SUPER_MAGIC
)
348 fprintf(stderr
, "%s: proc filesystem not mounted on " PROCFS
"\n",
354 /* chdir to the proc filesystem to make things easier */
357 /* a few preliminary checks */
363 unsigned long uptime
;
368 if ((fd
= open("uptime", 0)) != -1)
370 if (read(fd
, buff
, sizeof(buff
)) > 0)
372 uptime
= strtoul(buff
, &p
, 10);
373 gettimeofday(&tv
, 0);
374 boottime
= tv
.tv_sec
- uptime
;
379 /* see how many states we get from stat */
380 if ((fd
= open("stat", 0)) != -1)
382 if (read(fd
, buff
, sizeof(buff
)) > 0)
384 if ((p
= strchr(buff
, '\n')) != NULL
)
406 /* see if we have task subdirs */
407 if (stat("self/task", &st
) != -1 && S_ISDIR(st
.st_mode
))
409 dprintf("we have task directories\n");
414 /* if we aren't showing iowait, then we have to tweak cpustatenames */
417 cpustatenames
[4] = NULL
;
420 /* fill in the statics information */
421 statics
->procstate_names
= procstatenames
;
422 statics
->cpustate_names
= cpustatenames
;
423 statics
->kernel_names
= kernelnames
;
424 statics
->memory_names
= memorynames
;
425 statics
->swap_names
= swapnames
;
426 statics
->order_names
= ordernames
;
427 statics
->boottime
= boottime
;
428 statics
->flags
.fullcmds
= 1;
429 statics
->flags
.warmup
= 1;
430 statics
->flags
.threads
= 1;
432 /* allocate needed space */
433 pactive
= (struct top_proc
**)malloc(sizeof(struct top_proc
*) * INITIAL_ACTIVE_SIZE
);
434 activesize
= INITIAL_ACTIVE_SIZE
;
436 /* create process and task hashes */
437 ptable
= hash_create(HASH_SIZE
);
438 tasktable
= hash_create(HASH_SIZE
);
446 get_system_info(struct system_info
*info
)
452 struct timeval thistime
;
453 unsigned long intr
= 0;
454 unsigned long ctxt
= 0;
455 unsigned long newproc
= 0;
456 unsigned long flt
= 0;
458 /* timestamp and time difference */
459 gettimeofday(&thistime
, 0);
460 timersub(&thistime
, &lasttime
, &timediff
);
461 elapsed_msecs
= timediff
.tv_sec
* 1000 + timediff
.tv_usec
/ 1000;
464 /* get load averages */
465 if ((fd
= open("loadavg", O_RDONLY
)) != -1)
467 if ((len
= read(fd
, buffer
, sizeof(buffer
)-1)) > 0)
470 info
->load_avg
[0] = strtod(buffer
, &p
);
471 info
->load_avg
[1] = strtod(p
, &p
);
472 info
->load_avg
[2] = strtod(p
, &p
);
473 p
= skip_token(p
); /* skip running/tasks */
477 info
->last_pid
= atoi(p
);
487 /* get the cpu time info */
488 if ((fd
= open("stat", O_RDONLY
)) != -1)
490 if ((len
= read(fd
, buffer
, sizeof(buffer
)-1)) > 0)
493 p
= skip_token(buffer
); /* "cpu" */
494 cp_time
[0] = strtoul(p
, &p
, 0);
495 cp_time
[1] = strtoul(p
, &p
, 0);
496 cp_time
[2] = strtoul(p
, &p
, 0);
497 cp_time
[3] = strtoul(p
, &p
, 0);
500 cp_time
[4] = strtoul(p
, &p
, 0);
503 /* convert cp_time counts to percentages */
504 percentages(NCPUSTATES
, cpu_states
, cp_time
, cp_old
, cp_diff
);
506 /* get the rest of it */
511 if (strncmp(p
, "intr ", 5) == 0)
514 intr
= strtoul(p
, &p
, 10);
516 else if (strncmp(p
, "ctxt ", 5) == 0)
519 ctxt
= strtoul(p
, &p
, 10);
521 else if (strncmp(p
, "processes ", 10) == 0)
524 newproc
= strtoul(p
, &p
, 10);
530 kernel_stats
[KERNELINTR
] = per_second(intr
- last_intr
, elapsed_msecs
);
531 kernel_stats
[KERNELCTXT
] = per_second(ctxt
- last_ctxt
, elapsed_msecs
);
532 kernel_stats
[KERNELNEWPROC
] = per_second(newproc
- last_newproc
, elapsed_msecs
);
535 last_newproc
= newproc
;
540 /* get system wide memory usage */
541 if ((fd
= open("meminfo", O_RDONLY
)) != -1)
546 unsigned long memtotal
= 0;
547 unsigned long memfree
= 0;
548 unsigned long swaptotal
= 0;
550 if ((len
= read(fd
, buffer
, sizeof(buffer
)-1)) > 0)
555 /* iterate thru the lines */
559 if (p
[0] == ' ' || p
[0] == '\t')
563 else if (strncmp(p
, "Mem:", 4) == 0)
565 p
= skip_token(p
); /* "Mem:" */
566 p
= skip_token(p
); /* total memory */
567 memory_stats
[MEMUSED
] = strtoul(p
, &p
, 10);
568 memory_stats
[MEMFREE
] = strtoul(p
, &p
, 10);
569 memory_stats
[MEMSHARED
] = strtoul(p
, &p
, 10);
570 memory_stats
[MEMBUFFERS
] = strtoul(p
, &p
, 10);
571 memory_stats
[MEMCACHED
] = strtoul(p
, &p
, 10);
572 memory_stats
[MEMUSED
] = bytetok(memory_stats
[MEMUSED
]);
573 memory_stats
[MEMFREE
] = bytetok(memory_stats
[MEMFREE
]);
574 memory_stats
[MEMSHARED
] = bytetok(memory_stats
[MEMSHARED
]);
575 memory_stats
[MEMBUFFERS
] = bytetok(memory_stats
[MEMBUFFERS
]);
576 memory_stats
[MEMCACHED
] = bytetok(memory_stats
[MEMCACHED
]);
579 else if (strncmp(p
, "Swap:", 5) == 0)
581 p
= skip_token(p
); /* "Swap:" */
582 p
= skip_token(p
); /* total swap */
583 swap_stats
[SWAPUSED
] = strtoul(p
, &p
, 10);
584 swap_stats
[SWAPFREE
] = strtoul(p
, &p
, 10);
585 swap_stats
[SWAPUSED
] = bytetok(swap_stats
[SWAPUSED
]);
586 swap_stats
[SWAPFREE
] = bytetok(swap_stats
[SWAPFREE
]);
589 else if (!mem
&& strncmp(p
, "MemTotal:", 9) == 0)
592 memtotal
= strtoul(p
, &p
, 10);
594 else if (!mem
&& memtotal
> 0 && strncmp(p
, "MemFree:", 8) == 0)
597 memfree
= strtoul(p
, &p
, 10);
598 memory_stats
[MEMUSED
] = memtotal
- memfree
;
599 memory_stats
[MEMFREE
] = memfree
;
601 else if (!mem
&& strncmp(p
, "MemShared:", 10) == 0)
604 memory_stats
[MEMSHARED
] = strtoul(p
, &p
, 10);
606 else if (!mem
&& strncmp(p
, "Buffers:", 8) == 0)
609 memory_stats
[MEMBUFFERS
] = strtoul(p
, &p
, 10);
611 else if (!mem
&& strncmp(p
, "Cached:", 7) == 0)
614 memory_stats
[MEMCACHED
] = strtoul(p
, &p
, 10);
616 else if (!swap
&& strncmp(p
, "SwapTotal:", 10) == 0)
619 swaptotal
= strtoul(p
, &p
, 10);
621 else if (!swap
&& swaptotal
> 0 && strncmp(p
, "SwapFree:", 9) == 0)
624 memfree
= strtoul(p
, &p
, 10);
625 swap_stats
[SWAPUSED
] = swaptotal
- memfree
;
626 swap_stats
[SWAPFREE
] = memfree
;
628 else if (!mem
&& strncmp(p
, "SwapCached:", 11) == 0)
631 swap_stats
[SWAPCACHED
] = strtoul(p
, &p
, 10);
634 /* move to the next line */
641 /* get vm related stuff */
642 if ((fd
= open("vmstat", O_RDONLY
)) != -1)
646 if ((len
= read(fd
, buffer
, sizeof(buffer
)-1)) > 0)
651 /* iterate thru the lines */
654 if (strncmp(p
, "pgmajfault ", 11) == 0)
657 flt
= strtoul(p
, &p
, 10);
658 kernel_stats
[KERNELFLT
] = per_second(flt
- last_flt
, elapsed_msecs
);
663 /* move to the next line */
671 /* set arrays and strings */
672 info
->cpustates
= cpu_states
;
673 info
->memory
= memory_stats
;
674 info
->swap
= swap_stats
;
675 info
->kernel
= kernel_stats
;
679 read_one_proc_stat(pid_t pid
, pid_t taskpid
, struct top_proc
*proc
, struct process_select
*sel
)
681 char buffer
[4096], *p
, *q
;
685 dprintf("reading proc %d - %d\n", pid
, taskpid
);
687 /* if anything goes wrong, we return with proc->state == 0 */
690 /* full cmd handling */
691 fullcmd
= sel
->fullcmd
;
696 sprintf(buffer
, "%d/cmdline", pid
);
700 sprintf(buffer
, "%d/task/%d/cmdline", pid
, taskpid
);
702 if ((fd
= open(buffer
, O_RDONLY
)) != -1)
704 /* read command line data */
705 /* (theres no sense in reading more than we can fit) */
706 if ((len
= read(fd
, buffer
, MAX_COLS
)) > 1)
709 xfrm_cmdline(buffer
, len
);
710 update_procname(proc
, buffer
);
724 /* grab the shared memory size */
725 sprintf(buffer
, "%d/statm", pid
);
726 fd
= open(buffer
, O_RDONLY
);
727 len
= read(fd
, buffer
, sizeof(buffer
)-1);
731 p
= skip_token(p
); /* skip size */
732 p
= skip_token(p
); /* skip resident */
733 proc
->shared
= pagetok(strtoul(p
, &p
, 10));
735 /* grab the proc stat info in one go */
738 sprintf(buffer
, "%d/stat", pid
);
742 sprintf(buffer
, "%d/task/%d/stat", pid
, taskpid
);
745 fd
= open(buffer
, O_RDONLY
);
746 len
= read(fd
, buffer
, sizeof(buffer
)-1);
751 proc
->uid
= (uid_t
)proc_owner((int)pid
);
753 /* parse out the status */
755 /* skip pid and locate command, which is in parentheses */
756 if ((p
= strchr(buffer
, '(')) == NULL
)
760 if ((q
= strrchr(++p
, ')')) == NULL
)
765 /* set the procname */
769 update_procname(proc
, p
);
772 /* scan the rest of the line */
775 switch (*p
++) /* state */
777 case 'R': proc
->state
= 1; break;
778 case 'S': proc
->state
= 2; break;
779 case 'D': proc
->state
= 3; break;
780 case 'Z': proc
->state
= 4; break;
781 case 'T': proc
->state
= 5; break;
782 case 'W': proc
->state
= 6; break;
786 p
= skip_token(p
); /* skip ppid */
787 p
= skip_token(p
); /* skip pgrp */
788 p
= skip_token(p
); /* skip session */
789 p
= skip_token(p
); /* skip tty */
790 p
= skip_token(p
); /* skip tty pgrp */
791 p
= skip_token(p
); /* skip flags */
792 p
= skip_token(p
); /* skip min flt */
793 p
= skip_token(p
); /* skip cmin flt */
794 p
= skip_token(p
); /* skip maj flt */
795 p
= skip_token(p
); /* skip cmaj flt */
797 proc
->time
= strtoul(p
, &p
, 10); /* utime */
798 proc
->time
+= strtoul(p
, &p
, 10); /* stime */
800 p
= skip_token(p
); /* skip cutime */
801 p
= skip_token(p
); /* skip cstime */
803 proc
->pri
= strtol(p
, &p
, 10); /* priority */
804 proc
->nice
= strtol(p
, &p
, 10); /* nice */
805 proc
->threads
= strtol(p
, &p
, 10); /* threads */
807 p
= skip_token(p
); /* skip it_real_val */
808 proc
->start_time
= strtoul(p
, &p
, 10); /* start_time */
810 proc
->size
= bytetok(strtoul(p
, &p
, 10)); /* vsize */
811 proc
->rss
= pagetok(strtoul(p
, &p
, 10)); /* rss */
814 /* for the record, here are the rest of the fields */
815 p
= skip_token(p
); /* skip rlim */
816 p
= skip_token(p
); /* skip start_code */
817 p
= skip_token(p
); /* skip end_code */
818 p
= skip_token(p
); /* skip start_stack */
819 p
= skip_token(p
); /* skip sp */
820 p
= skip_token(p
); /* skip pc */
821 p
= skip_token(p
); /* skip signal */
822 p
= skip_token(p
); /* skip sigblocked */
823 p
= skip_token(p
); /* skip sigignore */
824 p
= skip_token(p
); /* skip sigcatch */
825 p
= skip_token(p
); /* skip wchan */
830 static int show_usernames
;
831 static int show_threads
;
835 get_process_info(struct system_info
*si
,
836 struct process_select
*sel
,
839 struct top_proc
*proc
;
840 struct top_proc
*taskproc
;
844 unsigned long elapsed
;
848 /* round current time to a second */
849 now
= (unsigned long)lasttime
.tv_sec
;
850 if (lasttime
.tv_usec
>= 500000)
855 /* calculate number of ticks since our last check */
856 elapsed
= timediff
.tv_sec
* HZ
+ (timediff
.tv_usec
* HZ
) / 1000000;
861 dprintf("get_process_info: elapsed %d ticks\n", elapsed
);
863 /* mark all hash table entries as not seen */
864 hi
= hash_first_pid(ptable
, &pos
);
867 ((struct top_proc
*)(hi
->value
))->state
= 0;
868 hi
= hash_next_pid(&pos
);
870 /* mark all hash table entries as not seen */
871 hi
= hash_first_pid(tasktable
, &pos
);
874 ((struct top_proc
*)(hi
->value
))->state
= 0;
875 hi
= hash_next_pid(&pos
);
878 /* read the process information */
880 DIR *dir
= opendir(".");
883 struct dirent
*taskent
;
885 struct top_proc
**active
;
890 int show_idle
= sel
->idle
;
891 int show_uid
= sel
->uid
!= -1;
892 char *show_command
= sel
->command
;
894 show_usernames
= sel
->usernames
;
895 show_threads
= sel
->threads
&& have_task
;
897 memset(process_states
, 0, sizeof(process_states
));
903 while ((ent
= readdir(dir
)) != NULL
)
907 if (!isdigit(ent
->d_name
[0]))
910 pid
= atoi(ent
->d_name
);
912 /* look up hash table entry */
913 proc
= hash_lookup_pid(ptable
, pid
);
915 /* if we came up empty, create a new entry */
921 hash_add_pid(ptable
, pid
, (void *)proc
);
924 /* remember the previous cpu time */
927 /* get current data */
928 read_one_proc_stat(pid
, -1, proc
, sel
);
930 /* continue on if this isn't really a process */
931 if (proc
->state
== 0)
934 /* reset linked list (for threads) */
937 /* accumulate process state data */
939 process_states
[proc
->state
]++;
942 if ((proc
->pcpu
= (proc
->time
- otime
) / (double)elapsed
) < 0.0001)
947 /* if we have task subdirs and this process has more than
948 one thread, collect data on each thread */
949 if (have_task
&& proc
->threads
> 1)
951 snprintf(buffer
, sizeof(buffer
), "%d/task", pid
);
952 if ((taskdir
= opendir(buffer
)) != NULL
)
954 while ((taskent
= readdir(taskdir
)) != NULL
)
956 if (!isdigit(taskent
->d_name
[0]))
959 /* lookup entry in tasktable */
960 taskpid
= atoi(taskent
->d_name
);
961 taskproc
= hash_lookup_pid(tasktable
, taskpid
);
963 /* if we came up empty, create a new entry */
964 if (taskproc
== NULL
)
966 taskproc
= new_proc();
967 taskproc
->pid
= taskpid
;
969 hash_add_pid(tasktable
, taskpid
, (void *)taskproc
);
972 /* remember the previous cpu time */
973 otime
= taskproc
->time
;
975 /* get current data */
976 read_one_proc_stat(pid
, taskpid
, taskproc
, sel
);
978 /* ignore if it isnt real */
979 if (taskproc
->state
== 0)
982 /* when showing threads, add this to the accumulated
983 process state data, but remember that the first
984 thread is already accounted for */
985 if (show_threads
&& pid
!= taskpid
)
988 process_states
[taskproc
->state
]++;
992 if ((taskproc
->pcpu
= (taskproc
->time
- otime
) /
993 (double)elapsed
) < 0.0)
998 /* link this in to the proc's list */
999 taskproc
->next
= proc
->next
;
1000 proc
->next
= taskproc
;
1008 /* make sure we have enough slots for the active procs */
1009 if (activesize
< total_procs
)
1011 pactive
= (struct top_proc
**)realloc(pactive
,
1012 sizeof(struct top_proc
*) * total_procs
);
1013 activesize
= total_procs
;
1016 /* set up the active procs and flush dead entries */
1018 hi
= hash_first_pid(ptable
, &pos
);
1021 proc
= (struct top_proc
*)(hi
->value
);
1022 if (proc
->state
== 0)
1025 hash_remove_pos_pid(&pos
);
1030 /* check to see if it qualifies as active */
1031 if ((show_idle
|| proc
->state
== 1 || proc
->pcpu
) &&
1032 (!show_uid
|| proc
->uid
== sel
->uid
) &&
1033 (show_command
== NULL
||
1034 strstr(proc
->name
, show_command
) != NULL
))
1036 /* are we showing threads and does this proc have any? */
1037 if (show_threads
&& proc
->threads
> 1 && proc
->next
!= NULL
)
1039 /* then add just the thread info -- the main process
1040 info is included in the list */
1042 while (proc
!= NULL
)
1050 /* add the process */
1056 hi
= hash_next_pid(&pos
);
1059 si
->p_active
= active
- pactive
;
1060 si
->p_total
= total_procs
;
1061 si
->procstates
= process_states
;
1064 /* if requested, sort the "active" procs */
1066 qsort(pactive
, si
->p_active
, sizeof(struct top_proc
*),
1067 proc_compares
[compare_index
]);
1069 /* don't even pretend that the return value thing here isn't bogus */
1070 nextactive
= pactive
;
1076 format_header(char *uname_field
)
1079 int uname_len
= strlen(uname_field
);
1083 memcpy(strchr(fmt_header
, 'X'), uname_field
, uname_len
);
1088 static char p_header
[MAX_COLS
];
1091 format_process_header(struct process_select
*sel
, caddr_t handle
, int count
)
1096 h
= sel
->threads
? proc_header_nothr
: proc_header_thr
;
1098 snprintf(p_header
, MAX_COLS
, h
, sel
->usernames
? "USERNAME" : "UID");
1105 format_next_process(caddr_t handle
, char *(*get_userid
)(int))
1108 static char fmt
[MAX_COLS
]; /* static area where result is built */
1109 struct top_proc
*p
= *nextactive
++;
1112 userbuf
= show_usernames
? username(p
->uid
) : itoa_w(p
->uid
, 7);
1116 snprintf(fmt
, sizeof(fmt
),
1117 "%5d %-8.8s %3d %4d %5s %5s %5s %-5s %6s %5s%% %s",
1120 p
->pri
< -99 ? -99 : p
->pri
,
1124 format_k(p
->shared
),
1125 state_abbrev
[p
->state
],
1126 format_time(p
->time
/ HZ
),
1127 format_percent(p
->pcpu
* 100.0),
1132 snprintf(fmt
, sizeof(fmt
),
1133 "%5d %-8.8s %4d %3d %4d %5s %5s %5s %-5s %6s %5s%% %s",
1136 p
->threads
<= 9999 ? p
->threads
: 9999,
1137 p
->pri
< -99 ? -99 : p
->pri
,
1141 format_k(p
->shared
),
1142 state_abbrev
[p
->state
],
1143 format_time(p
->time
/ HZ
),
1144 format_percent(p
->pcpu
* 100.0),
1148 /* return the result */
1152 /* comparison routines for qsort */
1155 * There are currently four possible comparison routines. main selects
1156 * one of these by indexing in to the array proc_compares.
1158 * Possible keys are defined as macros below. Currently these keys are
1159 * defined: percent cpu, cpu ticks, process state, resident set size,
1160 * total virtual memory usage. The process states are ordered as follows
1161 * (from least to most important): WAIT, zombie, sleep, stop, start, run.
1162 * The array declaration below maps a process state index into a number
1163 * that reflects this ordering.
1166 /* First, the possible comparison keys. These are defined in such a way
1167 that they can be merely listed in the source code to define the actual
1171 #define ORDERKEY_PCTCPU if (dresult = p2->pcpu - p1->pcpu,\
1172 (result = dresult > 0.0 ? 1 : dresult < 0.0 ? -1 : 0) == 0)
1173 #define ORDERKEY_CPTICKS if ((result = (long)p2->time - (long)p1->time) == 0)
1174 #define ORDERKEY_STATE if ((result = (sort_state[p2->state] - \
1175 sort_state[p1->state])) == 0)
1176 #define ORDERKEY_PRIO if ((result = p2->pri - p1->pri) == 0)
1177 #define ORDERKEY_RSSIZE if ((result = p2->rss - p1->rss) == 0)
1178 #define ORDERKEY_MEM if ((result = p2->size - p1->size) == 0)
1179 #define ORDERKEY_NAME if ((result = strcmp(p1->name, p2->name)) == 0)
1181 /* Now the array that maps process state to a weight */
1183 unsigned char sort_state
[] =
1195 /* compare_cpu - the comparison function for sorting by cpu percentage */
1199 struct top_proc
**pp1
,
1200 struct top_proc
**pp2
)
1202 register struct top_proc
*p1
;
1203 register struct top_proc
*p2
;
1204 register long result
;
1207 /* remove one level of indirection */
1219 return result
== 0 ? 0 : result
< 0 ? -1 : 1;
1222 /* compare_size - the comparison function for sorting by total memory usage */
1226 struct top_proc
**pp1
,
1227 struct top_proc
**pp2
)
1229 register struct top_proc
*p1
;
1230 register struct top_proc
*p2
;
1231 register long result
;
1234 /* remove one level of indirection */
1246 return result
== 0 ? 0 : result
< 0 ? -1 : 1;
1249 /* compare_res - the comparison function for sorting by resident set size */
1253 struct top_proc
**pp1
,
1254 struct top_proc
**pp2
)
1256 register struct top_proc
*p1
;
1257 register struct top_proc
*p2
;
1258 register long result
;
1261 /* remove one level of indirection */
1273 return result
== 0 ? 0 : result
< 0 ? -1 : 1;
1276 /* compare_time - the comparison function for sorting by total cpu time */
1280 struct top_proc
**pp1
,
1281 struct top_proc
**pp2
)
1283 register struct top_proc
*p1
;
1284 register struct top_proc
*p2
;
1285 register long result
;
1288 /* remove one level of indirection */
1300 return result
== 0 ? 0 : result
< 0 ? -1 : 1;
1304 /* compare_cmd - the comparison function for sorting by command name */
1308 struct top_proc
**pp1
,
1309 struct top_proc
**pp2
)
1311 register struct top_proc
*p1
;
1312 register struct top_proc
*p2
;
1313 register long result
;
1316 /* remove one level of indirection */
1329 return result
== 0 ? 0 : result
< 0 ? -1 : 1;
1334 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
1335 * the process does not exist.
1336 * It is EXTREMLY IMPORTANT that this function work correctly.
1337 * If top runs setuid root (as in SVR4), then this function
1338 * is the only thing that stands in the way of a serious
1339 * security problem. It validates requests for the "kill"
1340 * and "renice" commands.
1349 sprintf(buffer
, "%d", pid
);
1351 if (stat(buffer
, &sb
) < 0)
1354 return (int)sb
.st_uid
;