| OLD | NEW | 
|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 #include "base/process_util.h" | 5 #include "base/process_util.h" | 
| 6 | 6 | 
| 7 #import <Cocoa/Cocoa.h> | 7 #import <Cocoa/Cocoa.h> | 
| 8 #include <crt_externs.h> | 8 #include <crt_externs.h> | 
| 9 #include <dlfcn.h> | 9 #include <dlfcn.h> | 
| 10 #include <mach/mach.h> | 10 #include <mach/mach.h> | 
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 68 | 68 | 
| 69   // Since more processes could start between when we get the size and when | 69   // Since more processes could start between when we get the size and when | 
| 70   // we get the list, we do a loop to keep trying until we get it. | 70   // we get the list, we do a loop to keep trying until we get it. | 
| 71   bool done = false; | 71   bool done = false; | 
| 72   int try_num = 1; | 72   int try_num = 1; | 
| 73   const int max_tries = 10; | 73   const int max_tries = 10; | 
| 74   do { | 74   do { | 
| 75     // Get the size of the buffer | 75     // Get the size of the buffer | 
| 76     size_t len = 0; | 76     size_t len = 0; | 
| 77     if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) < 0) { | 77     if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) < 0) { | 
| 78       LOG(ERROR) << "failed to get the size needed for the process list"; | 78       DLOG(ERROR) << "failed to get the size needed for the process list"; | 
| 79       kinfo_procs_.resize(0); | 79       kinfo_procs_.resize(0); | 
| 80       done = true; | 80       done = true; | 
| 81     } else { | 81     } else { | 
| 82       size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc); | 82       size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc); | 
| 83       // Leave some spare room for process table growth (more could show up | 83       // Leave some spare room for process table growth (more could show up | 
| 84       // between when we check and now) | 84       // between when we check and now) | 
| 85       num_of_kinfo_proc += 16; | 85       num_of_kinfo_proc += 16; | 
| 86       kinfo_procs_.resize(num_of_kinfo_proc); | 86       kinfo_procs_.resize(num_of_kinfo_proc); | 
| 87       len = num_of_kinfo_proc * sizeof(struct kinfo_proc); | 87       len = num_of_kinfo_proc * sizeof(struct kinfo_proc); | 
| 88       // Load the list of processes | 88       // Load the list of processes | 
| 89       if (sysctl(mib, arraysize(mib), &kinfo_procs_[0], &len, NULL, 0) < 0) { | 89       if (sysctl(mib, arraysize(mib), &kinfo_procs_[0], &len, NULL, 0) < 0) { | 
| 90         // If we get a mem error, it just means we need a bigger buffer, so | 90         // If we get a mem error, it just means we need a bigger buffer, so | 
| 91         // loop around again.  Anything else is a real error and give up. | 91         // loop around again.  Anything else is a real error and give up. | 
| 92         if (errno != ENOMEM) { | 92         if (errno != ENOMEM) { | 
| 93           LOG(ERROR) << "failed to get the process list"; | 93           DLOG(ERROR) << "failed to get the process list"; | 
| 94           kinfo_procs_.resize(0); | 94           kinfo_procs_.resize(0); | 
| 95           done = true; | 95           done = true; | 
| 96         } | 96         } | 
| 97       } else { | 97       } else { | 
| 98         // Got the list, just make sure we're sized exactly right | 98         // Got the list, just make sure we're sized exactly right | 
| 99         size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc); | 99         size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc); | 
| 100         kinfo_procs_.resize(num_of_kinfo_proc); | 100         kinfo_procs_.resize(num_of_kinfo_proc); | 
| 101         done = true; | 101         done = true; | 
| 102       } | 102       } | 
| 103     } | 103     } | 
| 104   } while (!done && (try_num++ < max_tries)); | 104   } while (!done && (try_num++ < max_tries)); | 
| 105 | 105 | 
| 106   if (!done) { | 106   if (!done) { | 
| 107     LOG(ERROR) << "failed to collect the process list in a few tries"; | 107     DLOG(ERROR) << "failed to collect the process list in a few tries"; | 
| 108     kinfo_procs_.resize(0); | 108     kinfo_procs_.resize(0); | 
| 109   } | 109   } | 
| 110 } | 110 } | 
| 111 | 111 | 
| 112 ProcessIterator::~ProcessIterator() { | 112 ProcessIterator::~ProcessIterator() { | 
| 113 } | 113 } | 
| 114 | 114 | 
| 115 bool ProcessIterator::CheckForNextProcess() { | 115 bool ProcessIterator::CheckForNextProcess() { | 
| 116   std::string data; | 116   std::string data; | 
| 117   for (; index_of_kinfo_proc_ < kinfo_procs_.size(); ++index_of_kinfo_proc_) { | 117   for (; index_of_kinfo_proc_ < kinfo_procs_.size(); ++index_of_kinfo_proc_) { | 
| (...skipping 24 matching lines...) Expand all  Loading... | 
| 142     // |entry_.cmd_line_args_|. | 142     // |entry_.cmd_line_args_|. | 
| 143     std::string delimiters; | 143     std::string delimiters; | 
| 144     delimiters.push_back('\0'); | 144     delimiters.push_back('\0'); | 
| 145     Tokenize(data, delimiters, &entry_.cmd_line_args_); | 145     Tokenize(data, delimiters, &entry_.cmd_line_args_); | 
| 146 | 146 | 
| 147     // |data| starts with the full executable path followed by a null character. | 147     // |data| starts with the full executable path followed by a null character. | 
| 148     // We search for the first instance of '\0' and extract everything before it | 148     // We search for the first instance of '\0' and extract everything before it | 
| 149     // to populate |entry_.exe_file_|. | 149     // to populate |entry_.exe_file_|. | 
| 150     size_t exec_name_end = data.find('\0'); | 150     size_t exec_name_end = data.find('\0'); | 
| 151     if (exec_name_end == std::string::npos) { | 151     if (exec_name_end == std::string::npos) { | 
| 152       LOG(ERROR) << "command line data didn't match expected format"; | 152       DLOG(ERROR) << "command line data didn't match expected format"; | 
| 153       continue; | 153       continue; | 
| 154     } | 154     } | 
| 155 | 155 | 
| 156     entry_.pid_ = kinfo.kp_proc.p_pid; | 156     entry_.pid_ = kinfo.kp_proc.p_pid; | 
| 157     entry_.ppid_ = kinfo.kp_eproc.e_ppid; | 157     entry_.ppid_ = kinfo.kp_eproc.e_ppid; | 
| 158     entry_.gid_ = kinfo.kp_eproc.e_pgid; | 158     entry_.gid_ = kinfo.kp_eproc.e_pgid; | 
| 159     size_t last_slash = data.rfind('/', exec_name_end); | 159     size_t last_slash = data.rfind('/', exec_name_end); | 
| 160     if (last_slash == std::string::npos) | 160     if (last_slash == std::string::npos) | 
| 161       entry_.exe_file_.assign(data, 0, exec_name_end); | 161       entry_.exe_file_.assign(data, 0, exec_name_end); | 
| 162     else | 162     else | 
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 241 } | 241 } | 
| 242 | 242 | 
| 243 static bool GetCPUTypeForProcess(pid_t pid, cpu_type_t* cpu_type) { | 243 static bool GetCPUTypeForProcess(pid_t pid, cpu_type_t* cpu_type) { | 
| 244   size_t len = sizeof(*cpu_type); | 244   size_t len = sizeof(*cpu_type); | 
| 245   int result = sysctlbyname("sysctl.proc_cputype", | 245   int result = sysctlbyname("sysctl.proc_cputype", | 
| 246                             cpu_type, | 246                             cpu_type, | 
| 247                             &len, | 247                             &len, | 
| 248                             NULL, | 248                             NULL, | 
| 249                             0); | 249                             0); | 
| 250   if (result != 0) { | 250   if (result != 0) { | 
| 251     PLOG(ERROR) << "sysctlbyname(""sysctl.proc_cputype"")"; | 251     DPLOG(ERROR) << "sysctlbyname(""sysctl.proc_cputype"")"; | 
| 252     return false; | 252     return false; | 
| 253   } | 253   } | 
| 254 | 254 | 
| 255   return true; | 255   return true; | 
| 256 } | 256 } | 
| 257 | 257 | 
| 258 static bool IsAddressInSharedRegion(mach_vm_address_t addr, cpu_type_t type) { | 258 static bool IsAddressInSharedRegion(mach_vm_address_t addr, cpu_type_t type) { | 
| 259   if (type == CPU_TYPE_I386) | 259   if (type == CPU_TYPE_I386) | 
| 260     return addr >= SHARED_REGION_BASE_I386 && | 260     return addr >= SHARED_REGION_BASE_I386 && | 
| 261            addr < (SHARED_REGION_BASE_I386 + SHARED_REGION_SIZE_I386); | 261            addr < (SHARED_REGION_BASE_I386 + SHARED_REGION_SIZE_I386); | 
| (...skipping 11 matching lines...) Expand all  Loading... | 
| 273                                     size_t* shared_bytes) { | 273                                     size_t* shared_bytes) { | 
| 274   kern_return_t kr; | 274   kern_return_t kr; | 
| 275   size_t private_pages_count = 0; | 275   size_t private_pages_count = 0; | 
| 276   size_t shared_pages_count = 0; | 276   size_t shared_pages_count = 0; | 
| 277 | 277 | 
| 278   if (!private_bytes && !shared_bytes) | 278   if (!private_bytes && !shared_bytes) | 
| 279     return true; | 279     return true; | 
| 280 | 280 | 
| 281   mach_port_t task = TaskForPid(process_); | 281   mach_port_t task = TaskForPid(process_); | 
| 282   if (task == MACH_PORT_NULL) { | 282   if (task == MACH_PORT_NULL) { | 
| 283     LOG(ERROR) << "Invalid process"; | 283     DLOG(ERROR) << "Invalid process"; | 
| 284     return false; | 284     return false; | 
| 285   } | 285   } | 
| 286 | 286 | 
| 287   cpu_type_t cpu_type; | 287   cpu_type_t cpu_type; | 
| 288   if (!GetCPUTypeForProcess(process_, &cpu_type)) | 288   if (!GetCPUTypeForProcess(process_, &cpu_type)) | 
| 289     return false; | 289     return false; | 
| 290 | 290 | 
| 291   // The same region can be referenced multiple times. To avoid double counting | 291   // The same region can be referenced multiple times. To avoid double counting | 
| 292   // we need to keep track of which regions we've already counted. | 292   // we need to keep track of which regions we've already counted. | 
| 293   base::hash_set<int> seen_objects; | 293   base::hash_set<int> seen_objects; | 
| (...skipping 18 matching lines...) Expand all  Loading... | 
| 312                         &address, | 312                         &address, | 
| 313                         &size, | 313                         &size, | 
| 314                         VM_REGION_TOP_INFO, | 314                         VM_REGION_TOP_INFO, | 
| 315                         (vm_region_info_t)&info, | 315                         (vm_region_info_t)&info, | 
| 316                         &info_count, | 316                         &info_count, | 
| 317                         &object_name); | 317                         &object_name); | 
| 318     if (kr == KERN_INVALID_ADDRESS) { | 318     if (kr == KERN_INVALID_ADDRESS) { | 
| 319       // We're at the end of the address space. | 319       // We're at the end of the address space. | 
| 320       break; | 320       break; | 
| 321     } else if (kr != KERN_SUCCESS) { | 321     } else if (kr != KERN_SUCCESS) { | 
| 322       LOG(ERROR) << "Calling mach_vm_region failed with error: " | 322       DLOG(ERROR) << "Calling mach_vm_region failed with error: " | 
| 323                  << mach_error_string(kr); | 323                  << mach_error_string(kr); | 
| 324       return false; | 324       return false; | 
| 325     } | 325     } | 
| 326 | 326 | 
| 327     if (IsAddressInSharedRegion(address, cpu_type) && | 327     if (IsAddressInSharedRegion(address, cpu_type) && | 
| 328         info.share_mode != SM_PRIVATE) | 328         info.share_mode != SM_PRIVATE) | 
| 329       continue; | 329       continue; | 
| 330 | 330 | 
| 331     if (info.share_mode == SM_COW && info.ref_count == 1) | 331     if (info.share_mode == SM_COW && info.ref_count == 1) | 
| 332       info.share_mode = SM_PRIVATE; | 332       info.share_mode = SM_PRIVATE; | 
| (...skipping 14 matching lines...) Expand all  Loading... | 
| 347         } | 347         } | 
| 348         break; | 348         break; | 
| 349       default: | 349       default: | 
| 350         break; | 350         break; | 
| 351     } | 351     } | 
| 352   } | 352   } | 
| 353 | 353 | 
| 354   vm_size_t page_size; | 354   vm_size_t page_size; | 
| 355   kr = host_page_size(task, &page_size); | 355   kr = host_page_size(task, &page_size); | 
| 356   if (kr != KERN_SUCCESS) { | 356   if (kr != KERN_SUCCESS) { | 
| 357     LOG(ERROR) << "Failed to fetch host page size, error: " | 357     DLOG(ERROR) << "Failed to fetch host page size, error: " | 
| 358                << mach_error_string(kr); | 358                << mach_error_string(kr); | 
| 359     return false; | 359     return false; | 
| 360   } | 360   } | 
| 361 | 361 | 
| 362   if (private_bytes) | 362   if (private_bytes) | 
| 363     *private_bytes = private_pages_count * page_size; | 363     *private_bytes = private_pages_count * page_size; | 
| 364   if (shared_bytes) | 364   if (shared_bytes) | 
| 365     *shared_bytes = shared_pages_count * page_size; | 365     *shared_bytes = shared_pages_count * page_size; | 
| 366 | 366 | 
| 367   return true; | 367   return true; | 
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 465 | 465 | 
| 466 // Bytes committed by the system. | 466 // Bytes committed by the system. | 
| 467 size_t GetSystemCommitCharge() { | 467 size_t GetSystemCommitCharge() { | 
| 468   host_name_port_t host = mach_host_self(); | 468   host_name_port_t host = mach_host_self(); | 
| 469   mach_msg_type_number_t count = HOST_VM_INFO_COUNT; | 469   mach_msg_type_number_t count = HOST_VM_INFO_COUNT; | 
| 470   vm_statistics_data_t data; | 470   vm_statistics_data_t data; | 
| 471   kern_return_t kr = host_statistics(host, HOST_VM_INFO, | 471   kern_return_t kr = host_statistics(host, HOST_VM_INFO, | 
| 472                                      reinterpret_cast<host_info_t>(&data), | 472                                      reinterpret_cast<host_info_t>(&data), | 
| 473                                      &count); | 473                                      &count); | 
| 474   if (kr) { | 474   if (kr) { | 
| 475     LOG(WARNING) << "Failed to fetch host statistics."; | 475     DLOG(WARNING) << "Failed to fetch host statistics."; | 
| 476     return 0; | 476     return 0; | 
| 477   } | 477   } | 
| 478 | 478 | 
| 479   vm_size_t page_size; | 479   vm_size_t page_size; | 
| 480   kr = host_page_size(host, &page_size); | 480   kr = host_page_size(host, &page_size); | 
| 481   if (kr) { | 481   if (kr) { | 
| 482     LOG(ERROR) << "Failed to fetch host page size."; | 482     DLOG(ERROR) << "Failed to fetch host page size."; | 
| 483     return 0; | 483     return 0; | 
| 484   } | 484   } | 
| 485 | 485 | 
| 486   return (data.active_count * page_size) / 1024; | 486   return (data.active_count * page_size) / 1024; | 
| 487 } | 487 } | 
| 488 | 488 | 
| 489 namespace { | 489 namespace { | 
| 490 | 490 | 
| 491 // Finds the library path for malloc() and thus the libC part of libSystem, | 491 // Finds the library path for malloc() and thus the libC part of libSystem, | 
| 492 // which in Lion is in a separate image. | 492 // which in Lion is in a separate image. | 
| 493 const char* LookUpLibCPath() { | 493 const char* LookUpLibCPath() { | 
| 494   const void* addr = reinterpret_cast<void*>(&malloc); | 494   const void* addr = reinterpret_cast<void*>(&malloc); | 
| 495 | 495 | 
| 496   Dl_info info; | 496   Dl_info info; | 
| 497   if (dladdr(addr, &info)) | 497   if (dladdr(addr, &info)) | 
| 498     return info.dli_fname; | 498     return info.dli_fname; | 
| 499 | 499 | 
| 500   LOG(WARNING) << "Could not find image path for malloc()"; | 500   DLOG(WARNING) << "Could not find image path for malloc()"; | 
| 501   return NULL; | 501   return NULL; | 
| 502 } | 502 } | 
| 503 | 503 | 
| 504 typedef void(*malloc_error_break_t)(void); | 504 typedef void(*malloc_error_break_t)(void); | 
| 505 malloc_error_break_t g_original_malloc_error_break = NULL; | 505 malloc_error_break_t g_original_malloc_error_break = NULL; | 
| 506 | 506 | 
| 507 // Returns the function pointer for malloc_error_break. This symbol is declared | 507 // Returns the function pointer for malloc_error_break. This symbol is declared | 
| 508 // as __private_extern__ and cannot be dlsym()ed. Instead, use nlist() to | 508 // as __private_extern__ and cannot be dlsym()ed. Instead, use nlist() to | 
| 509 // get it. | 509 // get it. | 
| 510 malloc_error_break_t LookUpMallocErrorBreak() { | 510 malloc_error_break_t LookUpMallocErrorBreak() { | 
| (...skipping 27 matching lines...) Expand all  Loading... | 
| 538   reference_addr += nl[0].n_value; | 538   reference_addr += nl[0].n_value; | 
| 539 | 539 | 
| 540   return reinterpret_cast<malloc_error_break_t>(reference_addr); | 540   return reinterpret_cast<malloc_error_break_t>(reference_addr); | 
| 541 #endif  // ARCH_CPU_32_BITS | 541 #endif  // ARCH_CPU_32_BITS | 
| 542 | 542 | 
| 543   return NULL; | 543   return NULL; | 
| 544 } | 544 } | 
| 545 | 545 | 
| 546 void CrMallocErrorBreak() { | 546 void CrMallocErrorBreak() { | 
| 547   g_original_malloc_error_break(); | 547   g_original_malloc_error_break(); | 
| 548   LOG(ERROR) << | 548   DLOG(ERROR) << | 
| 549       "Terminating process due to a potential for future heap corruption"; | 549       "Terminating process due to a potential for future heap corruption"; | 
| 550   int* death_ptr = NULL; | 550   int* death_ptr = NULL; | 
| 551   *death_ptr = 0xf00bad; | 551   *death_ptr = 0xf00bad; | 
| 552 } | 552 } | 
| 553 | 553 | 
| 554 }  // namespace | 554 }  // namespace | 
| 555 | 555 | 
| 556 void EnableTerminationOnHeapCorruption() { | 556 void EnableTerminationOnHeapCorruption() { | 
| 557   malloc_error_break_t malloc_error_break = LookUpMallocErrorBreak(); | 557   malloc_error_break_t malloc_error_break = LookUpMallocErrorBreak(); | 
| 558   if (!malloc_error_break) { | 558   if (!malloc_error_break) { | 
| 559     LOG(WARNING) << "Could not find malloc_error_break"; | 559     DLOG(WARNING) << "Could not find malloc_error_break"; | 
| 560     return; | 560     return; | 
| 561   } | 561   } | 
| 562 | 562 | 
| 563   mach_error_t err = mach_override_ptr( | 563   mach_error_t err = mach_override_ptr( | 
| 564      (void*)malloc_error_break, | 564      (void*)malloc_error_break, | 
| 565      (void*)&CrMallocErrorBreak, | 565      (void*)&CrMallocErrorBreak, | 
| 566      (void**)&g_original_malloc_error_break); | 566      (void**)&g_original_malloc_error_break); | 
| 567 | 567 | 
| 568   if (err != err_none) | 568   if (err != err_none) | 
| 569     LOG(WARNING) << "Could not override malloc_error_break; error = " << err; | 569     DLOG(WARNING) << "Could not override malloc_error_break; error = " << err; | 
| 570 } | 570 } | 
| 571 | 571 | 
| 572 // ------------------------------------------------------------------------ | 572 // ------------------------------------------------------------------------ | 
| 573 | 573 | 
| 574 namespace { | 574 namespace { | 
| 575 | 575 | 
| 576 bool g_oom_killer_enabled; | 576 bool g_oom_killer_enabled; | 
| 577 | 577 | 
| 578 // === C malloc/calloc/valloc/realloc/posix_memalign === | 578 // === C malloc/calloc/valloc/realloc/posix_memalign === | 
| 579 | 579 | 
| (...skipping 389 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 969       << "Failed to get allocWithZone allocation function."; | 969       << "Failed to get allocWithZone allocation function."; | 
| 970   method_setImplementation(orig_method, | 970   method_setImplementation(orig_method, | 
| 971                            reinterpret_cast<IMP>(oom_killer_allocWithZone)); | 971                            reinterpret_cast<IMP>(oom_killer_allocWithZone)); | 
| 972 } | 972 } | 
| 973 | 973 | 
| 974 ProcessId GetParentProcessId(ProcessHandle process) { | 974 ProcessId GetParentProcessId(ProcessHandle process) { | 
| 975   struct kinfo_proc info; | 975   struct kinfo_proc info; | 
| 976   size_t length = sizeof(struct kinfo_proc); | 976   size_t length = sizeof(struct kinfo_proc); | 
| 977   int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process }; | 977   int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process }; | 
| 978   if (sysctl(mib, 4, &info, &length, NULL, 0) < 0) { | 978   if (sysctl(mib, 4, &info, &length, NULL, 0) < 0) { | 
| 979     PLOG(ERROR) << "sysctl"; | 979     DPLOG(ERROR) << "sysctl"; | 
| 980     return -1; | 980     return -1; | 
| 981   } | 981   } | 
| 982   if (length == 0) | 982   if (length == 0) | 
| 983     return -1; | 983     return -1; | 
| 984   return info.kp_eproc.e_ppid; | 984   return info.kp_eproc.e_ppid; | 
| 985 } | 985 } | 
| 986 | 986 | 
| 987 }  // namespace base | 987 }  // namespace base | 
| OLD | NEW | 
|---|