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.
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)
42 * MacOS X Server (Rhapsody Developer Release 2)
44 * CFLAGS: -DHAVE_STRERROR
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>
76 #define VMUNIX "/mach_kernel"
77 #define MEM "/dev/mem"
80 #define NUM_AVERAGES 3
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)
92 /* define what weighted cpu is */
93 #define weighted_cpu(pct, s) (s == 0 ? 0.0 : \
94 ((pct) / (1.0 - exp(s * logcpu))))
96 /* what we consider to be process size: */
98 #define PROCSIZE(pp) (VP((pp), vm_tsize) + VP((pp), vm_dsize) + VP((pp), vm_ssize))
100 #define PROCSIZE(pp) (EP(pp, e_xsize))
101 #define TASKSIZE(t) (TP(t, virtual_size) + TP(t, resident_size))
103 /* what we consider to be resident set size: */
105 #define RSSIZE(pp) (MVP((pp), vm_rssize))
107 #define RSSIZE(pp) (MEP((pp), e_xrssize))
109 #define pctdouble(p) ((double)(p) / FSCALE)
115 static kvm_t
*kd
= NULL
;
117 static int onproc
= -1;
120 static char fmt
[MAX_COLS
];
121 static double logcpu
= 1.0;
123 /* process array stuff */
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
;
132 * The mach information hopefully will not be necessary
133 * when the kvm_* interfaces are supported completely.
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.
140 static struct task_basic_info
*task_list
= NULL
;
142 /* memory statistics */
144 static int pageshift
= 0;
145 static int pagesize
= 0;
146 #define pagetok(size) ((size) << pageshift)
148 static int swappgsin
= -1;
149 static int swappgsout
= -1;
150 static vm_statistics_data_t vm_stats
;
151 static long memory_stats
[7];
153 /* CPU state percentages */
155 host_cpu_load_info_data_t cpuload
;
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
];
170 // char **procstate_names;
171 // char **cpustate_names;
172 // char **memory_names;
173 // char **order_names;
179 // double load_avg[NUM_AVERAGES];
180 // int p_total; /* total # of processes */
181 // int p_active; /* number processes considered active */
187 //struct process_select
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) */
196 * We need to declare a hybrid structure which will store all
197 * of the stuff we care about for each process.
202 struct kinfo_proc
*kproc
;
204 struct task_basic_info task_info
;
205 unsigned int thread_count
;
206 struct thread_basic_info thread_summary
;
211 struct macos_proc
**next_proc
;
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
220 #define Proc_format \
221 "%5d %-8.8s %3d %4d %5s %5s %-5s %6s %5.2f%% %5.2f%% %.16s"
224 int proc_compare(const void *, const void *);
230 * This function is used to report errors to stderr.
233 static void puke(const char* fmt
, ...)
237 vfprintf(stderr
, fmt
, args
);
247 * This function is a wrapper for the kvm_read() function
248 * with the addition of a message parameter per kvm_open().
250 * All other behavior is per kvm_read except the error reporting.
253 static ssize_t
kread(u_long addr
, void *buf
,
254 size_t nbytes
, const char *errstr
)
258 s
= kvm_read(kd
, addr
, buf
, nbytes
);
261 puke("error: kvm_read() failed for '%s' (%s)\n",
262 errstr
, strerror(errno
));
269 * prototypes for functions which top needs
275 * definitions for offsets
284 static struct nlist nlst
[] =
286 { "_maxproc" }, /* 0 *** maximum processes */
288 { "_mem_size" }, /* 2 */
292 static char *procstates
[] =
304 static char *cpustates
[] =
313 static char *state_abbrev
[] =
323 static char *mach_state
[] =
333 static char *thread_state
[] =
343 static char *flags_state
[] =
350 static char *memnames
[] =
365 * This function is used to add the username into the
366 * header information.
369 char *format_header(register char *uname_field
)
373 ptr
= header
+ UNAME_START
;
374 while(*uname_field
!= '\0')
375 *ptr
++ = *uname_field
++;
381 * format_next_process()
383 * This function actuall is responsible for the formatting of
384 * each row which is displayed.
387 char *format_next_process(caddr_t handle
, char *(*getuserid
)())
389 register struct macos_proc
*pp
;
390 register long cputime
;
397 * we need to keep track of the next proc structure.
400 hp
= (struct handle
*)handle
;
401 pp
= *(hp
->next_proc
++);
405 * get the process structure and take care of the cputime
408 if((MPP(pp
, p_flag
) & P_INMEM
) == 0)
410 /* we want to print swapped processes as <pname> */
411 char *comm
= MPP(pp
, p_comm
);
412 #define COMSIZ sizeof(MPP(pp, p_comm))
414 strncpy(buf
, comm
, COMSIZ
);
416 strncpy(&comm
[1], buf
, COMSIZ
- 2);
417 comm
[COMSIZ
- 2] = '\0';
418 strncat(comm
, ">", COMSIZ
- 1);
419 comm
[COMSIZ
- 1] = '\0';
423 * count the cpu time, but ignore the interrupts
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
430 * cputime = PP(pp, p_rtime).tv_sec;
433 cputime
= RP(pp
, user_time
).seconds
+ RP(pp
, system_time
).seconds
;
436 * calculate the base cpu percentages
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.
443 // pct = pctdouble(PP(pp, p_pctcpu));
444 pct
= (double)(RP(pp
, cpu_usage
))/TH_USAGE_SCALE
;
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.
455 * For now, we'll attempt to get some of the things we need
456 * from the mach task info.
462 (*getuserid
)(MEP(pp
, e_pcred
.p_ruid
)),
463 // TP(pp, base_priority),
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)),
473 printable(MPP(pp
, p_comm
)));
481 * This function returns information about the processes
485 caddr_t
get_process_info(struct system_info
*si
,
486 struct process_select
*sel
, int x
)
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
;
499 * these are copied out of sel for speed
507 kproc_list
= kvm_getprocs(kd
, KERN_PROC_ALL
, 0, &nproc
);
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
));
515 if(proc_ref
== NULL
|| proc_list
== NULL
|| kproc_list
== NULL
)
517 puke("error: out of memory (%s)", strerror(errno
));
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
529 for(pp2
= kproc_list
, i
= 0; i
< nproc
; pp2
++, i
++)
532 u_int info_count
= TASK_BASIC_INFO_COUNT
;
535 * first, we set the pointer to the reference in
539 proc_list
[i
].kproc
= pp2
;
542 * then, we load all of the task info for the process
545 if(PP(pp2
, p_stat
) != SZOMB
)
547 rc
= task_for_pid(mach_task_self(),
549 &(proc_list
[i
].the_task
));
551 if(rc
!= KERN_SUCCESS
)
553 puke("error: get task info for pid %d failed with rc = %d", PP(pp2
, p_pid
), rc
);
557 * load the task information
560 rc
= task_info(proc_list
[i
].the_task
, TASK_BASIC_INFO
,
561 (task_info_t
)&(proc_list
[i
].task_info
),
564 if(rc
!= KERN_SUCCESS
)
566 puke("error: couldn't get task info (%s); rc = %d", strerror(errno
), rc
);
570 * load the thread summary information
573 load_thread_info(&proc_list
[i
]);
577 /* get a pointer to the states summary array */
578 si
->procstates
= process_states
;
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
;
586 /* count up process states and get pointers to interesting procs */
589 memset((char *)process_states
, 0, sizeof(process_states
));
591 for(pp
= proc_list
, i
= 0; i
< nproc
; pp
++, i
++)
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.
600 if(MPP(pp
, p_stat
) != 0 &&
601 (show_system
|| ((MPP(pp
, p_flag
) & P_SYSTEM
) == 0)))
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
))
617 * if requested, sort the "interesting" processes
620 qsort((char *)proc_ref
, active_procs
, sizeof(struct macos_proc
*), proc_compare
);
622 /* remember active and total counts */
623 si
->p_total
= total_procs
;
624 si
->p_active
= pref_len
= active_procs
;
626 /* pass back a handle */
627 handle
.next_proc
= proc_ref
;
628 handle
.remaining
= active_procs
;
629 return((caddr_t
)&handle
);
635 * This function is responsible for geting the periodic
636 * system information snapshot.
639 void get_system_info(struct system_info
*si
)
643 unsigned int count
= HOST_CPU_LOAD_INFO_COUNT
;
645 if (host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO
,
646 (host_info_t
)&cpuload
, &count
) == KERN_SUCCESS
)
648 for (i
= 0; i
< CPU_STATE_MAX
; i
++)
650 cp_time
[i
] = cpuload
.cpu_ticks
[i
];
657 * print out the entries
660 for(i
= 0; i
< CPU_STATE_MAX
; i
++)
661 printf("cp_time[%d] = %d\n", i
, cp_time
[i
]);
664 #endif /* MAX_VERBOSE */
667 * get the load averages
670 if(kvm_getloadavg(kd
, si
->load_avg
, NUM_AVERAGES
) == -1)
672 puke("error: kvm_getloadavg() failed (%s)", strerror(errno
));
677 printf("%-30s%03.2f, %03.2f, %03.2f\n",
682 #endif /* MAX_VERBOSE */
684 total
= percentages(CPU_STATE_MAX
, cpu_states
, cp_time
, cp_old
, cp_diff
);
686 * get the memory statistics
690 kern_return_t status
;
692 count
= HOST_VM_INFO_COUNT
;
693 status
= host_statistics(mach_host_self(), HOST_VM_INFO
,
694 (host_info_t
)&vm_stats
, &count
);
696 if(status
!= KERN_SUCCESS
)
698 puke("error: vm_statistics() failed (%s)", strerror(errno
));
703 * we already have the total memory, we just need
704 * to get it in the right format.
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
);
720 memory_stats
[5] = pagetok(((vm_stats
.pageins
- swappgsin
)));
721 memory_stats
[6] = pagetok(((vm_stats
.pageouts
- swappgsout
)));
723 swappgsin
= vm_stats
.pageins
;
724 swappgsout
= vm_stats
.pageouts
;
727 si
->cpustates
= cpu_states
;
728 si
->memory
= memory_stats
;
737 * This function is responsible for filling in the values of the
741 int machine_init(struct statics
*stat
)
747 size
= sizeof(maxmem
);
748 sysctlbyname("hw.physmem", &maxmem
, &size
, NULL
, 0);
750 size
= sizeof(nproc
);
751 sysctlbyname("kern.maxproc", &nproc
, &size
, NULL
, 0);
754 printf("%-30s%10d\n", "total system memory:", maxmem
);
755 #endif /* MAX_VERBOSE */
758 * calculate the pageshift from the system page size
761 pagesize
= getpagesize();
763 while((pagesize
>>= 1) > 0)
766 pageshift
-= LOG1024
;
769 * fill in the statics information
772 stat
->procstate_names
= procstates
;
773 stat
->cpustate_names
= cpustates
;
774 stat
->memory_names
= memnames
;
776 if ((kd
= kvm_open(NULL
, NULL
, NULL
, O_RDONLY
, "kvm_open")) == NULL
)
782 /* comparison routine for qsort */
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.
795 static unsigned char sorted_state
[] =
799 1, /* ABANDONED (WAIT) */
806 int proc_compare(const void *pp1
, const void *pp2
)
808 register struct macos_proc
*p1
;
809 register struct macos_proc
*p2
;
811 register pctcpu lresult
;
813 /* remove one level of indirection */
814 p1
= *(struct macos_proc
**) pp1
;
815 p2
= *(struct macos_proc
**) pp2
;
817 /* compare percent cpu (pctcpu) */
818 if ((lresult
= RP(p2
, cpu_usage
) - RP(p1
, cpu_usage
)) == 0)
820 /* use cpticks to break the tie */
821 if ((result
= MPP(p2
, p_cpticks
) - MPP(p1
, p_cpticks
)) == 0)
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)
827 /* use priority to break the tie */
828 if ((result
= MPP(p2
, p_priority
) - MPP(p1
, p_priority
)) == 0)
830 /* use resident set size (rssize) to break the tie */
831 if ((result
= RSSIZE(p2
) - RSSIZE(p1
)) == 0)
833 /* use total memory to break the tie */
834 result
= PROCSIZE(p2
->kproc
) - PROCSIZE(p1
->kproc
);
842 result
= lresult
< 0 ? -1 : 1;
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.
865 register struct macos_proc
**prefp
;
866 register struct macos_proc
*pp
;
873 if (MPP(pp
, p_pid
) == (pid_t
)pid
)
875 return((int)MEP(pp
, e_pcred
.p_ruid
));
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
888 * returns the kern_return_t value of any failed call or KERN_SUCCESS
889 * if everything works.
892 int load_thread_info(struct macos_proc
*mp
)
894 register kern_return_t rc
= 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
;
902 thread_array_t thread_list
= NULL
;
905 * We need to load all of the threads for the
906 * given task so we can get the performance
910 mp
->thread_count
= 0;
911 rc
= task_threads(the_task
, &thread_list
, &(mp
->thread_count
));
913 if(rc
!= KERN_SUCCESS
)
915 // puke("error: unable to load threads for task (%s); rc = %d", strerror(errno), rc);
920 * now, for each of the threads, we need to sum the stats
921 * so we can present the whole thing to the caller.
924 for(i
= 0; i
< mp
->thread_count
; i
++)
926 struct thread_basic_info t_info
;
927 unsigned int icount
= THREAD_BASIC_INFO_COUNT
;
928 kern_return_t rc
= 0;
930 rc
= thread_info(thread_list
[i
], THREAD_BASIC_INFO
,
931 (thread_info_t
)&t_info
, &icount
);
933 if(rc
!= KERN_SUCCESS
)
935 puke("error: unable to load thread info for task (%s); rc = %d", strerror(errno
), rc
);
939 t_utime
+= t_info
.user_time
.seconds
;
940 t_stime
+= t_info
.system_time
.seconds
;
941 t_cpu
+= t_info
.cpu_usage
;
944 vm_deallocate(mach_task_self(), (vm_address_t
)thread_list
, sizeof(thread_array_t
)*(mp
->thread_count
));
947 * Now, we load the values in the structure above.
950 RP(mp
, user_time
).seconds
= t_utime
;
951 RP(mp
, system_time
).seconds
= t_stime
;
952 RP(mp
, cpu_usage
) = t_cpu
;
954 return(KERN_SUCCESS
);