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 |