1/* 2 * Copyright (c) 2002-2004, 2008, 2009 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * The contents of this file constitute Original Code as defined in and 7 * are subject to the Apple Public Source License Version 1.1 (the 8 * "License"). You may not use this file except in compliance with the 9 * License. Please obtain a copy of the License at 10 * http://www.apple.com/publicsource and read it before using this file. 11 * 12 * This Original Code and all software distributed under the License are 13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER 14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the 17 * License for the specific language governing rights and limitations 18 * under the License. 19 * 20 * @APPLE_LICENSE_HEADER_END@ 21 */ 22 23#include <stdio.h> 24#include <stdlib.h> 25#include <limits.h> 26#include <sys/types.h> 27#include <mach/bootstrap.h> 28#include <mach/host_priv.h> 29#include <mach/mach_error.h> 30#include <mach/mach_host.h> 31#include <mach/mach_port.h> 32#include <mach/mach_vm.h> 33#include <mach/mach_types.h> 34#include <mach/message.h> 35#include <mach/processor_set.h> 36#include <mach/task.h> 37#include <mach/thread_act.h> 38#include <mach/shared_region.h> 39#include <mach/vm_map.h> 40 41#define IOKIT 1 /* For io_name_t in device/device_types.h. */ 42#include <device/device_types.h> 43#include <CoreFoundation/CoreFoundation.h> 44#include <IOKit/IOKitLib.h> 45#include <IOKit/storage/IOBlockStorageDriver.h> 46 47#include <libproc.h> 48 49#include <fcntl.h> 50#include <nlist.h> 51#include <sys/param.h> 52#include <sys/sysctl.h> 53#include <pwd.h> 54 55#include <sys/resource.h> 56 57#include <sys/socket.h> 58#include <net/if.h> 59#include <net/route.h> 60#include <net/if_types.h> 61#include <ifaddrs.h> 62 63#define LIBTOP_DBG 64#ifndef LIBTOP_DBG 65/* Disable assertions. */ 66#ifndef NDEBUG 67#define NDEBUG 68#endif 69#endif 70#include <assert.h> 71 72#include "libtop.h" 73#include "rb.h" 74 75/* 76 * Process info. 77 */ 78typedef struct libtop_pinfo_s libtop_pinfo_t; 79struct libtop_pinfo_s { 80 /* Sample data that are exposed to the library user. */ 81 libtop_psamp_t psamp; 82 83 /* Linkage for pid-ordered tree. */ 84 rb_node(libtop_pinfo_t) pnode; 85 86 /* Linkage for sorted tree. */ 87 rb_node(libtop_pinfo_t) snode; 88 89 int flag; /* Set, but not used. */ 90 91 /* Manual override for memory region reporting. */ 92 libtop_preg_t preg; 93 94 /* TRUE if the globally shared text and data segments are mapped in. */ 95 boolean_t split; 96}; 97 98/* Memory object info. */ 99typedef struct libtop_oinfo_s libtop_oinfo_t; 100struct libtop_oinfo_s { 101 /* 102 * pinfo structure that was most recently used to access this 103 * structure. 104 */ 105 libtop_pinfo_t *pinfo; 106 107 /* Memory object ID. */ 108 int obj_id; 109 110 /* Memory object size, in pages. */ 111 int size; 112 113 /* SM_PRIVATE, SM_SHARED, SM_PRIVATE_ALIASED, SM_COW, ... */ 114 int share_type; 115 116 /* Number of pages resident in memory. */ 117 int resident_page_count; 118 119 /* Number of references to memory object. */ 120 int ref_count; 121 122 /* Number of references to memory object that pinfo holds. */ 123 int proc_ref_count; 124 125 /* 126 * Rollback fields. These are used to store old values that need to be 127 * rolled back to their previous values if an object is referenced more 128 * than once by a process. 129 * 130 * The reason rolling back is necessary has to do with the fact that 131 * memory usage statistics are tallied on the fly, but this can only be 132 * accurately done for each memory region a process refers to once all 133 * of the references have been encountered. Since the code 134 * optimistically updates the statistics, changes have to be backed out 135 * when another reference to the same memory region is encountered. 136 */ 137 int rb_share_type; /* SM_EMPTY == "no rollback" */ 138 uint64_t rb_aliased; 139 uint64_t rb_vprvt; 140 uint64_t rb_rshrd; 141}; 142 143static boolean_t ignore_PPP; 144 145/* Sample data that are exposed to the library user. */ 146static libtop_tsamp_t tsamp; 147 148/* Function pointer that points to an abstract printing function. */ 149static libtop_print_t *libtop_print; 150static void *libtop_user_data; 151 152/* Temporary storage for sorting function and opaque data pointer. */ 153static libtop_sort_t *libtop_sort; 154static void *libtop_sort_data; 155 156/* Mach port, used for various Mach calls. */ 157static mach_port_t libtop_port; 158 159/* Buffer that is large enough to hold the entire argument area of a process. */ 160static char *libtop_arg; 161static int libtop_argmax; 162 163static mach_port_t libtop_master_port; 164 165static uint32_t interval; 166 167/* 168 * Memory object hash table and list. For each sample, a hash table of memory 169 * objects is created, and it is used to determine various per-process memory 170 * statistics, as well as the total number of memory objects. Rather than 171 * allocating and freeing oinfo structures for every sample, previously 172 * allocated structures are linked into a list, and objects in the list are used 173 * in preference to allocation. 174 */ 175static CFMutableDictionaryRef libtop_oinfo_hash; 176 177/* Tree of all pinfo's, always ordered by -pid. */ 178static rb_tree(libtop_pinfo_t) libtop_ptree; 179/* 180 * Transient tree of all pinfo's, created for each sample according to a 181 * sorting function. 182 */ 183static rb_tree(libtop_pinfo_t) libtop_stree; 184 185/* TRUE if the most recent sample is sorted. */ 186static boolean_t libtop_sorted; 187 188/* Pointer to the most recently seen pinfo structure in libtop_piterate(). */ 189static libtop_pinfo_t *libtop_piter; 190 191/* Cache of uid->username translations. */ 192static CFMutableDictionaryRef libtop_uhash; 193 194#define TIME_VALUE_TO_TIMEVAL(a, r) do { \ 195 (r)->tv_sec = (a)->seconds; \ 196 (r)->tv_usec = (a)->microseconds; \ 197} while (0) 198 199enum libtop_status { 200 LIBTOP_NO_ERR = 0, 201 LIBTOP_ERR_INVALID = 1, /* An invalid task. */ 202 LIBTOP_ERR_ALLOC /* An allocation error. */ 203}; 204 205typedef enum libtop_status libtop_status_t; 206 207/* Function prototypes. */ 208static boolean_t libtop_p_print(void *user_data, const char *format, ...); 209static int libtop_p_mach_state_order(int state, long sleep_time); 210static int libtop_p_load_get(host_info_t r_load); 211static int libtop_p_loadavg_update(void); 212static bool in_shared_region(mach_vm_address_t addr, cpu_type_t type); 213static void libtop_p_fw_scan(task_t task, mach_vm_address_t region_base, 214 mach_vm_size_t region_size); 215static void libtop_p_fw_sample(boolean_t fw); 216static int libtop_p_vm_sample(void); 217static void libtop_p_networks_sample(void); 218static int libtop_p_disks_sample(void); 219static int libtop_p_proc_table_read(boolean_t reg); 220static libtop_status_t libtop_p_cputype(pid_t pid, cpu_type_t *cputype); 221static mach_vm_size_t libtop_p_shreg_size(cpu_type_t); 222static libtop_status_t libtop_p_task_update(task_t task, boolean_t reg); 223static libtop_status_t libtop_p_proc_command(libtop_pinfo_t *pinfo, 224 struct kinfo_proc *kinfo); 225static void libtop_p_pinsert(libtop_pinfo_t *pinfo); 226static void libtop_p_premove(libtop_pinfo_t *pinfo); 227static void libtop_p_destroy_pinfo(libtop_pinfo_t *pinfo); 228static libtop_pinfo_t* libtop_p_psearch(pid_t pid); 229static int libtop_p_pinfo_pid_comp(libtop_pinfo_t *a, libtop_pinfo_t *b); 230static int libtop_p_pinfo_comp(libtop_pinfo_t *a, libtop_pinfo_t *b); 231static libtop_oinfo_t* libtop_p_oinfo_insert(int obj_id, int share_type, 232 int resident_page_count, int ref_count, int size, 233 libtop_pinfo_t *pinfo); 234static int libtop_p_wq_update(libtop_pinfo_t *pinfo); 235static void libtop_i64_test(void); 236static void update_pages_stolen(libtop_tsamp_t *tsamp); 237 238 239/* CFDictionary callbacks */ 240static void 241simpleFree(CFAllocatorRef allocator, const void *value) 242{ 243 free((void *)value); 244} 245 246static const void * 247stringRetain(CFAllocatorRef allocator, const void *value) 248{ 249 return strdup(value); 250} 251 252static Boolean 253stringEqual(const void *value1, const void *value2) 254{ 255 return strcmp(value1, value2) == 0; 256} 257 258int 259libtop_init(libtop_print_t *print, void *user_data) 260{ 261 //libtop_i64_test(); 262 263 if (print != NULL) { 264 libtop_print = print; 265 libtop_user_data = user_data; 266 } else { 267 /* Use a noop printing function. */ 268 libtop_print = libtop_p_print; 269 libtop_user_data = NULL; 270 } 271 272 tsamp.seq = 0; 273 interval = 1; 274 libtop_port = mach_host_self(); 275 host_page_size(libtop_port, &tsamp.pagesize); 276 277 /* Get the physical memory size of the system. */ 278 { 279 int mib[2]; 280 size_t size; 281 282 mib[0] = CTL_HW; 283 mib[1] = HW_MEMSIZE; 284 285 size = sizeof(tsamp.memsize); 286 if (sysctl(mib, 2, &tsamp.memsize, &size, NULL, 0) == -1) { 287 libtop_print(libtop_user_data, "%s(): Error in sysctl(): %s", 288 __FUNCTION__, strerror(errno)); 289 return -1; 290 } 291 } 292 293 update_pages_stolen(&tsamp); 294 295 /* Initialize the pinfo tree. */ 296 rb_tree_new(&libtop_ptree, pnode); 297 298 /* Initialized the user hash. */ 299 CFDictionaryValueCallBacks stringValueCallBacks = { 0, stringRetain, simpleFree, NULL, stringEqual }; 300 libtop_uhash = CFDictionaryCreateMutable(NULL, 0, NULL, &stringValueCallBacks); 301 302 /* Initialize the memory object hash table and spares ring. */ 303 CFDictionaryValueCallBacks oinfoValueCallBacks = { 0, NULL, simpleFree, NULL, NULL }; 304 libtop_oinfo_hash = CFDictionaryCreateMutable(NULL, 0, NULL, &oinfoValueCallBacks); 305 306 /* 307 * Allocate a buffer that is large enough to hold the maximum arguments 308 * to execve(). This is used when getting the arguments to programs. 309 */ 310 { 311 int mib[2]; 312 size_t size; 313 314 mib[0] = CTL_KERN; 315 mib[1] = KERN_ARGMAX; 316 317 size = sizeof(libtop_argmax); 318 if (sysctl(mib, 2, &libtop_argmax, &size, NULL, 0) == -1) { 319 libtop_print(libtop_user_data, "%s(): Error in sysctl(): %s", 320 __FUNCTION__, strerror(errno)); 321 return -1; 322 } 323 324 libtop_arg = (char *)malloc(libtop_argmax); 325 if (libtop_arg == NULL) return -1; 326 } 327 328 /* 329 * Get ports and services for drive statistics. 330 */ 331 332 if (IOMasterPort(bootstrap_port, &libtop_master_port)) { 333 libtop_print(libtop_user_data, "Error in IOMasterPort()"); 334 return -1; 335 } 336 337 /* Initialize the load statistics. */ 338 if (libtop_p_load_get((host_info_t)&tsamp.b_cpu)) return -1; 339 340 tsamp.p_cpu = tsamp.b_cpu; 341 tsamp.cpu = tsamp.b_cpu; 342 343 /* Initialize the time. */ 344 gettimeofday(&tsamp.b_time, NULL); 345 tsamp.p_time = tsamp.b_time; 346 tsamp.time = tsamp.b_time; 347 348 ignore_PPP = FALSE; 349 return 0; 350} 351 352void 353libtop_fini(void) 354{ 355 libtop_pinfo_t *pinfo, *ppinfo; 356 357 /* Deallocate the arg string. */ 358 free(libtop_arg); 359 360 /* Clean up the oinfo structures. */ 361 CFRelease(libtop_oinfo_hash); 362 363 /* Clean up the pinfo structures. */ 364 rb_first(&libtop_ptree, pnode, pinfo); 365 for (; 366 pinfo != rb_tree_nil(&libtop_ptree); 367 pinfo = ppinfo) { 368 rb_next(&libtop_ptree, pinfo, libtop_pinfo_t, pnode, ppinfo); 369 370 /* This removes the pinfo from the tree, and frees pinfo and its data. */ 371 libtop_p_destroy_pinfo(pinfo); 372 } 373 374 /* Clean up the uid->username translation cache. */ 375 CFRelease(libtop_uhash); 376} 377 378/* 379 * Set the interval between framework updates. 380 */ 381int 382libtop_set_interval(uint32_t ival) 383{ 384 if (ival < 0 || ival > LIBTOP_MAX_INTERVAL) { 385 return -1; 386 } 387 interval = ival; 388 return 0; 389} 390 391/* Take a sample. */ 392int 393libtop_sample(boolean_t reg, boolean_t fw) 394{ 395 int res = 0; 396 397 /* Increment the sample sequence number. */ 398 tsamp.seq++; 399 400 /* 401 * Make a note that the results haven't been sorted (reset by 402 * libtop_psort()). 403 */ 404 libtop_sorted = FALSE; 405 libtop_piter = NULL; 406 407 /* Clear state breakdown. */ 408 memset(tsamp.state_breakdown, 0, sizeof(tsamp.state_breakdown)); 409 410 /* Get time. */ 411 if (tsamp.seq != 1) { 412 tsamp.p_time = tsamp.time; 413 res = gettimeofday(&tsamp.time, NULL); 414 } 415 416 if (res == 0) res = libtop_p_proc_table_read(reg); 417 if (res == 0) res = libtop_p_loadavg_update(); 418 419 /* Get CPU usage counters. */ 420 tsamp.p_cpu = tsamp.cpu; 421 if (res == 0) libtop_p_load_get((host_info_t)&tsamp.cpu); 422 423 if (res == 0) libtop_p_fw_sample(fw); 424 if (res == 0) libtop_p_vm_sample(); 425 if (res == 0) libtop_p_networks_sample(); 426 if (res == 0) libtop_p_disks_sample(); 427 428 return res; 429} 430 431/* Return a pointer to the structure that contains libtop-wide data. */ 432const libtop_tsamp_t * 433libtop_tsamp(void) 434{ 435 return &tsamp; 436} 437 438/* 439 * Given a tree of pinfo structures, create another tree that is sorted 440 * according to sort(). 441 */ 442void 443libtop_psort(libtop_sort_t *sort, void *data) 444{ 445 libtop_pinfo_t *pinfo, *ppinfo; 446 447 assert(tsamp.seq != 0); 448 449 /* Reset the iteration pointer. */ 450 libtop_piter = NULL; 451 452 /* Initialize the sorted tree. */ 453 rb_tree_new(&libtop_stree, snode); 454 455 /* Note that the results are sorted. */ 456 libtop_sorted = TRUE; 457 458 /* 459 * Set the sorting function and opaque data in preparation for building 460 * the sorted tree. 461 */ 462 libtop_sort = sort; 463 libtop_sort_data = data; 464 465 /* 466 * Iterate through ptree and insert the pinfo's into a sorted tree. 467 * At the same time, prune pinfo's that were associated with processes 468 * that were not found during the most recent sample. 469 */ 470 tsamp.nprocs = 0; 471 rb_first(&libtop_ptree, pnode, pinfo); 472 for (; 473 pinfo != rb_tree_nil(&libtop_ptree); 474 pinfo = ppinfo) { 475 /* 476 * Get the next pinfo before potentially removing this one from 477 * the tree. 478 */ 479 rb_next(&libtop_ptree, pinfo, libtop_pinfo_t, pnode, ppinfo); 480 481 if (pinfo->psamp.seq == tsamp.seq) { 482 /* Insert the pinfo into the sorted tree. */ 483 rb_node_new(&libtop_stree, pinfo, snode); 484 rb_insert(&libtop_stree, pinfo, libtop_p_pinfo_comp, 485 libtop_pinfo_t, snode); 486 487 tsamp.nprocs++; 488 } else { 489 /* The associated process has gone away. */ 490 libtop_p_destroy_pinfo(pinfo); 491 } 492 } 493} 494 495/* 496 * Iteratively return a pointer to each process that was in the most recent 497 * sample. The order depends on if/how libtop_psort() was called. 498 */ 499const libtop_psamp_t * 500libtop_piterate(void) 501{ 502 assert(tsamp.seq != 0); 503 504 if (libtop_sorted) { 505 /* Use the order set by libtop_psort(). */ 506 if (libtop_piter == NULL) { 507 rb_first(&libtop_stree, snode, libtop_piter); 508 } else { 509 rb_next(&libtop_stree, libtop_piter, libtop_pinfo_t, 510 snode, libtop_piter); 511 } 512 if (libtop_piter == rb_tree_nil(&libtop_stree)) { 513 libtop_piter = NULL; 514 } 515 } else { 516 boolean_t dead; 517 518 /* 519 * Return results in ascending pid order. Since dead processes 520 * weren't cleaned out by libtop_psamp(), take care to do so 521 * here on the fly. 522 */ 523 if (libtop_piter == NULL) { 524 rb_first(&libtop_ptree, pnode, libtop_piter); 525 } else { 526 rb_next(&libtop_ptree, libtop_piter, libtop_pinfo_t, 527 pnode, libtop_piter); 528 } 529 530 do { 531 dead = FALSE; 532 533 if (libtop_piter == rb_tree_nil(&libtop_ptree)) { 534 /* No more tree nodes. */ 535 libtop_piter = NULL; 536 break; 537 } 538 if (libtop_piter->psamp.seq != tsamp.seq) { 539 libtop_pinfo_t *pinfo; 540 541 /* 542 * Dead process. Get the next pinfo tree node 543 * before removing this one. 544 */ 545 pinfo = libtop_piter; 546 rb_next(&libtop_ptree, libtop_piter, 547 libtop_pinfo_t, pnode, libtop_piter); 548 549 libtop_p_destroy_pinfo(pinfo); 550 551 dead = TRUE; 552 } 553 } while (dead); 554 } 555 556 return &libtop_piter->psamp; 557} 558 559/* 560 * Set whether to collect memory region information for the process with pid 561 * pid. 562 */ 563int 564libtop_preg(pid_t pid, libtop_preg_t preg) 565{ 566 libtop_pinfo_t *pinfo; 567 568 pinfo = libtop_p_psearch(pid); 569 if (pinfo == NULL) return -1; 570 pinfo->preg = preg; 571 572 return 0; 573} 574 575/* Return a pointer to the username string associated with uid. */ 576const char * 577libtop_username(uid_t uid) 578{ 579 const void *k = (const void *)(uintptr_t)uid; 580 581 if (!CFDictionaryContainsKey(libtop_uhash, k)) { 582 struct passwd *pwd = getpwuid(uid); 583 if (pwd == NULL) 584 return NULL; 585 CFDictionarySetValue(libtop_uhash, k, pwd->pw_name); 586 } 587 588 return CFDictionaryGetValue(libtop_uhash, k); 589} 590 591/* Return a pointer to a string representation of a process state. */ 592const char * 593libtop_state_str(uint32_t state) 594{ 595 const char *strings[] = { 596 "zombie", 597#define LIBTOP_STATE_ZOMBIE 0 598 "running", 599#define LIBTOP_STATE_RUN 1 600 "stuck", 601#define LIBTOP_STATE_STUCK 2 602 "sleeping", 603#define LIBTOP_STATE_SLEEP 3 604 "idle", 605#define LIBTOP_STATE_IDLE 4 606 "stopped", 607#define LIBTOP_STATE_STOP 5 608 "halted", 609#define LIBTOP_STATE_HALT 6 610 "unknown" 611#define LIBTOP_STATE_UNKNOWN 7 612 }; 613 614 assert(LIBTOP_NSTATES == sizeof(strings) / sizeof(char *)); 615 assert(state <= LIBTOP_STATE_MAX); 616 assert(LIBTOP_STATE_MAXLEN >= 8); /* "sleeping" */ 617 618 return strings[state]; 619} 620 621/* 622 * Noop printing function, used when the user doesn't supply a printing 623 * function. 624 */ 625static boolean_t 626libtop_p_print(void *user_data, const char *format, ...) 627{ 628 /* Do nothing. */ 629 return 0; 630} 631 632/* Translate a mach state to a state in the state breakdown array. */ 633static int 634libtop_p_mach_state_order(int state, long sleeptime) 635{ 636 switch (state) { 637 case TH_STATE_RUNNING: 638 return LIBTOP_STATE_RUN; 639 case TH_STATE_UNINTERRUPTIBLE: 640 return LIBTOP_STATE_STUCK; 641 case TH_STATE_STOPPED: 642 return LIBTOP_STATE_STOP; 643 case TH_STATE_HALTED: 644 return LIBTOP_STATE_HALT; 645 case TH_STATE_WAITING: 646 return (sleeptime > 0) ? LIBTOP_STATE_IDLE : LIBTOP_STATE_SLEEP; 647 default: 648 return LIBTOP_STATE_UNKNOWN; 649 } 650} 651 652/* Get CPU load. */ 653static int 654libtop_p_load_get(host_info_t r_load) 655{ 656 kern_return_t kr; 657 mach_msg_type_number_t count; 658 659 count = HOST_CPU_LOAD_INFO_COUNT; 660 kr = host_statistics(libtop_port, HOST_CPU_LOAD_INFO, r_load, &count); 661 if (kr != KERN_SUCCESS) { 662 libtop_print(libtop_user_data, "Error in host_statistics(): %s", 663 mach_error_string(kr)); 664 return -1; 665 } 666 667 return 0; 668} 669 670/* Update load averages. */ 671static int 672libtop_p_loadavg_update(void) 673{ 674 double avg[3]; 675 676 if (getloadavg(avg, sizeof(avg)) < 0) { 677 libtop_print(libtop_user_data, "Error in getloadavg(): %s", 678 strerror(errno)); 679 return -1; 680 } 681 682 tsamp.loadavg[0] = avg[0]; 683 tsamp.loadavg[1] = avg[1]; 684 tsamp.loadavg[2] = avg[2]; 685 686 return 0; 687} 688 689/* 690 * Test whether the virtual address is within the architecture's shared region. 691 */ 692static bool 693in_shared_region(mach_vm_address_t addr, cpu_type_t type) { 694 mach_vm_address_t base = 0, size = 0; 695 696 switch(type) { 697 case CPU_TYPE_ARM: 698 base = SHARED_REGION_BASE_ARM; 699 size = SHARED_REGION_SIZE_ARM; 700 break; 701 702 703 case CPU_TYPE_X86_64: 704 base = SHARED_REGION_BASE_X86_64; 705 size = SHARED_REGION_SIZE_X86_64; 706 break; 707 708 case CPU_TYPE_I386: 709 base = SHARED_REGION_BASE_I386; 710 size = SHARED_REGION_SIZE_I386; 711 break; 712 713 case CPU_TYPE_POWERPC: 714 base = SHARED_REGION_BASE_PPC; 715 size = SHARED_REGION_SIZE_PPC; 716 break; 717 718 case CPU_TYPE_POWERPC64: 719 base = SHARED_REGION_BASE_PPC64; 720 size = SHARED_REGION_SIZE_PPC64; 721 break; 722 723 default: { 724 int t = type; 725 726 fprintf(stderr, "unknown CPU type: 0x%x\n", t); 727 abort(); 728 } 729 break; 730 } 731 732 733 return(addr >= base && addr < (base + size)); 734} 735 736/* Iterate through a given region of memory, adding up the various 737 submap regions found therein. Modifies tsamp. */ 738static void 739libtop_p_fw_scan(task_t task, mach_vm_address_t region_base, mach_vm_size_t region_size) { 740 mach_vm_size_t vsize = 0; 741 mach_vm_size_t code = 0; 742 mach_vm_size_t data = 0; 743 mach_vm_size_t linkedit = 0; 744 mach_vm_size_t pagesize = tsamp.pagesize; 745 746 mach_vm_address_t addr; 747 mach_vm_size_t size; 748 749 750 for (addr = region_base; addr < (region_base + region_size); addr += size) { 751 kern_return_t kr; 752 uint32_t depth = 1; 753 vm_region_submap_info_data_64_t sinfo; 754 mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64; 755 756 // Get the next submap in the specified region of memory 757 kr = mach_vm_region_recurse(task, &addr, &size, &depth, 758 (vm_region_recurse_info_t)&sinfo, &count); 759 if (kr != KERN_SUCCESS) break; 760 761 /* 762 * There should be no reason to test if addr is in a shared 763 * region, because the for loop limits the region space to the 764 * passed SHARED_REGION_BASE and SHARED_REGION_SIZE. 765 */ 766 767 vsize += size; 768 769 switch (sinfo.share_mode) { 770 case SM_SHARED: 771 case SM_COW: 772 case SM_TRUESHARED: 773 if (sinfo.max_protection & VM_PROT_EXECUTE) { 774 // CODE 775 code += sinfo.pages_resident; 776 tsamp.fw_count++; 777 } else if (sinfo.max_protection & VM_PROT_WRITE) { 778 // DATA 779 data += sinfo.pages_resident; 780 } else { 781 // LINKEDIT 782 linkedit += sinfo.pages_resident; 783 } 784 break; 785 } 786 } 787 788 tsamp.fw_vsize += vsize; 789 tsamp.fw_code += code * pagesize; 790 tsamp.fw_data += data * pagesize; 791 tsamp.fw_linkedit += linkedit * pagesize; 792} 793 794/* Sample framework memory statistics (if fw is TRUE). */ 795static void 796libtop_p_fw_sample(boolean_t fw) 797{ 798 if (!fw) return; 799 if ((interval != 1) && ((tsamp.seq % interval) != 1)) return; 800 801 tsamp.fw_count = 0; 802 tsamp.fw_code = 0; 803 tsamp.fw_data = 0; 804 tsamp.fw_linkedit = 0; 805 tsamp.fw_vsize = 0; 806 tsamp.fw_private = 0; 807 808#ifdef __arm__ 809 libtop_p_fw_scan(mach_task_self(), SHARED_REGION_BASE_ARM, SHARED_REGION_SIZE_ARM); 810#else 811 libtop_p_fw_scan(mach_task_self(), SHARED_REGION_BASE_PPC, SHARED_REGION_SIZE_PPC); 812 libtop_p_fw_scan(mach_task_self(), SHARED_REGION_BASE_PPC64, SHARED_REGION_SIZE_PPC64); 813 814 assert(SHARED_REGION_BASE_PPC <= SHARED_REGION_BASE_I386); 815 assert(SHARED_REGION_SIZE_PPC >= SHARED_REGION_SIZE_I386); 816 // Skip: i386 region is a subset of PPC region. 817 //libtop_p_fw_scan(mach_task_self(), SHARED_REGION_BASE_I386, SHARED_REGION_SIZE_I386); 818 819 assert(SHARED_REGION_BASE_PPC64 <= SHARED_REGION_BASE_X86_64); 820 assert(SHARED_REGION_SIZE_PPC64 >= SHARED_REGION_SIZE_X86_64); 821 // Skip: x86_64 region is a subset of ppc64 region. 822 //libtop_p_fw_scan(mach_task_self(), SHARED_REGION_BASE_X86_64, SHARED_REGION_SIZE_X86_64); 823#endif 824 825 // Iterate through all processes, collecting their individual fw stats 826 libtop_piter = NULL; 827 while (libtop_piterate()) { 828 tsamp.fw_private += libtop_piter->psamp.fw_private; 829 } 830} 831 832static int 833libtop_tsamp_update_swap_usage(libtop_tsamp_t* tsamp) { 834 int mib[2] = { CTL_VM, VM_SWAPUSAGE }; 835 int miblen = 2; 836 size_t len = sizeof(tsamp->xsu); 837 int res = sysctl(mib, miblen, &tsamp->xsu, &len, NULL, 0); 838 tsamp->xsu_is_valid = (res == 0); 839 return res; 840} 841 842/* This is for <rdar://problem/6410098>. */ 843static uint64_t 844round_down_wired(uint64_t value) { 845 return (value & ~((128 * 1024 * 1024ULL) - 1)); 846} 847 848/* This is for <rdar://problem/6410098>. */ 849static void 850update_pages_stolen(libtop_tsamp_t *tsamp) { 851 static int mib_reserved[CTL_MAXNAME]; 852 static int mib_unusable[CTL_MAXNAME]; 853 static int mib_other[CTL_MAXNAME]; 854 static size_t mib_reserved_len = 0; 855 static size_t mib_unusable_len = 0; 856 static size_t mib_other_len = 0; 857 int r; 858 859 tsamp->pages_stolen = 0; 860 861 /* This can be used for testing: */ 862 //tsamp->pages_stolen = (256 * 1024 * 1024ULL) / tsamp->pagesize; 863 864 if(0 == mib_reserved_len) { 865 mib_reserved_len = CTL_MAXNAME; 866 867 r = sysctlnametomib("machdep.memmap.Reserved", mib_reserved, 868 &mib_reserved_len); 869 870 if(-1 == r) { 871 mib_reserved_len = 0; 872 return; 873 } 874 875 mib_unusable_len = CTL_MAXNAME; 876 877 r = sysctlnametomib("machdep.memmap.Unusable", mib_unusable, 878 &mib_unusable_len); 879 880 if(-1 == r) { 881 mib_reserved_len = 0; 882 return; 883 } 884 885 886 mib_other_len = CTL_MAXNAME; 887 888 r = sysctlnametomib("machdep.memmap.Other", mib_other, 889 &mib_other_len); 890 891 if(-1 == r) { 892 mib_reserved_len = 0; 893 return; 894 } 895 } 896 897 if(mib_reserved_len > 0 && mib_unusable_len > 0 && mib_other_len > 0) { 898 uint64_t reserved = 0, unusable = 0, other = 0; 899 size_t reserved_len; 900 size_t unusable_len; 901 size_t other_len; 902 903 reserved_len = sizeof(reserved); 904 unusable_len = sizeof(unusable); 905 other_len = sizeof(other); 906 907 /* These are all declared as QUAD/uint64_t sysctls in the kernel. */ 908 909 if(-1 == sysctl(mib_reserved, mib_reserved_len, &reserved, 910 &reserved_len, NULL, 0)) { 911 return; 912 } 913 914 if(-1 == sysctl(mib_unusable, mib_unusable_len, &unusable, 915 &unusable_len, NULL, 0)) { 916 return; 917 } 918 919 if(-1 == sysctl(mib_other, mib_other_len, &other, 920 &other_len, NULL, 0)) { 921 return; 922 } 923 924 if(reserved_len == sizeof(reserved) 925 && unusable_len == sizeof(unusable) 926 && other_len == sizeof(other)) { 927 uint64_t stolen = reserved + unusable + other; 928 uint64_t mb128 = 128 * 1024 * 1024ULL; 929 930 if(stolen >= mb128) { 931 tsamp->pages_stolen = round_down_wired(stolen) / tsamp->pagesize; 932 } 933 } 934 } 935} 936 937 938 939static int 940libtop_tsamp_update_vm_stats(libtop_tsamp_t* tsamp) { 941 kern_return_t kr; 942 tsamp->p_vm_stat = tsamp->vm_stat; 943 944 mach_msg_type_number_t count = sizeof(tsamp->vm_stat) / sizeof(natural_t); 945 kr = host_statistics64(libtop_port, HOST_VM_INFO64, (host_info64_t)&tsamp->vm_stat, &count); 946 if (kr != KERN_SUCCESS) { 947 return kr; 948 } 949 950 if (tsamp->pages_stolen > 0) { 951 tsamp->vm_stat.wire_count += tsamp->pages_stolen; 952 } 953 954 // Check whether we got purgeable memory statistics 955 tsamp->purgeable_is_valid = (count == (sizeof(tsamp->vm_stat)/sizeof(natural_t))); 956 if (!tsamp->purgeable_is_valid) { 957 tsamp->vm_stat.purgeable_count = 0; 958 tsamp->vm_stat.purges = 0; 959 } 960 961 if (tsamp->seq == 1) { 962 tsamp->p_vm_stat = tsamp->vm_stat; 963 tsamp->b_vm_stat = tsamp->vm_stat; 964 } 965 966 return kr; 967} 968 969static void 970sum_rshrd(const void *key, const void *value, void *context) 971{ 972 libtop_oinfo_t *oinfo = (libtop_oinfo_t *)value; 973 mach_vm_size_t *rshrd = (mach_vm_size_t *)context; 974 975 switch (oinfo->share_type) { 976 case SM_SHARED: 977 case SM_COW: 978 *rshrd += oinfo->resident_page_count; 979 break; 980 } 981} 982 983/* Sample general VM statistics. */ 984static int 985libtop_p_vm_sample(void) 986{ 987 /* Get VM statistics. */ 988 libtop_tsamp_update_vm_stats(&tsamp); 989 990 /* Get swap usage */ 991 libtop_tsamp_update_swap_usage(&tsamp); 992 993 /* 994 * Iterate through the oinfo hash table and add up the collective size 995 * of the shared objects. 996 */ 997 998 mach_vm_size_t reg = 0; 999 mach_vm_size_t rprvt = 0; 1000 mach_vm_size_t rshrd = 0; 1001 mach_vm_size_t vsize = 0; 1002 1003 CFDictionaryApplyFunction(libtop_oinfo_hash, sum_rshrd, &rshrd); 1004 1005 /* Iterate through all processes, collecting their individual vm stats */ 1006 libtop_piter = NULL; 1007 while (libtop_piterate()) { 1008 reg += libtop_piter->psamp.reg; 1009 rprvt += libtop_piter->psamp.rprvt; 1010 vsize += libtop_piter->psamp.vsize; 1011 } 1012 1013 tsamp.reg = reg; 1014 tsamp.rprvt = rprvt; 1015 tsamp.rshrd = rshrd * tsamp.pagesize; 1016 tsamp.vsize = vsize; 1017 1018 return 0; 1019} 1020 1021/* 1022 * Sample network usage. 1023 * 1024 * The algorithm used does not deal with the following condition, which can 1025 * cause the statistics to be invalid: 1026 * 1027 * Interfaces are dynamic -- they can appear and disappear at any time. 1028 * There is no way to get statistics on an interface that has disappeared, so 1029 * it isn't possible to determine the amount of data transfer between the 1030 * previous sample and when the interface went away. 1031 * 1032 * Due to this problem, if an interface disappears, it is possible for the 1033 * current sample values to be lower than those of the beginning or previous 1034 * samples. 1035 */ 1036static void 1037libtop_p_networks_sample(void) 1038{ 1039 short network_layer; 1040 short link_layer; 1041 int mib[6]; 1042 char *buf = NULL, *lim, *next; 1043 size_t len; 1044 struct if_msghdr *ifm; 1045 1046 1047 tsamp.p_net_ipackets = tsamp.net_ipackets; 1048 tsamp.p_net_opackets = tsamp.net_opackets; 1049 tsamp.p_net_ibytes = tsamp.net_ibytes; 1050 tsamp.p_net_obytes = tsamp.net_obytes; 1051 1052 tsamp.net_ipackets = 0; 1053 tsamp.net_opackets = 0; 1054 tsamp.net_ibytes = 0; 1055 tsamp.net_obytes = 0; 1056 1057 mib[0] = CTL_NET; // networking subsystem 1058 mib[1] = PF_ROUTE; // type of information 1059 mib[2] = 0; // protocol (IPPROTO_xxx) 1060 mib[3] = 0; // address family 1061 mib[4] = NET_RT_IFLIST2; // operation 1062 mib[5] = 0; 1063 if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) return; 1064 if ((buf = malloc(len)) == NULL) { 1065 printf("malloc failed\n"); 1066 exit(1); 1067 } 1068 if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { 1069 if (buf) free(buf); 1070 return; 1071 } 1072 1073 lim = buf + len; 1074 for (next = buf; next < lim; ) { 1075 network_layer = link_layer = 0; 1076 ifm = (struct if_msghdr *)next; 1077 next += ifm->ifm_msglen; 1078 1079 if (ifm->ifm_type == RTM_IFINFO2) { 1080 struct if_msghdr2 *if2m = (struct if_msghdr2 *)ifm; 1081 1082 if(if2m->ifm_data.ifi_type==IFT_ETHER) /* If we've seen any ethernet traffic, */ 1083 ignore_PPP=TRUE; /* ignore any PPP traffic (PPPoE or VPN) */ 1084 1085 1086 if(/*We want to count them all in top.*/ 1 1087 || ((if2m->ifm_data.ifi_type!=IFT_LOOP) /* do not count loopback traffic */ 1088 && !(ignore_PPP && if2m->ifm_data.ifi_type==IFT_PPP))) { /* or VPN/PPPoE */ 1089 tsamp.net_opackets += if2m->ifm_data.ifi_opackets; 1090 tsamp.net_ipackets += if2m->ifm_data.ifi_ipackets; 1091 tsamp.net_obytes += if2m->ifm_data.ifi_obytes; 1092 tsamp.net_ibytes += if2m->ifm_data.ifi_ibytes; 1093 } 1094 } 1095 } 1096 1097 if (tsamp.seq == 1) { 1098 tsamp.b_net_ipackets = tsamp.net_ipackets; 1099 tsamp.p_net_ipackets = tsamp.net_ipackets; 1100 1101 tsamp.b_net_opackets = tsamp.net_opackets; 1102 tsamp.p_net_opackets = tsamp.net_opackets; 1103 1104 tsamp.b_net_ibytes = tsamp.net_ibytes; 1105 tsamp.p_net_ibytes = tsamp.net_ibytes; 1106 1107 tsamp.b_net_obytes = tsamp.net_obytes; 1108 tsamp.p_net_obytes = tsamp.net_obytes; 1109 } 1110 1111 free(buf); 1112} 1113 1114/* 1115 * Sample disk usage. The algorithm used has the same limitations as that used 1116 * for libtop_p_networks_sample(). 1117 */ 1118static int 1119libtop_p_disks_sample(void) 1120{ 1121 int retval; 1122 io_registry_entry_t drive; 1123 io_iterator_t drive_list; 1124 CFNumberRef number; 1125 CFDictionaryRef properties, statistics; 1126 UInt64 value; 1127 1128 /* Get the list of all drive objects. */ 1129 if (IOServiceGetMatchingServices(libtop_master_port, 1130 IOServiceMatching("IOBlockStorageDriver"), &drive_list)) { 1131 libtop_print(libtop_user_data, 1132 "Error in IOServiceGetMatchingServices()"); 1133 return -1; 1134 } 1135 1136 tsamp.p_disk_rops = tsamp.disk_rops; 1137 tsamp.p_disk_wops = tsamp.disk_wops; 1138 tsamp.p_disk_rbytes = tsamp.disk_rbytes; 1139 tsamp.p_disk_wbytes = tsamp.disk_wbytes; 1140 1141 tsamp.disk_rops = 0; 1142 tsamp.disk_wops = 0; 1143 tsamp.disk_rbytes = 0; 1144 tsamp.disk_wbytes = 0; 1145 while ((drive = IOIteratorNext(drive_list)) != 0) { 1146 number = 0; 1147 properties = 0; 1148 statistics = 0; 1149 value = 0; 1150 1151 /* Obtain the properties for this drive object. */ 1152 if (IORegistryEntryCreateCFProperties(drive, 1153 (CFMutableDictionaryRef *)&properties, kCFAllocatorDefault, 0)) { 1154 libtop_print(libtop_user_data, 1155 "Error in IORegistryEntryCreateCFProperties()"); 1156 retval = -1; 1157 goto RETURN; // We must use a goto here to clean up drive_list 1158 } 1159 1160 if (properties != 0) { 1161 /* Obtain the statistics from the drive properties. */ 1162 statistics 1163 = (CFDictionaryRef)CFDictionaryGetValue(properties, 1164 CFSTR(kIOBlockStorageDriverStatisticsKey)); 1165 1166 if (statistics != 0) { 1167 /* Get number of reads. */ 1168 number = 1169 (CFNumberRef)CFDictionaryGetValue(statistics, 1170 CFSTR(kIOBlockStorageDriverStatisticsReadsKey)); 1171 if (number != 0) { 1172 CFNumberGetValue(number, 1173 kCFNumberSInt64Type, &value); 1174 tsamp.disk_rops += value; 1175 } 1176 1177 /* Get bytes read. */ 1178 number = 1179 (CFNumberRef)CFDictionaryGetValue(statistics, 1180 CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey)); 1181 if (number != 0) { 1182 CFNumberGetValue(number, 1183 kCFNumberSInt64Type, &value); 1184 tsamp.disk_rbytes += value; 1185 } 1186 1187 /* Get number of writes. */ 1188 number = 1189 (CFNumberRef)CFDictionaryGetValue(statistics, 1190 CFSTR(kIOBlockStorageDriverStatisticsWritesKey)); 1191 if (number != 0) { 1192 CFNumberGetValue(number, 1193 kCFNumberSInt64Type, &value); 1194 tsamp.disk_wops += value; 1195 } 1196 1197 /* Get bytes written. */ 1198 number = 1199 (CFNumberRef)CFDictionaryGetValue(statistics, 1200 CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey)); 1201 if (number != 0) { 1202 CFNumberGetValue(number, 1203 kCFNumberSInt64Type, &value); 1204 tsamp.disk_wbytes += value; 1205 } 1206 } 1207 1208 /* Release. */ 1209 CFRelease(properties); 1210 } 1211 1212 /* Release. */ 1213 IOObjectRelease(drive); 1214 } 1215 IOIteratorReset(drive_list); 1216 if (tsamp.seq == 1) { 1217 tsamp.b_disk_rops = tsamp.disk_rops; 1218 tsamp.p_disk_rops = tsamp.disk_rops; 1219 1220 tsamp.b_disk_wops = tsamp.disk_wops; 1221 tsamp.p_disk_wops = tsamp.disk_wops; 1222 1223 tsamp.b_disk_rbytes = tsamp.disk_rbytes; 1224 tsamp.p_disk_rbytes = tsamp.disk_rbytes; 1225 1226 tsamp.b_disk_wbytes = tsamp.disk_wbytes; 1227 tsamp.p_disk_wbytes = tsamp.disk_wbytes; 1228 } 1229 1230 retval = 0; 1231 RETURN: 1232 /* Release. */ 1233 IOObjectRelease(drive_list); 1234 return retval; 1235} 1236 1237 1238/* Iterate through all processes and update their statistics. */ 1239static int 1240libtop_p_proc_table_read(boolean_t reg) 1241{ 1242 kern_return_t kr; 1243 processor_set_name_array_t psets; 1244 processor_set_t pset; 1245 task_array_t tasks; 1246 mach_msg_type_number_t i, j, pcnt, tcnt; 1247 bool fatal_error_occurred = false; 1248 1249 kr = host_processor_sets(libtop_port, &psets, &pcnt); 1250 if (kr != KERN_SUCCESS) { 1251 libtop_print(libtop_user_data, "Error in host_processor_sets(): %s", 1252 mach_error_string(kr)); 1253 return -1; 1254 } 1255 1256 for (i = 0; i < pcnt; i++) { 1257 kr = host_processor_set_priv(libtop_port, psets[i], &pset); 1258 if (kr != KERN_SUCCESS) { 1259 libtop_print(libtop_user_data, 1260 "Error in host_processor_set_priv(): %s", 1261 mach_error_string(kr)); 1262 return -1; 1263 } 1264 1265 kr = processor_set_tasks(pset, &tasks, &tcnt); 1266 if (kr != KERN_SUCCESS) { 1267 libtop_print(libtop_user_data, "Error in processor_set_tasks(): %s", 1268 mach_error_string(kr)); 1269 return -1; 1270 } 1271 1272 tsamp.reg = 0; 1273 tsamp.rprvt = 0; 1274 tsamp.vsize = 0; 1275 tsamp.threads = 0; 1276// libtop_p_oinfo_reset(); 1277 1278 for (j = 0; j < tcnt; j++) { 1279 switch(libtop_p_task_update(tasks[j], reg)) { 1280 case LIBTOP_ERR_INVALID: 1281 /* 1282 * The task became invalid. 1283 * The called function takes care of destroying the pinfo in the tree, if needed. 1284 */ 1285 break; 1286 1287 case LIBTOP_ERR_ALLOC: 1288 fatal_error_occurred = true; 1289 break; 1290 } 1291 1292 mach_port_deallocate(mach_task_self(), tasks[j]); 1293 } 1294 1295 kr = mach_vm_deallocate(mach_task_self(), (mach_vm_address_t)(uintptr_t)tasks, tcnt * sizeof(*tasks)); 1296 kr = mach_port_deallocate(mach_task_self(), pset); 1297 kr = mach_port_deallocate(mach_task_self(), psets[i]); 1298 } 1299 1300 kr = mach_vm_deallocate(mach_task_self(), (mach_vm_address_t)(uintptr_t)psets, pcnt * sizeof(*psets)); 1301 1302 return (fatal_error_occurred) ? -1 : 0; 1303} 1304 1305/* 1306 * Return the CPU type of the process. 1307 */ 1308static libtop_status_t 1309libtop_p_cputype(pid_t pid, cpu_type_t *cputype) { 1310 int res = -1; 1311 static int mib[CTL_MAXNAME]; 1312 static size_t miblen = 0; 1313 1314 *cputype = 0; 1315 1316 if (miblen == 0) { 1317 miblen = CTL_MAXNAME; 1318 res = sysctlnametomib("sysctl.proc_cputype", mib, &miblen); 1319 if (res != 0) { 1320 miblen = 0; 1321 } 1322 } 1323 1324 if (miblen > 0) { 1325 mib[miblen] = pid; 1326 size_t len = sizeof(*cputype); 1327 res = sysctl(mib, miblen + 1, cputype, &len, NULL, 0); 1328 } 1329 1330 /* res will be 0 if the sysctl was successful. */ 1331 return (res == 0) ? LIBTOP_NO_ERR : LIBTOP_ERR_INVALID; 1332} 1333 1334/* 1335 * Return the shared region size for an architecture. 1336 */ 1337static mach_vm_size_t 1338libtop_p_shreg_size(cpu_type_t type) { 1339 switch(type) 1340 { 1341 case CPU_TYPE_ARM: return SHARED_REGION_SIZE_ARM; 1342 case CPU_TYPE_POWERPC: return SHARED_REGION_SIZE_PPC; 1343 case CPU_TYPE_POWERPC64: return SHARED_REGION_SIZE_PPC64; 1344 case CPU_TYPE_I386: return SHARED_REGION_SIZE_I386; 1345 case CPU_TYPE_X86_64: return SHARED_REGION_SIZE_X86_64; 1346 default: return 0; 1347 } 1348} 1349 1350static int __attribute__((noinline)) 1351kinfo_for_pid(struct kinfo_proc* kinfo, pid_t pid) { 1352 size_t miblen = 4, len; 1353 int mib[miblen]; 1354 int res; 1355 1356 mib[0] = CTL_KERN; 1357 mib[1] = KERN_PROC; 1358 mib[2] = KERN_PROC_PID; 1359 mib[3] = pid; 1360 len = sizeof(struct kinfo_proc); 1361 res = sysctl(mib, miblen, kinfo, &len, NULL, 0); 1362 if (res != 0) { 1363 libtop_print(libtop_user_data, "%s(): Error in sysctl(): %s", 1364 __FUNCTION__, strerror(errno)); 1365 } 1366 return res; 1367} 1368 1369static kern_return_t 1370libtop_pinfo_update_mach_ports(task_t task, libtop_pinfo_t* pinfo) { 1371 kern_return_t kr; 1372 mach_msg_type_number_t ncnt, tcnt; 1373 mach_port_name_array_t names; 1374 mach_port_type_array_t types; 1375 1376 pinfo->psamp.p_prt = pinfo->psamp.prt; 1377 1378 kr = mach_port_names(task, &names, &ncnt, &types, &tcnt); 1379 if (kr != KERN_SUCCESS) return 0; 1380 1381 pinfo->psamp.prt = ncnt; 1382 1383 kr = mach_vm_deallocate(mach_task_self(), (mach_vm_address_t)(uintptr_t)names, ncnt * sizeof(*names)); 1384 kr = mach_vm_deallocate(mach_task_self(), (mach_vm_address_t)(uintptr_t)types, tcnt * sizeof(*types)); 1385 1386 return kr; 1387} 1388 1389static kern_return_t 1390libtop_pinfo_update_events_info(task_t task, libtop_pinfo_t* pinfo) { 1391 kern_return_t kr; 1392 mach_msg_type_number_t count = TASK_EVENTS_INFO_COUNT; 1393 1394 pinfo->psamp.p_events = pinfo->psamp.events; 1395 1396 pinfo->psamp.faults.previous = pinfo->psamp.faults.now; 1397 pinfo->psamp.pageins.previous = pinfo->psamp.pageins.now; 1398 pinfo->psamp.cow_faults.previous = pinfo->psamp.cow_faults.now; 1399 pinfo->psamp.messages_sent.previous = pinfo->psamp.messages_sent.now; 1400 pinfo->psamp.messages_recv.previous = pinfo->psamp.messages_recv.now; 1401 pinfo->psamp.syscalls_mach.previous = pinfo->psamp.syscalls_mach.now; 1402 pinfo->psamp.syscalls_bsd.previous = pinfo->psamp.syscalls_bsd.now; 1403 pinfo->psamp.csw.previous = pinfo->psamp.csw.now; 1404 1405 kr = task_info(task, TASK_EVENTS_INFO, (task_info_t)&pinfo->psamp.events, &count); 1406 if (kr != KERN_SUCCESS) return kr; 1407 1408 1409#if 0 1410 if(pinfo->psamp.pid == 0) { 1411 /* Used for testing libtop_i64_*(). */ 1412 static int i; 1413 pinfo->psamp.events.faults = ((INT_MAX - pinfo->psamp.events.faults) - 20000) + i; 1414 1415 /* This tests the libtop_i64_init initial overflow behavior. */ 1416 pinfo->psamp.events.csw += INT_MAX; 1417 1418 i += INT_MAX / 2; 1419 } 1420#endif 1421 1422 if (pinfo->psamp.p_seq == 0) { 1423 pinfo->psamp.b_events = pinfo->psamp.events; 1424 pinfo->psamp.p_events = pinfo->psamp.events; 1425 1426 1427 pinfo->psamp.faults.i64 = libtop_i64_init(0, pinfo->psamp.events.faults); 1428 pinfo->psamp.pageins.i64 = libtop_i64_init(0, pinfo->psamp.events.pageins); 1429 pinfo->psamp.cow_faults.i64 = libtop_i64_init(0, pinfo->psamp.events.cow_faults); 1430 pinfo->psamp.messages_sent.i64 = libtop_i64_init(0, pinfo->psamp.events.messages_sent); 1431 pinfo->psamp.messages_recv.i64 = libtop_i64_init(0, pinfo->psamp.events.messages_received); 1432 pinfo->psamp.syscalls_mach.i64 = libtop_i64_init(0, pinfo->psamp.events.syscalls_mach); 1433 pinfo->psamp.syscalls_bsd.i64 = libtop_i64_init(0, pinfo->psamp.events.syscalls_unix); 1434 pinfo->psamp.csw.i64 = libtop_i64_init(0, pinfo->psamp.events.csw); 1435 } 1436 1437 libtop_i64_update(&pinfo->psamp.faults.i64, pinfo->psamp.events.faults); 1438 libtop_i64_update(&pinfo->psamp.pageins.i64, pinfo->psamp.events.pageins); 1439 libtop_i64_update(&pinfo->psamp.cow_faults.i64, pinfo->psamp.events.cow_faults); 1440 libtop_i64_update(&pinfo->psamp.messages_sent.i64, pinfo->psamp.events.messages_sent); 1441 libtop_i64_update(&pinfo->psamp.messages_recv.i64, pinfo->psamp.events.messages_received); 1442 libtop_i64_update(&pinfo->psamp.syscalls_mach.i64, pinfo->psamp.events.syscalls_mach); 1443 libtop_i64_update(&pinfo->psamp.syscalls_bsd.i64, pinfo->psamp.events.syscalls_unix); 1444 libtop_i64_update(&pinfo->psamp.csw.i64, pinfo->psamp.events.csw); 1445 1446 pinfo->psamp.faults.now = libtop_i64_value(&pinfo->psamp.faults.i64); 1447 pinfo->psamp.pageins.now = libtop_i64_value(&pinfo->psamp.pageins.i64); 1448 pinfo->psamp.cow_faults.now = libtop_i64_value(&pinfo->psamp.cow_faults.i64); 1449 pinfo->psamp.messages_sent.now = libtop_i64_value(&pinfo->psamp.messages_sent.i64); 1450 pinfo->psamp.messages_recv.now = libtop_i64_value(&pinfo->psamp.messages_recv.i64); 1451 pinfo->psamp.syscalls_mach.now = libtop_i64_value(&pinfo->psamp.syscalls_mach.i64); 1452 pinfo->psamp.syscalls_bsd.now = libtop_i64_value(&pinfo->psamp.syscalls_bsd.i64); 1453 pinfo->psamp.csw.now = libtop_i64_value(&pinfo->psamp.csw.i64); 1454 1455 return kr; 1456} 1457 1458static kern_return_t 1459libtop_pinfo_update_kernmem_info(task_t task, libtop_pinfo_t* pinfo) { 1460 kern_return_t kr; 1461 1462 mach_msg_type_number_t count = TASK_KERNELMEMORY_INFO_COUNT; 1463 1464 pinfo->psamp.p_palloc = pinfo->psamp.palloc; 1465 pinfo->psamp.p_pfree = pinfo->psamp.pfree; 1466 pinfo->psamp.p_salloc = pinfo->psamp.salloc; 1467 pinfo->psamp.p_sfree = pinfo->psamp.sfree; 1468 1469 kr = task_info(task, TASK_KERNELMEMORY_INFO, (task_info_t)&pinfo->psamp.palloc, &count); 1470 return kr; 1471} 1472 1473static kern_return_t 1474libtop_pinfo_update_power_info(task_t task, libtop_pinfo_t *pinfo) 1475{ 1476 kern_return_t kr; 1477 mach_msg_type_number_t count = TASK_POWER_INFO_COUNT; 1478 1479 pinfo->psamp.p_power = pinfo->psamp.power; 1480 1481 kr = task_info(task, TASK_POWER_INFO, (task_info_t)&pinfo->psamp.power, &count); 1482 if (kr != KERN_SUCCESS) return kr; 1483 1484 if (pinfo->psamp.p_seq == 0) { 1485 pinfo->psamp.b_power = pinfo->psamp.power; 1486 pinfo->psamp.p_power = pinfo->psamp.power; 1487 } 1488 1489 return kr; 1490} 1491 1492#ifndef TASK_VM_INFO_PURGEABLE 1493// cribbed from sysmond 1494static uint64_t 1495sum_vm_purgeable_info(const vm_purgeable_info_t info) 1496{ 1497 uint64_t sum = 0; 1498 int i; 1499 1500 for (i = 0; i < 8; i++) { 1501 sum += info->fifo_data[i].size; 1502 } 1503 sum += info->obsolete_data.size; 1504 for (i = 0; i < 8; i++) { 1505 sum += info->lifo_data[i].size; 1506 } 1507 1508 return sum; 1509} 1510#endif /* !TASK_VM_INFO_PURGEABLE */ 1511 1512static kern_return_t 1513libtop_pinfo_update_vm_info(task_t task, libtop_pinfo_t *pinfo) 1514{ 1515 kern_return_t kr; 1516#ifndef TASK_VM_INFO_PURGEABLE 1517 task_purgable_info_t purgeable_info; 1518 uint64_t purgeable_sum = 0; 1519#endif /* !TASK_VM_INFO_PURGEABLE */ 1520 mach_msg_type_number_t info_count; 1521 task_vm_info_data_t vm_info; 1522 1523 pinfo->psamp.p_purgeable = pinfo->psamp.purgeable; 1524 pinfo->psamp.p_anonymous = pinfo->psamp.anonymous; 1525 pinfo->psamp.p_compressed = pinfo->psamp.compressed; 1526 1527#ifndef TASK_VM_INFO_PURGEABLE 1528 kr = task_purgable_info(task, &purgeable_info); 1529 if (kr == KERN_SUCCESS) { 1530 purgeable_sum = sum_vm_purgeable_info(&purgeable_info); 1531 pinfo->psamp.purgeable = purgeable_sum; 1532 } 1533#endif /* !TASK_VM_INFO_PURGEABLE */ 1534 1535 info_count = TASK_VM_INFO_COUNT; 1536#ifdef TASK_VM_INFO_PURGEABLE 1537 kr = task_info(task, TASK_VM_INFO_PURGEABLE, (task_info_t)&vm_info, &info_count); 1538#else 1539 kr = task_info(task, TASK_VM_INFO, (task_info_t)&vm_info, &info_count); 1540#endif 1541 if (kr == KERN_SUCCESS) { 1542#ifdef TASK_VM_INFO_PURGEABLE 1543 pinfo->psamp.purgeable = vm_info.purgeable_volatile_resident; 1544 pinfo->psamp.anonymous = vm_info.internal - vm_info.purgeable_volatile_pmap; 1545#else 1546 if (purgeable_sum < vm_info.internal) { 1547 pinfo->psamp.anonymous = vm_info.internal - purgeable_sum; 1548 } else { 1549 /* radar:13816348 */ 1550 pinfo->psamp.anonymous = 0; 1551 } 1552#endif 1553 pinfo->psamp.compressed = vm_info.compressed; 1554 } 1555 1556 return kr; 1557} 1558 1559static kern_return_t 1560libtop_pinfo_update_cpu_usage(task_t task, libtop_pinfo_t* pinfo, int *state) { 1561 kern_return_t kr; 1562 thread_act_array_t threads; 1563 mach_msg_type_number_t tcnt; 1564 1565 // Update thread status 1566 1567 *state = LIBTOP_STATE_MAX; 1568 pinfo->psamp.state = LIBTOP_STATE_MAX; 1569 1570 kr = task_threads(task, &threads, &tcnt); 1571 if (kr != KERN_SUCCESS) return kr; 1572 1573 if(tsamp.seq > 1) { 1574 pinfo->psamp.p_th = pinfo->psamp.th; 1575 pinfo->psamp.p_running_th = pinfo->psamp.running_th; 1576 } else { 1577 pinfo->psamp.p_th = 0; 1578 pinfo->psamp.p_running_th = 0; 1579 } 1580 1581 pinfo->psamp.th = tcnt; 1582 tsamp.threads += tcnt; 1583 1584 pinfo->psamp.running_th = 0; 1585 1586 int i; 1587 for (i = 0; i < tcnt; i++) { 1588 thread_basic_info_data_t info; 1589 mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT; 1590 1591 kr = thread_info(threads[i], THREAD_BASIC_INFO, (thread_info_t)&info, &count); 1592 if (kr != KERN_SUCCESS) continue; 1593 1594 if ((info.flags & TH_FLAGS_IDLE) == 0) { 1595 struct timeval tv; 1596 TIME_VALUE_TO_TIMEVAL(&info.user_time, &tv); 1597 timeradd(&pinfo->psamp.total_time, &tv, &pinfo->psamp.total_time); 1598 TIME_VALUE_TO_TIMEVAL(&info.system_time, &tv); 1599 timeradd(&pinfo->psamp.total_time, &tv, &pinfo->psamp.total_time); 1600 } 1601 1602 if(info.run_state == TH_STATE_RUNNING) { 1603 pinfo->psamp.running_th++; 1604 } 1605 1606 uint32_t tstate; 1607 tstate = libtop_p_mach_state_order(info.run_state, info.sleep_time); 1608 if (tstate < *state) { 1609 *state = tstate; 1610 pinfo->psamp.state = tstate; 1611 } 1612 1613 kr = mach_port_deallocate(mach_task_self(), threads[i]); 1614 } 1615 kr = mach_vm_deallocate(mach_task_self(), (mach_vm_address_t)(uintptr_t)threads, tcnt * sizeof(*threads)); 1616 return kr; 1617} 1618 1619#ifdef TOP_DEBUG_VM 1620static char* 1621share_mode_to_string(int share_mode) { 1622 switch(share_mode) { 1623 case SM_COW: return "COW"; 1624 case SM_PRIVATE: return "PRV"; 1625 case SM_EMPTY: return "NUL"; 1626 case SM_SHARED: return "ALI"; 1627 default: return "???"; 1628 } 1629} 1630#endif 1631 1632static kern_return_t 1633libtop_update_vm_regions(task_t task, libtop_pinfo_t* pinfo) { 1634 kern_return_t kr; 1635 1636 mach_vm_size_t rprvt = 0; 1637 mach_vm_size_t vprvt = 0; 1638 mach_vm_size_t rshrd = 0; 1639 mach_vm_size_t empty = 0; 1640 mach_vm_size_t aliased = 0; 1641 mach_vm_size_t fw_private = 0; 1642 mach_vm_size_t pagesize = tsamp.pagesize; 1643 mach_vm_size_t regs = 0; 1644 1645 mach_vm_address_t addr; 1646 mach_vm_size_t size; 1647 1648 libtop_oinfo_t *oinfo; 1649 1650 pinfo->split = FALSE; 1651 1652#ifdef TOP_DEBUG_VM 1653 fprintf(stderr, "pid %d\n", pinfo->psamp.pid); 1654 fprintf(stderr, "range\tsize\tprivate\tshared\trc\toid\tmode\tvprvt\trprvt\trshrd\tempty\taliased\n"); 1655#endif 1656 1657 for (addr = 0; ; addr += size) { 1658 vm_region_top_info_data_t info; 1659 mach_msg_type_number_t count = VM_REGION_TOP_INFO_COUNT; 1660 mach_port_t object_name; 1661 1662 kr = mach_vm_region(task, &addr, &size, VM_REGION_TOP_INFO, (vm_region_info_t)&info, &count, &object_name); 1663 if (kr != KERN_SUCCESS) break; 1664 1665#ifdef TOP_DEBUG_VM 1666 fprintf(stderr, "%016llx-%016llx\t%lld\t%d\t%d\t%d\t%d\t%s", 1667 addr, addr+size, size, info.private_pages_resident, info.shared_pages_resident, info.ref_count, info.obj_id, share_mode_to_string(info.share_mode)); 1668#endif 1669 1670 if (in_shared_region(addr, pinfo->psamp.cputype)) { 1671 // Private Shared 1672 fw_private += info.private_pages_resident * pagesize; 1673 1674 /* 1675 * Check if this process has the globally shared 1676 * text and data regions mapped in. If so, set 1677 * pinfo->split to TRUE and avoid checking 1678 * again. 1679 */ 1680 if (pinfo->split == FALSE && info.share_mode == SM_EMPTY) { 1681 vm_region_basic_info_data_64_t b_info; 1682 mach_vm_address_t b_addr = addr; 1683 mach_vm_size_t b_size = size; 1684 count = VM_REGION_BASIC_INFO_COUNT_64; 1685 1686 kr = mach_vm_region(task, &b_addr, &b_size, VM_REGION_BASIC_INFO, (vm_region_info_t)&b_info, &count, &object_name); 1687 if (kr != KERN_SUCCESS) break; 1688 1689 if (b_info.reserved) { 1690 pinfo->split = TRUE; 1691 } 1692 } 1693 1694 /* 1695 * Short circuit the loop if this isn't a shared 1696 * private region, since that's the only region 1697 * type we care about within the current address 1698 * range. 1699 */ 1700 if (info.share_mode != SM_PRIVATE) { 1701#ifdef TOP_DEBUG_VM 1702 fprintf(stderr, "\n"); 1703#endif 1704 continue; 1705 } 1706 } 1707 1708 regs++; 1709 1710 /* 1711 * Update counters according to the region type. 1712 */ 1713 1714 if (info.share_mode == SM_COW && info.ref_count == 1) { 1715 // Treat single reference SM_COW as SM_PRIVATE 1716 info.share_mode = SM_PRIVATE; 1717 } 1718 1719 switch (info.share_mode) { 1720 case SM_LARGE_PAGE: 1721 // Treat SM_LARGE_PAGE the same as SM_PRIVATE 1722 // since they are not shareable and are wired. 1723 case SM_PRIVATE: 1724 rprvt += info.private_pages_resident * pagesize; 1725 rprvt += info.shared_pages_resident * pagesize; 1726 vprvt += size; 1727 break; 1728 1729 case SM_EMPTY: 1730 empty += size; 1731 break; 1732 1733 case SM_COW: 1734 case SM_SHARED: 1735 if (pinfo->psamp.pid == 0) { 1736 // Treat kernel_task specially 1737 if (info.share_mode == SM_COW) { 1738 rprvt += info.private_pages_resident * pagesize; 1739 vprvt += size; 1740 } 1741 break; 1742 } 1743 1744 oinfo = libtop_p_oinfo_insert(info.obj_id, info.share_mode, info.shared_pages_resident, info.ref_count, size, pinfo); 1745 if (!oinfo) { 1746 return -1; 1747 } 1748 1749 // roll back if necessary 1750 if (oinfo->proc_ref_count > 1) { 1751 if (oinfo->rb_share_type != SM_EMPTY) { 1752 oinfo->share_type = oinfo->rb_share_type; 1753 } 1754 aliased -= oinfo->rb_aliased; 1755 vprvt -= oinfo->rb_vprvt; 1756 rshrd -= oinfo->rb_rshrd; 1757 } 1758 // clear rollback fields 1759 oinfo->rb_share_type = SM_EMPTY; 1760 oinfo->rb_aliased = 0; 1761 oinfo->rb_vprvt = 0; 1762 oinfo->rb_rshrd = 0; 1763 1764 // XXX: SM_SHARED make sense here for the SM_COW case? 1765 if (oinfo->share_type == SM_SHARED && oinfo->ref_count == oinfo->proc_ref_count) { 1766 // Private aliased object 1767 oinfo->rb_share_type = oinfo->share_type; 1768 oinfo->share_type = SM_PRIVATE_ALIASED; 1769 1770 oinfo->rb_aliased += oinfo->resident_page_count * pagesize; 1771 aliased += oinfo->resident_page_count * pagesize; 1772 1773 oinfo->rb_vprvt += oinfo->size; 1774 vprvt += oinfo->size; 1775 } 1776 1777 if (oinfo->share_type != SM_PRIVATE_ALIASED) { 1778 oinfo->rb_rshrd += oinfo->resident_page_count * pagesize; 1779 rshrd += oinfo->resident_page_count * pagesize; 1780 } 1781 1782 if (info.share_mode == SM_COW) { 1783 rprvt += info.private_pages_resident * pagesize; 1784 vprvt += info.private_pages_resident * pagesize; // XXX: size? 1785 } 1786 break; 1787 1788 default: 1789 assert(0); 1790 break; 1791 } 1792#ifdef TOP_DEBUG_VM 1793 fprintf(stderr, "\t%lld\t%lld\t%lld\t%lld\t%lld\n", vprvt, rprvt, rshrd, empty, aliased); 1794#endif 1795 } 1796 1797 pinfo->psamp.rprvt = rprvt; 1798 pinfo->psamp.vprvt = vprvt; 1799 pinfo->psamp.rshrd = rshrd; 1800 pinfo->psamp.empty = empty; 1801 pinfo->psamp.rprvt += aliased; 1802 pinfo->psamp.fw_private = fw_private; 1803 1804 if(tsamp.seq > 1) { 1805 pinfo->psamp.p_reg = pinfo->psamp.reg; 1806 } else { 1807 pinfo->psamp.p_reg = 0; 1808 } 1809 pinfo->psamp.reg = regs; 1810 1811 return kr; 1812} 1813 1814/* 1815 * This may destroy the pinfo associated with a pid/task. 1816 * The caller should not make assumptions about the lifetime of the pinfo. 1817 */ 1818static libtop_status_t __attribute__((noinline)) 1819libtop_p_task_update(task_t task, boolean_t reg) 1820{ 1821 kern_return_t kr; 1822 int res; 1823 struct kinfo_proc kinfo; 1824 pid_t pid; 1825 mach_msg_type_number_t count; 1826 libtop_pinfo_t *pinfo; 1827 struct task_basic_info_64 ti; 1828 int state; 1829 1830 state = LIBTOP_STATE_ZOMBIE; 1831 1832 /* Get pid for this task. */ 1833 kr = pid_for_task(task, &pid); 1834 if (kr != KERN_SUCCESS) { 1835 return LIBTOP_ERR_INVALID; 1836 } 1837 1838 res = kinfo_for_pid(&kinfo, pid); 1839 if (res != 0) { 1840 return LIBTOP_ERR_INVALID; 1841 } 1842 1843 if (kinfo.kp_proc.p_stat == SZOMB) { 1844 /* Zombie process. */ 1845 } 1846 1847 /* 1848 * Search for the process. If we haven't seen it before, allocate and 1849 * insert a new pinfo structure. 1850 */ 1851 pinfo = libtop_p_psearch((pid_t)pid); 1852 if (pinfo == NULL) { 1853 pinfo = (libtop_pinfo_t *)calloc(1, sizeof(libtop_pinfo_t)); 1854 if (pinfo == NULL) { 1855 return LIBTOP_ERR_ALLOC; 1856 } 1857 pinfo->psamp.pid = (pid_t)pid; 1858 libtop_p_pinsert(pinfo); 1859 } 1860 1861 /* 1862 * Get command name/args. 1863 * This is expected to return a LIBTOP_ERR* or LIBTOP_NO_ERR (when successful). 1864 */ 1865 res = libtop_p_proc_command(pinfo, &kinfo); 1866 switch(res) { 1867 case LIBTOP_NO_ERR: 1868 break; 1869 1870 case LIBTOP_ERR_INVALID: 1871 /* 1872 * Something failed while fetching the command string (the pid is probably gone). 1873 * Remove the pinfo struct from the tree, and free it with destroy_pinfo. 1874 */ 1875 libtop_p_destroy_pinfo(pinfo); 1876 return res; 1877 1878 case LIBTOP_ERR_ALLOC: 1879 return res; 1880 } 1881 1882 pinfo->psamp.uid = kinfo.kp_eproc.e_ucred.cr_uid; 1883 pinfo->psamp.ppid = kinfo.kp_eproc.e_ppid; 1884 pinfo->psamp.pgrp = kinfo.kp_eproc.e_pgid; 1885 pinfo->flag = kinfo.kp_proc.p_flag; 1886 pinfo->psamp.started = kinfo.kp_proc.p_starttime; 1887 1888 pinfo->psamp.p_seq = pinfo->psamp.seq; 1889 pinfo->psamp.seq = tsamp.seq; 1890 1891 /* 1892 * 6255752: CPU type of a process can change due to a re-exec. 1893 * We may see some transient oddities due to this behavior, but 1894 * it should stabilize after one refresh. 1895 */ 1896 if(LIBTOP_NO_ERR != libtop_p_cputype(pinfo->psamp.pid, &pinfo->psamp.cputype)) { 1897 /* 1898 * This pid is most likely invalid now, because the 1899 * cputype couldn't be found. 1900 * 1901 */ 1902 1903 libtop_p_destroy_pinfo(pinfo); 1904 return LIBTOP_ERR_INVALID; 1905 } 1906 1907 /* 1908 * Get task_info, which is used for memory usage and CPU usage 1909 * statistics. 1910 */ 1911 count = TASK_BASIC_INFO_64_COUNT; 1912 kr = task_info(task, TASK_BASIC_INFO_64, (task_info_t)&ti, &count); 1913 if (kr != KERN_SUCCESS) { 1914 libtop_p_destroy_pinfo(pinfo); 1915 return LIBTOP_ERR_INVALID; 1916 } 1917 1918 /* 1919 * Get memory usage statistics. 1920 */ 1921 1922 /* Make copies of previous sample values. */ 1923 pinfo->psamp.p_rsize = pinfo->psamp.rsize; 1924 pinfo->psamp.p_vsize = pinfo->psamp.vsize; 1925 pinfo->psamp.p_rprvt = pinfo->psamp.rprvt; 1926 pinfo->psamp.p_vprvt = pinfo->psamp.vprvt; 1927 pinfo->psamp.p_rshrd = pinfo->psamp.rshrd; 1928 pinfo->psamp.p_empty = pinfo->psamp.empty; 1929 1930 /* Clear sizes in preparation for determining their current values. */ 1931 //pinfo->psamp.rprvt = 0; 1932 //pinfo->psamp.vprvt = 0; 1933 //pinfo->psamp.rshrd = 0; 1934 //pinfo->psamp.empty = 0; 1935 //pinfo->psamp.reg = 0; 1936 //pinfo->psamp.fw_private = 0; 1937 1938 /* 1939 * Do memory object traversal if any of the following is true: 1940 * 1941 * 1) Region reporting is enabled for this sample, and it isn't 1942 * explicitly disabled for this process. 1943 * 1944 * 2) Region reporting is explicitly enabled for this process. 1945 * 1946 * 3) This is the first sample for this process. 1947 * 1948 * 4) A previous sample detected that the globally shared text and data 1949 * segments were mapped in, but if we were to subtract them out, 1950 * the process's calculated vsize would be less than 0. 1951 */ 1952 1953 if ((reg && pinfo->preg != LIBTOP_PREG_off) 1954 || pinfo->preg == LIBTOP_PREG_on 1955 || pinfo->psamp.p_seq == 0 1956 || (pinfo->split && ti.virtual_size 1957 < libtop_p_shreg_size(pinfo->psamp.cputype))) { 1958 1959 libtop_update_vm_regions(task, pinfo); 1960 } 1961 1962 /* 1963 * These need to be copied regardless of the region reporting above. 1964 * The previous (p_) values were copied earlier. 1965 */ 1966 pinfo->psamp.rsize = ti.resident_size; 1967 pinfo->psamp.vsize = ti.virtual_size; 1968 1969 // Update total time. 1970 pinfo->psamp.p_total_time = pinfo->psamp.total_time; 1971 1972 struct timeval tv; 1973 TIME_VALUE_TO_TIMEVAL(&ti.user_time, &pinfo->psamp.total_time); 1974 TIME_VALUE_TO_TIMEVAL(&ti.system_time, &tv); 1975 timeradd(&pinfo->psamp.total_time, &tv, &pinfo->psamp.total_time); 1976 1977 /* 1978 * Get CPU usage statistics. 1979 */ 1980 kr = libtop_pinfo_update_cpu_usage(task, pinfo, &state); 1981 1982 if (pinfo->psamp.p_seq == 0) { 1983 /* Set initial values. */ 1984 pinfo->psamp.b_total_time = pinfo->psamp.total_time; 1985 pinfo->psamp.p_total_time = pinfo->psamp.total_time; 1986 } 1987 1988 /* 1989 * Get number of Mach ports. 1990 */ 1991 kr = libtop_pinfo_update_mach_ports(task, pinfo); 1992 1993 /* 1994 * Get event counters. 1995 */ 1996 kr = libtop_pinfo_update_events_info(task, pinfo); 1997 1998 /* 1999 * Get updated wired memory usage numbers 2000 */ 2001 kr = libtop_pinfo_update_kernmem_info(task, pinfo); 2002 2003 /* 2004 * Get power info (platform idle wakeups) 2005 */ 2006 kr = libtop_pinfo_update_power_info(task, pinfo); 2007 2008 /* 2009 * Get VM info (anonymous, purgeable, and compressed memory). 2010 */ 2011 kr = libtop_pinfo_update_vm_info(task, pinfo); 2012 2013 libtop_p_wq_update(pinfo); 2014 2015 tsamp.state_breakdown[state]++; 2016 2017 return LIBTOP_NO_ERR; 2018} 2019 2020/* 2021 * Get the command name for the process associated with pinfo. For CFM 2022 * applications, this requires substantial extra work, since the basename of the 2023 * first program argument is the actual command name. 2024 * 2025 * Due to limitations in the KERN_PROCARGS sysctl as implemented in OS X 10.2, 2026 * changes were made to the sysctl to make finding the process arguments more 2027 * deterministic. If TOP_JAGUAR is defined, the old algorithm is used, rather 2028 * than the simpler new one. 2029 */ 2030static libtop_status_t 2031libtop_p_proc_command(libtop_pinfo_t *pinfo, struct kinfo_proc *kinfo) 2032{ 2033#if 0 2034 uint32_t bufferSize = 30; 2035 int result; 2036 2037 if (pinfo->psamp.command) { 2038 free(pinfo->psamp.command); 2039 pinfo->psamp.command = NULL; 2040 } 2041 2042 pinfo->psamp.command = malloc(bufferSize); 2043 if (NULL == pinfo->psamp.command) 2044 return LIBTOP_ERR_ALLOC; 2045 2046 pinfo->psamp.command[0] = '\0'; 2047 proc_name(pinfo->psamp.pid, pinfo->psamp.command, bufferSize); 2048#if 0 2049 if(result) { 2050 pinfo->psamp.command[0] = '\0'; 2051 return LIBTOP_ERR_INVALID; 2052 } 2053#endif 2054 2055 return 0; 2056#endif 2057 2058 2059 uint32_t len; 2060 2061 if (pinfo->psamp.command) { 2062 free(pinfo->psamp.command); 2063 pinfo->psamp.command = NULL; 2064 } 2065 2066 len = strlen(kinfo->kp_proc.p_comm); 2067 2068 if (strncmp(kinfo->kp_proc.p_comm, "LaunchCFMApp",len) != 0) { 2069 /* Normal program. */ 2070 pinfo->psamp.command = strdup(kinfo->kp_proc.p_comm); 2071 if (!pinfo->psamp.command) { 2072 return LIBTOP_ERR_ALLOC; 2073 } 2074 } else { 2075 int mib[3]; 2076 size_t procargssize; 2077#ifdef TOP_JAGUAR 2078 char *arg_end, *exec_path; 2079 int *ip; 2080#else 2081 char *cp; 2082#endif 2083 char *command_beg, *command, *command_end; 2084 2085 assert(pinfo->psamp.pid != 0); 2086 2087 /* 2088 * CFM application. Get the basename of the first argument and 2089 * use that as the command string. 2090 */ 2091 2092 /* 2093 * Make a sysctl() call to get the raw argument space of the 2094 * process. The layout is documented in start.s, which is part 2095 * of the Csu project. In summary, it looks like: 2096 * 2097 * /---------------\ 0x00000000 2098 * : : 2099 * : : 2100 * |---------------| 2101 * | argc | 2102 * |---------------| 2103 * | arg[0] | 2104 * |---------------| 2105 * : : 2106 * : : 2107 * |---------------| 2108 * | arg[argc - 1] | 2109 * |---------------| 2110 * | 0 | 2111 * |---------------| 2112 * | env[0] | 2113 * |---------------| 2114 * : : 2115 * : : 2116 * |---------------| 2117 * | env[n] | 2118 * |---------------| 2119 * | 0 | 2120 * |---------------| <-- Beginning of data returned by sysctl() 2121 * | exec_path | is here. 2122 * |:::::::::::::::| 2123 * | | 2124 * | String area. | 2125 * | | 2126 * |---------------| <-- Top of stack. 2127 * : : 2128 * : : 2129 * \---------------/ 0xffffffff 2130 */ 2131 mib[0] = CTL_KERN; 2132 mib[1] = KERN_PROCARGS2; 2133 mib[2] = pinfo->psamp.pid; 2134 2135 procargssize = libtop_argmax; 2136#ifdef TOP_JAGUAR 2137 /* Hack to avoid kernel bug. */ 2138 if (procargssize > 8192) { 2139 procargssize = 8192; 2140 } 2141#endif 2142 if (sysctl(mib, 3, libtop_arg, &procargssize, NULL, 0) == -1) { 2143 libtop_print(libtop_user_data, "%s(): Error in sysctl(): %s", 2144 __FUNCTION__, strerror(errno)); 2145 /* sysctl probably failed due to an invalid pid. */ 2146 return LIBTOP_ERR_INVALID; 2147 } 2148 2149#ifdef TOP_JAGUAR 2150 /* Set ip just above the end of libtop_arg. */ 2151 arg_end = &libtop_arg[procargssize]; 2152 ip = (int *)arg_end; 2153 2154 /* 2155 * Skip the last 2 words, since the last is a 0 word, and 2156 * the second to last may be as well, if there are no 2157 * arguments. 2158 */ 2159 ip -= 3; 2160 2161 /* Iterate down the arguments until a 0 word is found. */ 2162 for (; *ip != 0; ip--) { 2163 if (ip == (int *)libtop_arg) { 2164 goto ERROR; 2165 } 2166 } 2167 2168 /* The saved exec_path is just above the 0 word. */ 2169 ip++; 2170 exec_path = (char *)ip; 2171 2172 /* 2173 * Get the beginning of the first argument. It is word-aligned, 2174 * so skip padding '\0' bytes. 2175 */ 2176 command_beg = exec_path + strlen(exec_path); 2177 for (; *command_beg == '\0'; command_beg++) { 2178 if (command_beg >= arg_end) { 2179 goto ERROR; 2180 } 2181 } 2182 2183 /* Get the basename of command. */ 2184 command = command_end = command_beg + strlen(command_beg) + 1; 2185 for (command--; command >= command_beg; command--) { 2186 if (*command == '/') { 2187 break; 2188 } 2189 } 2190 command++; 2191 2192 /* Allocate space for the command and copy. */ 2193 len = command_end - command; 2194 pinfo->psamp.command = (char *)malloc(len + 1); 2195 if (pinfo->psamp.command == NULL) { 2196 return LIBTOP_ERR_ALLOC; 2197 } 2198 memcpy(pinfo->psamp.command, command, len + 1); 2199#else 2200 /* Skip the saved exec_path. */ 2201 for (cp = libtop_arg; cp < &libtop_arg[procargssize]; cp++) { 2202 if (*cp == '\0') { 2203 /* End of exec_path reached. */ 2204 break; 2205 } 2206 } 2207 if (cp == &libtop_arg[procargssize]) { 2208 goto ERROR; 2209 } 2210 2211 /* Skip trailing '\0' characters. */ 2212 for (; cp < &libtop_arg[procargssize]; cp++) { 2213 if (*cp != '\0') { 2214 /* Beginning of first argument reached. */ 2215 break; 2216 } 2217 } 2218 if (cp == &libtop_arg[procargssize]) { 2219 goto ERROR; 2220 } 2221 command_beg = cp; 2222 2223 /* 2224 * Make sure that the command is '\0'-terminated. This protects 2225 * against malicious programs; under normal operation this never 2226 * ends up being a problem.. 2227 */ 2228 for (; cp < &libtop_arg[procargssize]; cp++) { 2229 if (*cp == '\0') { 2230 /* End of first argument reached. */ 2231 break; 2232 } 2233 } 2234 if (cp == &libtop_arg[procargssize]) { 2235 goto ERROR; 2236 } 2237 command_end = command = cp; 2238 2239 /* Get the basename of command. */ 2240 for (command--; command >= command_beg; command--) { 2241 if (*command == '/') { 2242 command++; 2243 break; 2244 } 2245 } 2246 2247 /* Allocate space for the command and copy. */ 2248 len = command_end - command; 2249 pinfo->psamp.command = (char *)malloc(len + 1); 2250 if (pinfo->psamp.command == NULL) { 2251 return LIBTOP_ERR_ALLOC; 2252 } 2253 memcpy(pinfo->psamp.command, command, len + 1); 2254#endif 2255 } 2256 2257 return LIBTOP_NO_ERR; 2258 2259ERROR: 2260 /* 2261 * Unable to parse the arguments. Set the command name to 2262 * "(LaunchCFMApp)". 2263 */ 2264 pinfo->psamp.command = strdup("(LaunchCFMApp)"); 2265 return LIBTOP_NO_ERR; 2266} 2267 2268/* Insert a pinfo structure into the pid-ordered tree. */ 2269static void 2270libtop_p_pinsert(libtop_pinfo_t *pinfo) 2271{ 2272 rb_node_new(&libtop_ptree, pinfo, pnode); 2273 rb_insert(&libtop_ptree, pinfo, libtop_p_pinfo_pid_comp, 2274 libtop_pinfo_t, pnode); 2275} 2276 2277/* Remove a pinfo structure from the pid-ordered tree. */ 2278static void 2279libtop_p_premove(libtop_pinfo_t *pinfo) 2280{ 2281 rb_remove(&libtop_ptree, pinfo, libtop_pinfo_t, pnode); 2282} 2283 2284/* Search for a pinfo structure with pid pid. */ 2285static libtop_pinfo_t * 2286libtop_p_psearch(pid_t pid) 2287{ 2288 libtop_pinfo_t *retval, key; 2289 2290 key.psamp.pid = pid; 2291 rb_search(&libtop_ptree, &key, libtop_p_pinfo_pid_comp, pnode, retval); 2292 if (retval == rb_tree_nil(&libtop_ptree)) { 2293 retval = NULL; 2294 } 2295 2296 return retval; 2297} 2298 2299/* 2300 * Compare two pinfo structures according to pid. This function is used for 2301 * operations on the pid-sorted tree of pinfo structures. 2302 */ 2303static int 2304libtop_p_pinfo_pid_comp(libtop_pinfo_t *a, libtop_pinfo_t *b) 2305{ 2306 assert(a != NULL); 2307 assert(b != NULL); 2308 if (a->psamp.pid < b->psamp.pid) return -1; 2309 if (a->psamp.pid > b->psamp.pid) return 1; 2310 return 0; 2311} 2312 2313/* Process comparison wrapper function, used by the red-black tree code. */ 2314static int 2315libtop_p_pinfo_comp(libtop_pinfo_t *a, libtop_pinfo_t *b) 2316{ 2317 return libtop_sort(libtop_sort_data, &a->psamp, &b->psamp); 2318} 2319 2320/* Insert or update a memory object info entry. */ 2321static libtop_oinfo_t * 2322libtop_p_oinfo_insert(int obj_id, int share_type, int resident_page_count, 2323 int ref_count, int size, libtop_pinfo_t *pinfo) 2324{ 2325 const void *k = (const void *)(intptr_t)obj_id; 2326 libtop_oinfo_t *oinfo = (libtop_oinfo_t *)CFDictionaryGetValue(libtop_oinfo_hash, k); 2327 int clear_rollback = 0; 2328 2329 if (oinfo != NULL) { 2330 /* Use existing record. */ 2331 if (oinfo->pinfo != pinfo) { 2332 oinfo->proc_ref_count = 0; 2333 oinfo->pinfo = pinfo; 2334 clear_rollback = 1; 2335 } 2336 2337 oinfo->size += size; 2338 oinfo->proc_ref_count++; 2339 } else { 2340 oinfo = (libtop_oinfo_t *)malloc(sizeof(libtop_oinfo_t)); 2341 2342 if (oinfo == NULL) 2343 return NULL; 2344 2345 oinfo->pinfo = pinfo; 2346 oinfo->obj_id = obj_id; 2347 oinfo->size = size; 2348 oinfo->share_type = share_type; 2349 oinfo->resident_page_count = resident_page_count; 2350 oinfo->ref_count = ref_count; 2351 oinfo->proc_ref_count = 1; 2352 clear_rollback = 1; 2353 2354 CFDictionarySetValue(libtop_oinfo_hash, k, oinfo); 2355 } 2356 2357 if (clear_rollback) { 2358 // clear rollback fields 2359 oinfo->rb_share_type = SM_EMPTY; 2360 oinfo->rb_aliased = 0; 2361 oinfo->rb_vprvt = 0; 2362 oinfo->rb_rshrd = 0; 2363 } 2364 2365 return oinfo; 2366} 2367 2368static void 2369libtop_p_destroy_pinfo(libtop_pinfo_t *pinfo) { 2370 libtop_p_premove(pinfo); 2371 2372 if(pinfo->psamp.command) { 2373 free(pinfo->psamp.command); 2374 /* Guard against reuse even though we are freeing. */ 2375 pinfo->psamp.command = NULL; 2376 } 2377 2378 free(pinfo); 2379} 2380 2381static int 2382libtop_p_wq_update(libtop_pinfo_t *pinfo) { 2383#ifdef PROC_PIDWORKQUEUEINFO 2384 struct proc_workqueueinfo wqinfo; 2385 int result; 2386#endif 2387 2388 if (tsamp.seq > 1) { 2389 pinfo->psamp.p_wq_nthreads = pinfo->psamp.wq_nthreads; 2390 pinfo->psamp.p_wq_run_threads = pinfo->psamp.wq_run_threads; 2391 pinfo->psamp.p_wq_blocked_threads = pinfo->psamp.wq_blocked_threads; 2392 } 2393 2394 pinfo->psamp.wq_nthreads = 0; 2395 pinfo->psamp.wq_run_threads = 0; 2396 pinfo->psamp.wq_blocked_threads = 0; 2397 2398#ifdef PROC_PIDWORKQUEUEINFO 2399 result = proc_pidinfo(pinfo->psamp.pid, PROC_PIDWORKQUEUEINFO, 2400 /*arg is UNUSED with this flavor*/ 0ULL, 2401 &wqinfo, PROC_PIDWORKQUEUEINFO_SIZE); 2402 if(!result) 2403 return -1; /*error*/ 2404 2405 pinfo->psamp.wq_nthreads = wqinfo.pwq_nthreads; 2406 pinfo->psamp.wq_run_threads = wqinfo.pwq_runthreads; 2407 pinfo->psamp.wq_blocked_threads = wqinfo.pwq_blockedthreads; 2408#endif 2409 2410 return 0; 2411} 2412 2413libtop_i64_t 2414libtop_i64_init(uint64_t acc, int last_value) { 2415 libtop_i64_t r; 2416 2417 r.accumulator = acc; 2418 r.last_value = last_value; 2419 2420 if(0 == acc) { 2421 if(last_value >= 0) { 2422 r.accumulator += last_value; 2423 } else { 2424 /* The initial value has already overflowed. */ 2425 r.accumulator += INT_MAX; 2426 r.accumulator += abs(INT_MIN - last_value) + 1; 2427 } 2428 } 2429 2430 return r; 2431} 2432 2433void 2434libtop_i64_update(struct libtop_i64 *i, int value) { 2435 int delta = 0; 2436 2437 if(value >= 0) { 2438 if(i->last_value >= 0) { 2439 delta = value - i->last_value; 2440 } else { 2441 delta = value + (-i->last_value); 2442 } 2443 } else { 2444 if(i->last_value >= 0) { 2445 delta = abs(INT_MIN - value) + 1; 2446 delta += INT_MAX - i->last_value; 2447 } else { 2448 delta = value - i->last_value; 2449 } 2450 } 2451 2452 i->accumulator += delta; 2453 2454#ifdef LIBTOP_I64_DEBUG 2455 if(delta == 0) 2456 assert((value - i->last_value) == 0); 2457 2458 fprintf(stderr, "%s: delta %u :: value %d :: i->last_value %d\n", __func__, 2459 delta, value, i->last_value); 2460 fprintf(stderr, "%s: accumulator > INT_MAX %s\n", __func__, 2461 (i->accumulator > INT_MAX) ? "YES" : "NO"); 2462 2463 fprintf(stderr, "%s: value %x value unsigned %u > UINT_MAX %s\n", __func__, 2464 value, value, (i->accumulator > UINT_MAX) ? "YES" : "NO"); 2465#endif 2466 2467 i->last_value = value; 2468} 2469 2470uint64_t 2471libtop_i64_value(libtop_i64_t *i) { 2472 return i->accumulator; 2473} 2474 2475/* This tests every branch in libtop_i64_update(). */ 2476static void 2477libtop_i64_test(void) { 2478 libtop_i64_t i64; 2479 int i; 2480 int value = 3; 2481 int incr = INT_MAX / 4; 2482 uint64_t accum = value; 2483 2484 i64 = libtop_i64_init(0, value); 2485 2486 i = 0; 2487 2488 while(1) { 2489 fprintf(stderr, "test: i %d value %d i64 value %" PRIu64 " matches? %" PRIu64 "\n", i, value, libtop_i64_value(&i64), accum); 2490 fprintf(stderr, "+ %d\n", incr); 2491 value += incr; 2492 accum += incr; 2493 libtop_i64_update(&i64, value); 2494 ++i; 2495 } 2496 2497} 2498