| 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 |