xref: /minix/external/bsd/top/dist/machine/m_macosx.c (revision e1cdaee1)
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.
10  *
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.
15  *
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.
19  *
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.
31  */
32 
33 /*
34  * m_macosx.c
35  *
36  * AUTHOR:	Andrew S. Townley
37  *		based on m_bsd44.c and m_next32.c
38  *		by Christos Zoulas and Tim Pugh
39  * CREATED:	Tue Aug 11 01:51:35 CDT 1998
40  * SYNOPSIS:  MacOS X Server (Rhapsody Developer Release 2)
41  * DESCRIPTION:
42  *	MacOS X Server (Rhapsody Developer Release 2)
43  *
44  * CFLAGS: -DHAVE_STRERROR
45  * TERMCAP: none
46  * MATH: none
47  */
48 
49 /*
50  * normal stuff
51  */
52 
53 #include "config.h"
54 
55 #include <stdio.h>
56 #include <stdarg.h>
57 #include <errno.h>
58 #include "os.h"
59 #include "top.h"
60 #include "machine.h"
61 #include "utils.h"
62 
63 /*
64  * MacOS kernel stuff
65  */
66 
67 #include <kvm.h>
68 #include <fcntl.h>
69 #include <sys/dkstat.h>
70 #include <sys/sysctl.h>
71 #include <mach/message.h>
72 #include <mach/vm_statistics.h>
73 #include <mach/mach.h>
74 #include <mach/host_info.h>
75 
76 #define VMUNIX		"/mach_kernel"
77 #define MEM		"/dev/mem"
78 #define SWAP		NULL
79 
80 #define NUM_AVERAGES	3
81 #define LOG1024		10
82 
83 #define PP(pp, field)	((pp)->kp_proc . field)
84 #define EP(pp, field)	((pp)->kp_eproc . field)
85 #define VP(pp, field)	((pp)->kp_eproc.e_vm . field)
86 #define MPP(mp, field)	(PP((mp)->kproc, field))
87 #define MEP(mp, field)	(EP((mp)->kproc, field))
88 #define MVP(mp, field)	(VP((mp)->kproc, field))
89 #define TP(mp, field)	((mp)->task_info . field)
90 #define RP(mp, field)	((mp)->thread_summary . field)
91 
92 /* define what weighted cpu is */
93 #define weighted_cpu(pct, s) (s == 0 ? 0.0 : \
94                          ((pct) / (1.0 - exp(s * logcpu))))
95 
96 /* what we consider to be process size: */
97 #ifdef notdef
98 #define PROCSIZE(pp) (VP((pp), vm_tsize) + VP((pp), vm_dsize) + VP((pp), vm_ssize))
99 #endif
100 #define PROCSIZE(pp) (EP(pp, e_xsize))
101 #define TASKSIZE(t) (TP(t, virtual_size) + TP(t, resident_size))
102 
103 /* what we consider to be resident set size: */
104 #ifdef notdef
105 #define RSSIZE(pp) (MVP((pp), vm_rssize))
106 #endif
107 #define RSSIZE(pp) (MEP((pp), e_xrssize))
108 
109 #define pctdouble(p) ((double)(p) / FSCALE)
110 
111 /*
112  * globals
113  */
114 
115 static kvm_t		*kd = NULL;
116 static int		nproc;
117 static int		onproc = -1;
118 static int		pref_len;
119 static int		maxmem;
120 static char		fmt[MAX_COLS];
121 static double		logcpu = 1.0;
122 
123 /* process array stuff */
124 
125 static struct kinfo_proc	*kproc_list = NULL;
126 static struct macos_proc	*proc_list = NULL;
127 static struct macos_proc	**proc_ref = NULL;
128 static int			process_states[7];
129 static struct handle		handle;
130 
131 /*
132  * The mach information hopefully will not be necessary
133  * when the kvm_* interfaces are supported completely.
134  *
135  * Since we're only concerned with task and thread info
136  * for 'interesting' processes, we're going to only allocate
137  * as many task and thread structures as needed.
138  */
139 
140 static struct task_basic_info	*task_list = NULL;
141 
142 /* memory statistics */
143 
144 static int 		pageshift 	= 0;
145 static int		pagesize	= 0;
146 #define pagetok(size)	((size) << pageshift)
147 
148 static int		swappgsin	= -1;
149 static int		swappgsout	= -1;
150 static vm_statistics_data_t	vm_stats;
151 static long		memory_stats[7];
152 
153 /* CPU state percentages */
154 
155 host_cpu_load_info_data_t cpuload;
156 
157 static long	cp_time[CPU_STATE_MAX];
158 static long	cp_old[CPU_STATE_MAX];
159 static long	cp_diff[CPU_STATE_MAX];
160 static int		cpu_states[CPU_STATE_MAX];
161 
162 /*
163  * types
164  */
165 
166 typedef long 		pctcpu;
167 
168 //struct statics
169 //{
170 //	char	**procstate_names;
171 //	char	**cpustate_names;
172 //	char	**memory_names;
173 //	char	**order_names;
174 //};
175 //
176 //struct system_info
177 //{
178 //	int	last_pid;
179 //	double	load_avg[NUM_AVERAGES];
180 //	int	p_total;	/* total # of processes */
181 //	int	p_active;	/* number processes considered active */
182 //	int	*procstates;
183 //	int	*cpustates;
184 //	int	*memory;
185 //};
186 //
187 //struct process_select
188 //{
189 //	int	idle;		/* show idle processes */
190 //	int	system;		/* show system processes */
191 //	int	uid;		/* show only this uid (unless -1) */
192 //	char	*command;	/* only this command (unless NULL) */
193 //};
194 
195 /*
196  * We need to declare a hybrid structure which will store all
197  * of the stuff we care about for each process.
198  */
199 
200 struct macos_proc
201 {
202 	struct kinfo_proc		*kproc;
203 	task_t				the_task;
204 	struct task_basic_info		task_info;
205 	unsigned int			thread_count;
206 	struct thread_basic_info	thread_summary;
207 };
208 
209 struct handle
210 {
211 	struct macos_proc		**next_proc;
212 	int				remaining;
213 };
214 
215 static char header[] =
216   "  PID X        PRI THRD  SIZE   RES STATE   TIME    MEM    CPU COMMAND";
217 /* 0123456   -- field to fill in starts at header+6 */
218 #define UNAME_START 6
219 
220 #define Proc_format \
221         "%5d %-8.8s %3d %4d %5s %5s %-5s %6s %5.2f%% %5.2f%% %.16s"
222 
223 
224 int proc_compare(const void *, const void *);
225 
226 
227 /*
228  * puke()
229  *
230  * This function is used to report errors to stderr.
231  */
232 
233 static void puke(const char* fmt, ...)
234 {
235 	va_list	args;
236 	va_start(args, fmt);
237 	vfprintf(stderr, fmt, args);
238 	va_end(args);
239 
240 	fputc('\n', stderr);
241 	fflush(stderr);
242 }
243 
244 /*
245  * kread()
246  *
247  * This function is a wrapper for the kvm_read() function
248  * with the addition of a message parameter per kvm_open().
249  *
250  * All other behavior is per kvm_read except the error reporting.
251  */
252 
253 static ssize_t kread(u_long addr, void *buf,
254 	size_t nbytes, const char *errstr)
255 {
256 	ssize_t	s = 0;
257 
258 	s = kvm_read(kd, addr, buf, nbytes);
259 	if(s == -1)
260 		{
261 		puke("error:  kvm_read() failed for '%s' (%s)\n",
262 			errstr, strerror(errno));
263 		}
264 
265 	return s;
266 }
267 
268 /*
269  * prototypes for functions which top needs
270  */
271 
272 char *printable();
273 
274 /*
275  * definitions for offsets
276  */
277 
278 #define X_NPROC		0
279 #define X_HZ		1
280 #define X_MAXMEM	2
281 
282 #define NLIST_LAST	3
283 
284 static struct nlist	nlst[] =
285 {
286 	{ "_maxproc" },		/* 0 *** maximum processes */
287 	{ "_hz" },		/* 1 */
288 	{ "_mem_size" },	/* 2 */
289 	{ 0 }
290 };
291 
292 static char *procstates[] =
293 {
294 	"",
295 	" starting, ",
296 	" running, ",
297 	" sleeping, ",
298 	" stopped, ",
299 	" zombie, ",
300 	" swapped ",
301 	NULL
302 };
303 
304 static char *cpustates[] =
305 {
306 	"user",
307 	"system",
308 	"idle",
309 	"nice",
310 	NULL
311 };
312 
313 static char *state_abbrev[] =
314 {
315 	"",
316 	"start",
317 	"run\0\0\0",
318 	"sleep",
319 	"stop",
320 	"zomb"
321 };
322 
323 static char *mach_state[] =
324 {
325 	"",
326 	"R",
327 	"T",
328 	"S",
329 	"U",
330 	"H"
331 };
332 
333 static char *thread_state[] =
334 {
335 	"",
336 	"run\0\0\0",
337 	"stop",
338 	"wait",
339 	"uwait",
340 	"halted",
341 };
342 
343 static char *flags_state[] =
344 {
345 	"",
346 	"W",
347 	"I"
348 };
349 
350 static char *memnames[] =
351 {
352 	"K Tot, ",
353 	"K Free, ",
354 	"K Act, ",
355 	"K Inact, ",
356 	"K Wired, ",
357 	"K in, ",
358 	"K out ",
359 	NULL
360 };
361 
362 /*
363  * format_header()
364  *
365  * This function is used to add the username into the
366  * header information.
367  */
368 
369 char *format_header(register char *uname_field)
370 {
371 	register char *ptr;
372 
373 	ptr = header + UNAME_START;
374 	while(*uname_field != '\0')
375 		*ptr++ = *uname_field++;
376 
377 	return(header);
378 }
379 
380 /*
381  * format_next_process()
382  *
383  * This function actuall is responsible for the formatting of
384  * each row which is displayed.
385  */
386 
387 char *format_next_process(caddr_t handle, char *(*getuserid)())
388 {
389 	register struct macos_proc	*pp;
390 	register long			cputime;
391 	register double			pct;
392 	register int			vsize;
393 	register int			rsize;
394 	struct handle			*hp;
395 
396 	/*
397 	 * we need to keep track of the next proc structure.
398 	 */
399 
400 	hp = (struct handle*)handle;
401 	pp = *(hp->next_proc++);
402 	hp->remaining--;
403 
404 	/*
405 	 * get the process structure and take care of the cputime
406 	 */
407 
408 	if((MPP(pp, p_flag) & P_INMEM) == 0)
409 		{
410 		/* we want to print swapped processes as <pname> */
411 		char	*comm = MPP(pp, p_comm);
412 #define COMSIZ	sizeof(MPP(pp, p_comm))
413 		char	buf[COMSIZ];
414 		strncpy(buf, comm, COMSIZ);
415 		comm[0] = '<';
416 		strncpy(&comm[1], buf, COMSIZ - 2);
417 		comm[COMSIZ - 2] = '\0';
418 		strncat(comm, ">", COMSIZ - 1);
419 		comm[COMSIZ - 1] = '\0';
420 		}
421 
422 	/*
423 	 * count the cpu time, but ignore the interrupts
424 	 *
425 	 * At the present time (DR2 8/1998), MacOS X doesn't
426 	 * correctly report this information through the
427 	 * kinfo_proc structure.  We need to get it from the
428 	 * task threads.
429 	 *
430 	 * cputime = PP(pp, p_rtime).tv_sec;
431 	 */
432 
433 	cputime = RP(pp, user_time).seconds + RP(pp, system_time).seconds;
434 
435 	/*
436 	 * calculate the base cpu percentages
437 	 *
438 	 * Again, at the present time, MacOS X doesn't report
439 	 * this information through the kinfo_proc.  We need
440 	 * to talk to the threads.
441 	 */
442 
443 //	pct = pctdouble(PP(pp, p_pctcpu));
444 	pct = (double)(RP(pp, cpu_usage))/TH_USAGE_SCALE;
445 
446 	/*
447 	 * format the entry
448 	 */
449 
450 	/*
451 	 * In the final version, I would expect this to work correctly,
452 	 * but it seems that not all of the fields in the proc
453 	 * structure are being used.
454 	 *
455 	 * For now, we'll attempt to get some of the things we need
456 	 * from the mach task info.
457 	 */
458 
459 	sprintf(fmt,
460 		Proc_format,
461 		MPP(pp, p_pid),
462 		(*getuserid)(MEP(pp, e_pcred.p_ruid)),
463 //		TP(pp, base_priority),
464 		0,
465 		pp->thread_count,
466 		format_k(TASKSIZE(pp) / 1024),
467 		format_k(pagetok(RSSIZE(pp))),
468 		state_abbrev[(u_char)MPP(pp, p_stat)],
469 		format_time(cputime),
470 		100.0 * TP(pp, resident_size) / maxmem,
471 //		100.0 * weighted_cpu(pct, (RP(pp, user_time).seconds + RP(pp, system_time).seconds)),
472 		100.0 * pct,
473 		printable(MPP(pp, p_comm)));
474 
475 	return(fmt);
476 }
477 
478 /*
479  * get_process_info()
480  *
481  * This function returns information about the processes
482  * on the system.
483  */
484 
485 caddr_t get_process_info(struct system_info *si,
486 		struct process_select *sel, int x)
487 
488 {
489 	register int 				i;
490 	register int 				total_procs;
491 	register int 				active_procs;
492 	register struct macos_proc 		**prefp;
493 	register struct macos_proc 		*pp;
494 	register struct kinfo_proc		*pp2;
495 	register struct kinfo_proc		**prefp2;
496 	register struct thread_basic_info 	*thread;
497 
498 	/*
499 	 * these are copied out of sel for speed
500 	 */
501 
502 	int show_idle;
503 	int show_system;
504 	int show_uid;
505 	int show_command;
506 
507 	kproc_list = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc);
508 
509 	if(nproc > onproc)
510 		{
511 		proc_list = (struct macos_proc*)realloc(proc_list, sizeof(struct macos_proc) * nproc);
512 		proc_ref = (struct macos_proc **)realloc(proc_ref, sizeof(struct macos_proc *) * (onproc = nproc));
513 		}
514 
515 	if(proc_ref == NULL || proc_list == NULL || kproc_list == NULL)
516 		{
517 		puke("error:  out of memory (%s)", strerror(errno));
518 		return(NULL);
519 		}
520 
521 	/*
522 	 * now, our task is to build the array of information we
523 	 * need to function correctly.  This involves setting a pointer
524 	 * to each real kinfo_proc structure returned by kvm_getprocs()
525 	 * in addition to getting the mach information for each of
526 	 * those processes.
527 	 */
528 
529 	for(pp2 = kproc_list, i = 0; i < nproc; pp2++, i++)
530 		{
531 		kern_return_t	rc;
532 		u_int		info_count = TASK_BASIC_INFO_COUNT;
533 
534 		/*
535 		 * first, we set the pointer to the reference in
536 		 * the kproc list.
537 		 */
538 
539 		proc_list[i].kproc = pp2;
540 
541 		/*
542 		 * then, we load all of the task info for the process
543 		 */
544 
545 		if(PP(pp2, p_stat) != SZOMB)
546 			{
547 			rc = task_for_pid(mach_task_self(),
548 				PP(pp2, p_pid),
549 				&(proc_list[i].the_task));
550 
551 			if(rc != KERN_SUCCESS)
552 				{
553 				puke("error:  get task info for pid %d failed with rc = %d", PP(pp2, p_pid), rc);
554 				}
555 
556 			/*
557 			 * load the task information
558 			 */
559 
560 			rc = task_info(proc_list[i].the_task, TASK_BASIC_INFO,
561 				(task_info_t)&(proc_list[i].task_info),
562 				&info_count);
563 
564 			if(rc != KERN_SUCCESS)
565 				{
566 				puke("error:  couldn't get task info (%s); rc = %d", strerror(errno), rc);
567 				}
568 
569 			/*
570 			 * load the thread summary information
571 			 */
572 
573 			load_thread_info(&proc_list[i]);
574 			}
575 		}
576 
577 	/* get a pointer to the states summary array */
578 	si->procstates = process_states;
579 
580 	/* set up flags which define what we are going to select */
581 	show_idle = sel->idle;
582 	show_system = sel->system;
583 	show_uid = sel->uid != -1;
584 	show_command = sel->command != NULL;
585 
586 	/* count up process states and get pointers to interesting procs */
587 	total_procs = 0;
588 	active_procs = 0;
589 	memset((char *)process_states, 0, sizeof(process_states));
590 	prefp = proc_ref;
591 	for(pp = proc_list, i = 0; i < nproc; pp++, i++)
592 		{
593 		/*
594 		 *  Place pointers to each valid proc structure in
595 		 * proc_ref[].  Process slots that are actually in use
596 		 * have a non-zero status field.  Processes with
597 		 * P_SYSTEM set are system processes---these get
598 		 * ignored unless show_sysprocs is set.
599 		 */
600 		if(MPP(pp, p_stat) != 0 &&
601 				(show_system || ((MPP(pp, p_flag) & P_SYSTEM) == 0)))
602 			{
603 			total_procs++;
604 			process_states[(unsigned char) MPP(pp, p_stat)]++;
605 			if((MPP(pp, p_stat) != SZOMB) &&
606 					(show_idle || (MPP(pp, p_pctcpu) != 0) ||
607 			 		(MPP(pp, p_stat) == SRUN)) &&
608 					(!show_uid || MEP(pp, e_pcred.p_ruid) == (uid_t)sel->uid))
609 				{
610 				*prefp++ = pp;
611 				active_procs++;
612 				}
613 			}
614 		}
615 
616 	/*
617 	 * if requested, sort the "interesting" processes
618 	 */
619 
620 	qsort((char *)proc_ref, active_procs, sizeof(struct macos_proc *), proc_compare);
621 
622 	/* remember active and total counts */
623 	si->p_total = total_procs;
624 	si->p_active = pref_len = active_procs;
625 
626 	/* pass back a handle */
627 	handle.next_proc = proc_ref;
628 	handle.remaining = active_procs;
629 	return((caddr_t)&handle);
630 }
631 
632 /*
633  * get_system_info()
634  *
635  * This function is responsible for geting the periodic
636  * system information snapshot.
637  */
638 
639 void get_system_info(struct system_info *si)
640 {
641 	register long	total;
642 	register int	i;
643 	unsigned int count = HOST_CPU_LOAD_INFO_COUNT;
644 
645 	if (host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO,
646 			    (host_info_t)&cpuload, &count) == KERN_SUCCESS)
647 	{
648 	    for (i = 0; i < CPU_STATE_MAX; i++)
649 	    {
650 		cp_time[i] = cpuload.cpu_ticks[i];
651 	    }
652 	}
653 
654 #ifdef MAX_VERBOSE
655 
656 	/*
657 	 * print out the entries
658 	 */
659 
660 	for(i = 0; i < CPU_STATE_MAX; i++)
661 		printf("cp_time[%d] = %d\n", i, cp_time[i]);
662 	fflush(stdout);
663 
664 #endif /* MAX_VERBOSE */
665 
666 	/*
667 	 * get the load averages
668 	 */
669 
670 	if(kvm_getloadavg(kd, si->load_avg, NUM_AVERAGES) == -1)
671 		{
672 		puke("error:  kvm_getloadavg() failed (%s)", strerror(errno));
673 		return;
674 		}
675 
676 #ifdef MAX_VERBOSE
677 	printf("%-30s%03.2f, %03.2f, %03.2f\n",
678 			"load averages:",
679 			si->load_avg[0],
680 			si->load_avg[1],
681 			si->load_avg[2]);
682 #endif /* MAX_VERBOSE */
683 
684 	total = percentages(CPU_STATE_MAX, cpu_states, cp_time, cp_old, cp_diff);
685 	/*
686 	 * get the memory statistics
687 	 */
688 
689 	{
690 		kern_return_t	status;
691 
692 		count = HOST_VM_INFO_COUNT;
693 		status = host_statistics(mach_host_self(), HOST_VM_INFO,
694 					 (host_info_t)&vm_stats, &count);
695 
696 		if(status != KERN_SUCCESS)
697 			{
698 			puke("error:  vm_statistics() failed (%s)", strerror(errno));
699 			return;
700 			}
701 
702 		/*
703 		 * we already have the total memory, we just need
704 		 * to get it in the right format.
705 		 */
706 
707 		memory_stats[0] = pagetok(maxmem / pagesize);
708 		memory_stats[1] = pagetok(vm_stats.free_count);
709 		memory_stats[2] = pagetok(vm_stats.active_count);
710 		memory_stats[3] = pagetok(vm_stats.inactive_count);
711 		memory_stats[4] = pagetok(vm_stats.wire_count);
712 
713 		if(swappgsin < 0)
714 			{
715 			memory_stats[5] = 1;
716 			memory_stats[6] = 1;
717 			}
718 		else
719 			{
720 			memory_stats[5] = pagetok(((vm_stats.pageins - swappgsin)));
721 			memory_stats[6] = pagetok(((vm_stats.pageouts - swappgsout)));
722 			}
723 		swappgsin = vm_stats.pageins;
724 		swappgsout = vm_stats.pageouts;
725 	}
726 
727 	si->cpustates = cpu_states;
728 	si->memory = memory_stats;
729 	si->last_pid = -1;
730 
731 	return;
732 }
733 
734 /*
735  * machine_init()
736  *
737  * This function is responsible for filling in the values of the
738  * statics structure.
739  */
740 
741 int machine_init(struct statics *stat)
742 {
743 	register int rc = 0;
744 	register int i = 0;
745 	size_t size;
746 
747 	size = sizeof(maxmem);
748 	sysctlbyname("hw.physmem", &maxmem, &size, NULL, 0);
749 
750 	size = sizeof(nproc);
751 	sysctlbyname("kern.maxproc", &nproc, &size, NULL, 0);
752 
753 #ifdef MAX_VERBOSE
754 	printf("%-30s%10d\n", "total system memory:", maxmem);
755 #endif /* MAX_VERBOSE */
756 
757 	/*
758 	 * calculate the pageshift from the system page size
759 	 */
760 
761 	pagesize = getpagesize();
762 	pageshift = 0;
763 	while((pagesize >>= 1) > 0)
764 		pageshift++;
765 
766 	pageshift -= LOG1024;
767 
768 	/*
769 	 * fill in the statics information
770 	 */
771 
772 	stat->procstate_names = procstates;
773 	stat->cpustate_names = cpustates;
774 	stat->memory_names = memnames;
775 
776 	if ((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "kvm_open")) == NULL)
777 	  return -1;
778 
779 	return(0);
780 }
781 
782 /* comparison routine for qsort */
783 
784 /*
785  *  proc_compare - comparison function for "qsort"
786  *	Compares the resource consumption of two processes using five
787  *  	distinct keys.  The keys (in descending order of importance) are:
788  *  	percent cpu, cpu ticks, state, resident set size, total virtual
789  *  	memory usage.  The process states are ordered as follows (from least
790  *  	to most important):  WAIT, zombie, sleep, stop, start, run.  The
791  *  	array declaration below maps a process state index into a number
792  *  	that reflects this ordering.
793  */
794 
795 static unsigned char sorted_state[] =
796 {
797     0,	/* not used		*/
798     3,	/* sleep		*/
799     1,	/* ABANDONED (WAIT)	*/
800     6,	/* run			*/
801     5,	/* start		*/
802     2,	/* zombie		*/
803     4	/* stop			*/
804 };
805 
806 int proc_compare(const void *pp1, const void *pp2)
807 {
808     register struct macos_proc *p1;
809     register struct macos_proc *p2;
810     register int result;
811     register pctcpu lresult;
812 
813     /* remove one level of indirection */
814     p1 = *(struct macos_proc **) pp1;
815     p2 = *(struct macos_proc **) pp2;
816 
817     /* compare percent cpu (pctcpu) */
818     if ((lresult = RP(p2, cpu_usage) - RP(p1, cpu_usage)) == 0)
819     {
820 	/* use cpticks to break the tie */
821 	if ((result = MPP(p2, p_cpticks) - MPP(p1, p_cpticks)) == 0)
822 	{
823 	    /* use process state to break the tie */
824 	    if ((result = sorted_state[(unsigned char) MPP(p2, p_stat)] -
825 			  sorted_state[(unsigned char) MPP(p1, p_stat)])  == 0)
826 	    {
827 		/* use priority to break the tie */
828 		if ((result = MPP(p2, p_priority) - MPP(p1, p_priority)) == 0)
829 		{
830 		    /* use resident set size (rssize) to break the tie */
831 		    if ((result = RSSIZE(p2) - RSSIZE(p1)) == 0)
832 		    {
833 			/* use total memory to break the tie */
834 			result = PROCSIZE(p2->kproc) - PROCSIZE(p1->kproc);
835 		    }
836 		}
837 	    }
838 	}
839     }
840     else
841     {
842 	result = lresult < 0 ? -1 : 1;
843     }
844 
845     return(result);
846 }
847 
848 
849 /*
850  * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
851  *		the process does not exist.
852  *		It is EXTREMLY IMPORTANT that this function work correctly.
853  *		If top runs setuid root (as in SVR4), then this function
854  *		is the only thing that stands in the way of a serious
855  *		security problem.  It validates requests for the "kill"
856  *		and "renice" commands.
857  */
858 
859 int proc_owner(pid)
860 
861 int pid;
862 
863 {
864     register int cnt;
865     register struct macos_proc **prefp;
866     register struct macos_proc *pp;
867 
868     prefp = proc_ref;
869     cnt = pref_len;
870     while (--cnt >= 0)
871     {
872 	pp = *prefp++;
873 	if (MPP(pp, p_pid) == (pid_t)pid)
874 	{
875 	    return((int)MEP(pp, e_pcred.p_ruid));
876 	}
877     }
878     return(-1);
879 }
880 
881 /*
882  * load_thread_info()
883  *
884  * This function will attempt to load the thread summary info
885  * for a Mach task.  The task is located as part of the macos_proc
886  * structure.
887  *
888  * returns the kern_return_t value of any failed call or KERN_SUCCESS
889  * if everything works.
890  */
891 
892 int load_thread_info(struct macos_proc *mp)
893 {
894 	register kern_return_t		rc = 0;
895 	register int			i = 0;
896 	register int			t_utime = 0;
897 	register int			t_stime = 0;
898 	register int			t_cpu = 0;
899 	register int			t_state = 0;
900 	register task_t			the_task = mp->the_task;
901 
902 	thread_array_t			thread_list = NULL;
903 
904 	/*
905 	 * We need to load all of the threads for the
906 	 * given task so we can get the performance
907 	 * data from them.
908 	 */
909 
910 	mp->thread_count = 0;
911 	rc = task_threads(the_task, &thread_list, &(mp->thread_count));
912 
913 	if(rc != KERN_SUCCESS)
914 		{
915 //		puke("error:  unable to load threads for task (%s); rc = %d", strerror(errno), rc);
916 		return(rc);
917 		}
918 
919 	/*
920 	 * now, for each of the threads, we need to sum the stats
921 	 * so we can present the whole thing to the caller.
922 	 */
923 
924 	for(i = 0; i < mp->thread_count; i++)
925 		{
926 		struct thread_basic_info	t_info;
927 		unsigned int			icount = THREAD_BASIC_INFO_COUNT;
928 		kern_return_t			rc = 0;
929 
930 		rc = thread_info(thread_list[i], THREAD_BASIC_INFO,
931 				(thread_info_t)&t_info, &icount);
932 
933 		if(rc != KERN_SUCCESS)
934 			{
935 			puke("error:  unable to load thread info for task (%s); rc = %d", strerror(errno), rc);
936 			return(rc);
937 			}
938 
939 		t_utime += t_info.user_time.seconds;
940 		t_stime += t_info.system_time.seconds;
941 		t_cpu += t_info.cpu_usage;
942 		}
943 
944 	vm_deallocate(mach_task_self(), (vm_address_t)thread_list, sizeof(thread_array_t)*(mp->thread_count));
945 
946 	/*
947 	 * Now, we load the values in the structure above.
948 	 */
949 
950 	RP(mp, user_time).seconds = t_utime;
951 	RP(mp, system_time).seconds = t_stime;
952 	RP(mp, cpu_usage) = t_cpu;
953 
954 	return(KERN_SUCCESS);
955 }
956 
957