OLD | NEW |
---|---|
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 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/process_metrics.h" | 5 #include "base/process/process_metrics.h" |
6 | 6 |
7 #include <mach/mach.h> | 7 #include <mach/mach.h> |
8 #include <mach/mach_vm.h> | 8 #include <mach/mach_vm.h> |
9 #include <mach/shared_region.h> | 9 #include <mach/shared_region.h> |
10 #include <stddef.h> | 10 #include <stddef.h> |
11 #include <stdint.h> | 11 #include <stdint.h> |
12 #include <sys/sysctl.h> | 12 #include <sys/sysctl.h> |
13 | 13 |
14 #include "base/containers/hash_tables.h" | 14 #include "base/containers/hash_tables.h" |
15 #include "base/logging.h" | 15 #include "base/logging.h" |
16 #include "base/mac/mach_logging.h" | 16 #include "base/mac/mach_logging.h" |
17 #include "base/mac/scoped_mach_port.h" | 17 #include "base/mac/scoped_mach_port.h" |
18 #include "base/memory/ptr_util.h" | 18 #include "base/memory/ptr_util.h" |
19 #include "base/numerics/safe_conversions.h" | 19 #include "base/numerics/safe_conversions.h" |
20 #include "base/numerics/safe_math.h" | |
20 #include "base/sys_info.h" | 21 #include "base/sys_info.h" |
21 | 22 |
22 namespace base { | 23 namespace base { |
23 | 24 |
24 namespace { | 25 namespace { |
25 | 26 |
26 bool GetTaskInfo(mach_port_t task, task_basic_info_64* task_info_data) { | 27 bool GetTaskInfo(mach_port_t task, task_basic_info_64* task_info_data) { |
27 if (task == MACH_PORT_NULL) | 28 if (task == MACH_PORT_NULL) |
28 return false; | 29 return false; |
29 mach_msg_type_number_t count = TASK_BASIC_INFO_64_COUNT; | 30 mach_msg_type_number_t count = TASK_BASIC_INFO_64_COUNT; |
(...skipping 25 matching lines...) Expand all Loading... | |
55 return addr >= SHARED_REGION_BASE_I386 && | 56 return addr >= SHARED_REGION_BASE_I386 && |
56 addr < (SHARED_REGION_BASE_I386 + SHARED_REGION_SIZE_I386); | 57 addr < (SHARED_REGION_BASE_I386 + SHARED_REGION_SIZE_I386); |
57 } else if (type == CPU_TYPE_X86_64) { | 58 } else if (type == CPU_TYPE_X86_64) { |
58 return addr >= SHARED_REGION_BASE_X86_64 && | 59 return addr >= SHARED_REGION_BASE_X86_64 && |
59 addr < (SHARED_REGION_BASE_X86_64 + SHARED_REGION_SIZE_X86_64); | 60 addr < (SHARED_REGION_BASE_X86_64 + SHARED_REGION_SIZE_X86_64); |
60 } else { | 61 } else { |
61 return false; | 62 return false; |
62 } | 63 } |
63 } | 64 } |
64 | 65 |
65 enum MachVMRegionResult { Finished, Error, Success }; | |
66 | |
67 // Both |size| and |address| are in-out parameters. | |
68 // |info| is an output parameter, only valid on Success. | |
69 MachVMRegionResult GetTopInfo(mach_port_t task, | |
70 mach_vm_size_t* size, | |
71 mach_vm_address_t* address, | |
72 vm_region_top_info_data_t* info) { | |
73 mach_msg_type_number_t info_count = VM_REGION_TOP_INFO_COUNT; | |
74 mach_port_t object_name; | |
75 kern_return_t kr = mach_vm_region(task, address, size, VM_REGION_TOP_INFO, | |
76 reinterpret_cast<vm_region_info_t>(info), | |
77 &info_count, &object_name); | |
78 // We're at the end of the address space. | |
79 if (kr == KERN_INVALID_ADDRESS) | |
80 return Finished; | |
81 | |
82 if (kr != KERN_SUCCESS) | |
83 return Error; | |
84 | |
85 // The kernel always returns a null object for VM_REGION_TOP_INFO, but | |
86 // balance it with a deallocate in case this ever changes. See 10.9.2 | |
87 // xnu-2422.90.20/osfmk/vm/vm_map.c vm_map_region. | |
88 mach_port_deallocate(task, object_name); | |
89 return Success; | |
90 } | |
91 | |
92 MachVMRegionResult GetBasicInfo(mach_port_t task, | |
93 mach_vm_size_t* size, | |
94 mach_vm_address_t* address, | |
95 vm_region_basic_info_64* info) { | |
96 mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64; | |
97 mach_port_t object_name; | |
98 kern_return_t kr = mach_vm_region( | |
99 task, address, size, VM_REGION_BASIC_INFO_64, | |
100 reinterpret_cast<vm_region_info_t>(info), &info_count, &object_name); | |
101 if (kr == KERN_INVALID_ADDRESS) { | |
102 // We're at the end of the address space. | |
103 return Finished; | |
104 } else if (kr != KERN_SUCCESS) { | |
105 return Error; | |
106 } | |
107 | |
108 // The kernel always returns a null object for VM_REGION_BASIC_INFO_64, but | |
109 // balance it with a deallocate in case this ever changes. See 10.9.2 | |
110 // xnu-2422.90.20/osfmk/vm/vm_map.c vm_map_region. | |
111 mach_port_deallocate(task, object_name); | |
112 return Success; | |
113 } | |
114 | |
115 } // namespace | 66 } // namespace |
116 | 67 |
117 // Getting a mach task from a pid for another process requires permissions in | 68 // Getting a mach task from a pid for another process requires permissions in |
118 // general, so there doesn't really seem to be a way to do these (and spinning | 69 // general, so there doesn't really seem to be a way to do these (and spinning |
119 // up ps to fetch each stats seems dangerous to put in a base api for anyone to | 70 // up ps to fetch each stats seems dangerous to put in a base api for anyone to |
120 // call). Child processes ipc their port, so return something if available, | 71 // call). Child processes ipc their port, so return something if available, |
121 // otherwise return 0. | 72 // otherwise return 0. |
122 | 73 |
123 // static | 74 // static |
124 std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateProcessMetrics( | 75 std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateProcessMetrics( |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
184 // try to avoid counting pages that are also referenced by other tasks. Since | 135 // try to avoid counting pages that are also referenced by other tasks. Since |
185 // we don't have access to the VM regions of other tasks the only hint we have | 136 // we don't have access to the VM regions of other tasks the only hint we have |
186 // is if the address is in the shared region area. | 137 // is if the address is in the shared region area. |
187 // | 138 // |
188 // Private memory is much simpler. We simply count the pages that are marked | 139 // Private memory is much simpler. We simply count the pages that are marked |
189 // as private or copy on write (COW). | 140 // as private or copy on write (COW). |
190 // | 141 // |
191 // See libtop_update_vm_regions in | 142 // See libtop_update_vm_regions in |
192 // http://www.opensource.apple.com/source/top/top-67/libtop.c | 143 // http://www.opensource.apple.com/source/top/top-67/libtop.c |
193 mach_vm_size_t size = 0; | 144 mach_vm_size_t size = 0; |
194 for (mach_vm_address_t address = MACH_VM_MIN_ADDRESS;; address += size) { | 145 mach_vm_address_t address = MACH_VM_MIN_ADDRESS; |
195 mach_vm_size_t size_copy = size; | 146 while (true) { |
147 base::CheckedNumeric<mach_vm_address_t> numeric(address); | |
Mark Mentovai
2017/04/21 12:33:51
“numeric” isn’t a great name for an address. The i
erikchen
2017/04/21 18:20:24
Done.
| |
148 numeric += size; | |
149 if (!numeric.IsValid()) | |
150 return false; | |
151 address = numeric.ValueOrDie(); | |
152 | |
196 mach_vm_address_t address_copy = address; | 153 mach_vm_address_t address_copy = address; |
197 | |
198 vm_region_top_info_data_t info; | 154 vm_region_top_info_data_t info; |
199 MachVMRegionResult result = GetTopInfo(task, &size, &address, &info); | 155 MachVMRegionResult result = GetTopInfo(task, &size, &address, &info); |
200 if (result == Error) | 156 if (result == MachVMRegionResult::Error) |
201 return false; | 157 return false; |
202 if (result == Finished) | 158 if (result == MachVMRegionResult::Finished) |
203 break; | 159 break; |
204 | 160 |
205 vm_region_basic_info_64 basic_info; | 161 vm_region_basic_info_64 basic_info; |
206 result = GetBasicInfo(task, &size_copy, &address_copy, &basic_info); | 162 mach_vm_size_t dummy_size = 0; |
207 switch (result) { | 163 result = GetBasicInfo(task, &dummy_size, &address_copy, &basic_info); |
208 case Finished: | 164 if (result == MachVMRegionResult::Error) |
209 case Error: | 165 return false; |
210 return false; | 166 if (result == MachVMRegionResult::Finished) |
211 case Success: | 167 break; |
212 break; | 168 |
213 } | |
214 bool is_wired = basic_info.user_wired_count > 0; | 169 bool is_wired = basic_info.user_wired_count > 0; |
215 | 170 |
216 if (IsAddressInSharedRegion(address, cpu_type) && | 171 if (IsAddressInSharedRegion(address, cpu_type) && |
217 info.share_mode != SM_PRIVATE) | 172 info.share_mode != SM_PRIVATE) |
218 continue; | 173 continue; |
219 | 174 |
220 if (info.share_mode == SM_COW && info.ref_count == 1) | 175 if (info.share_mode == SM_COW && info.ref_count == 1) |
221 info.share_mode = SM_PRIVATE; | 176 info.share_mode = SM_PRIVATE; |
222 | 177 |
223 switch (info.share_mode) { | 178 switch (info.share_mode) { |
(...skipping 225 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
449 meminfo->speculative = | 404 meminfo->speculative = |
450 saturated_cast<int>(PAGE_SIZE / 1024 * vm_info.speculative_count); | 405 saturated_cast<int>(PAGE_SIZE / 1024 * vm_info.speculative_count); |
451 meminfo->file_backed = | 406 meminfo->file_backed = |
452 saturated_cast<int>(PAGE_SIZE / 1024 * vm_info.external_page_count); | 407 saturated_cast<int>(PAGE_SIZE / 1024 * vm_info.external_page_count); |
453 meminfo->purgeable = | 408 meminfo->purgeable = |
454 saturated_cast<int>(PAGE_SIZE / 1024 * vm_info.purgeable_count); | 409 saturated_cast<int>(PAGE_SIZE / 1024 * vm_info.purgeable_count); |
455 | 410 |
456 return true; | 411 return true; |
457 } | 412 } |
458 | 413 |
414 // Both |size| and |address| are in-out parameters. | |
415 // |info| is an output parameter, only valid on Success. | |
416 MachVMRegionResult GetTopInfo(mach_port_t task, | |
417 mach_vm_size_t* size, | |
418 mach_vm_address_t* address, | |
419 vm_region_top_info_data_t* info) { | |
420 mach_msg_type_number_t info_count = VM_REGION_TOP_INFO_COUNT; | |
421 mach_port_t object_name; | |
422 kern_return_t kr = mach_vm_region(task, address, size, VM_REGION_TOP_INFO, | |
423 reinterpret_cast<vm_region_info_t>(info), | |
424 &info_count, &object_name); | |
425 // We're at the end of the address space. | |
426 if (kr == KERN_INVALID_ADDRESS) | |
427 return MachVMRegionResult::Finished; | |
428 | |
429 if (kr != KERN_SUCCESS) | |
430 return MachVMRegionResult::Error; | |
431 | |
432 // The kernel always returns a null object for VM_REGION_TOP_INFO, but | |
433 // balance it with a deallocate in case this ever changes. See 10.9.2 | |
434 // xnu-2422.90.20/osfmk/vm/vm_map.c vm_map_region. | |
435 mach_port_deallocate(task, object_name); | |
436 return MachVMRegionResult::Success; | |
437 } | |
438 | |
439 MachVMRegionResult GetBasicInfo(mach_port_t task, | |
440 mach_vm_size_t* size, | |
441 mach_vm_address_t* address, | |
442 vm_region_basic_info_64* info) { | |
443 mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64; | |
444 mach_port_t object_name; | |
445 kern_return_t kr = mach_vm_region( | |
446 task, address, size, VM_REGION_BASIC_INFO_64, | |
447 reinterpret_cast<vm_region_info_t>(info), &info_count, &object_name); | |
448 if (kr == KERN_INVALID_ADDRESS) { | |
Mark Mentovai
2017/04/21 12:33:52
No reason for this section to deviate from 425-430
erikchen
2017/04/21 18:20:25
Done.
| |
449 // We're at the end of the address space. | |
450 return MachVMRegionResult::Finished; | |
451 } else if (kr != KERN_SUCCESS) { | |
452 return MachVMRegionResult::Error; | |
453 } | |
454 | |
455 // The kernel always returns a null object for VM_REGION_BASIC_INFO_64, but | |
456 // balance it with a deallocate in case this ever changes. See 10.9.2 | |
457 // xnu-2422.90.20/osfmk/vm/vm_map.c vm_map_region. | |
458 mach_port_deallocate(task, object_name); | |
459 return MachVMRegionResult::Success; | |
460 } | |
461 | |
459 } // namespace base | 462 } // namespace base |
OLD | NEW |