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
puke(const char * fmt,...)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
kread(u_long addr,void * buf,size_t nbytes,const char * errstr)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
format_header(register char * uname_field)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
format_next_process(caddr_t handle,char * (* getuserid)())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
get_process_info(struct system_info * si,struct process_select * sel,int x)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
get_system_info(struct system_info * si)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
machine_init(struct statics * stat)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
proc_compare(const void * pp1,const void * pp2)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
proc_owner(pid)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
load_thread_info(struct macos_proc * mp)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