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