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