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