Chromium Code Reviews| 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> |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 72 return addr >= SHARED_REGION_BASE_I386 && | 72 return addr >= SHARED_REGION_BASE_I386 && |
| 73 addr < (SHARED_REGION_BASE_I386 + SHARED_REGION_SIZE_I386); | 73 addr < (SHARED_REGION_BASE_I386 + SHARED_REGION_SIZE_I386); |
| 74 } else if (type == CPU_TYPE_X86_64) { | 74 } else if (type == CPU_TYPE_X86_64) { |
| 75 return addr >= SHARED_REGION_BASE_X86_64 && | 75 return addr >= SHARED_REGION_BASE_X86_64 && |
| 76 addr < (SHARED_REGION_BASE_X86_64 + SHARED_REGION_SIZE_X86_64); | 76 addr < (SHARED_REGION_BASE_X86_64 + SHARED_REGION_SIZE_X86_64); |
| 77 } else { | 77 } else { |
| 78 return false; | 78 return false; |
| 79 } | 79 } |
| 80 } | 80 } |
| 81 | 81 |
| 82 enum MachVMRegionResult { Finished, Error, Success }; | |
| 83 | |
| 84 // Both |size| and |address| are in-out parameters. | |
| 85 // |info| is an output parameter, only valid on Success. | |
| 86 MachVMRegionResult GetTopInfo(mach_port_t task, | |
| 87 mach_vm_size_t* size, | |
| 88 mach_vm_address_t* address, | |
| 89 vm_region_top_info_data_t* info) { | |
| 90 mach_msg_type_number_t info_count = VM_REGION_TOP_INFO_COUNT; | |
| 91 mach_port_t object_name; | |
| 92 kern_return_t kr = mach_vm_region(task, address, size, VM_REGION_TOP_INFO, | |
| 93 reinterpret_cast<vm_region_info_t>(info), | |
| 94 &info_count, &object_name); | |
| 95 if (kr == KERN_INVALID_ADDRESS) { | |
| 96 // We're at the end of the address space. | |
| 97 return Finished; | |
| 98 } else if (kr != KERN_SUCCESS) { | |
|
Lei Zhang
2017/03/30 04:26:35
nit: no need for else after a return.
erikchen
2017/03/30 18:02:47
Done.
| |
| 99 return Error; | |
| 100 } | |
| 101 | |
| 102 // The kernel always returns a null object for VM_REGION_TOP_INFO, but | |
| 103 // balance it with a deallocate in case this ever changes. See 10.9.2 | |
| 104 // xnu-2422.90.20/osfmk/vm/vm_map.c vm_map_region. | |
| 105 mach_port_deallocate(task, object_name); | |
| 106 return Success; | |
| 107 } | |
| 108 | |
| 109 MachVMRegionResult GetBasicInfo(mach_port_t task, | |
| 110 mach_vm_size_t* size, | |
| 111 mach_vm_address_t* address, | |
| 112 vm_region_basic_info_64* info) { | |
| 113 mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64; | |
| 114 mach_port_t object_name; | |
| 115 kern_return_t kr = mach_vm_region( | |
| 116 task, address, size, VM_REGION_BASIC_INFO_64, | |
| 117 reinterpret_cast<vm_region_info_t>(info), &info_count, &object_name); | |
| 118 if (kr == KERN_INVALID_ADDRESS) { | |
| 119 // We're at the end of the address space. | |
| 120 return Finished; | |
| 121 } else if (kr != KERN_SUCCESS) { | |
| 122 return Error; | |
| 123 } | |
| 124 | |
| 125 // The kernel always returns a null object for VM_REGION_BASIC_INFO_64, but | |
| 126 // balance it with a deallocate in case this ever changes. See 10.9.2 | |
| 127 // xnu-2422.90.20/osfmk/vm/vm_map.c vm_map_region. | |
| 128 mach_port_deallocate(task, object_name); | |
| 129 return Success; | |
| 130 } | |
| 131 | |
| 82 } // namespace | 132 } // namespace |
| 83 | 133 |
| 84 // Getting a mach task from a pid for another process requires permissions in | 134 // Getting a mach task from a pid for another process requires permissions in |
| 85 // general, so there doesn't really seem to be a way to do these (and spinning | 135 // general, so there doesn't really seem to be a way to do these (and spinning |
| 86 // up ps to fetch each stats seems dangerous to put in a base api for anyone to | 136 // up ps to fetch each stats seems dangerous to put in a base api for anyone to |
| 87 // call). Child processes ipc their port, so return something if available, | 137 // call). Child processes ipc their port, so return something if available, |
| 88 // otherwise return 0. | 138 // otherwise return 0. |
| 89 | 139 |
| 90 // static | 140 // static |
| 91 std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateProcessMetrics( | 141 std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateProcessMetrics( |
| 92 ProcessHandle process, | 142 ProcessHandle process, |
| 93 PortProvider* port_provider) { | 143 PortProvider* port_provider) { |
| 94 return WrapUnique(new ProcessMetrics(process, port_provider)); | 144 return WrapUnique(new ProcessMetrics(process, port_provider)); |
| 95 } | 145 } |
| 96 | 146 |
| 97 size_t ProcessMetrics::GetPagefileUsage() const { | 147 size_t ProcessMetrics::GetPagefileUsage() const { |
| 98 task_basic_info_64 task_info_data; | 148 task_basic_info_64 task_info_data; |
| 99 if (!GetTaskInfo(TaskForPid(process_), &task_info_data)) | 149 if (!GetTaskInfo(TaskForPid(process_), &task_info_data)) |
| 100 return 0; | 150 return 0; |
| 101 return task_info_data.virtual_size; | 151 return task_info_data.virtual_size; |
| 102 } | 152 } |
| 103 | 153 |
| 104 size_t ProcessMetrics::GetPeakPagefileUsage() const { | 154 size_t ProcessMetrics::GetPeakPagefileUsage() const { |
| 105 return 0; | 155 return 0; |
| 106 } | 156 } |
| 107 | 157 |
| 108 size_t ProcessMetrics::GetWorkingSetSize() const { | 158 size_t ProcessMetrics::GetWorkingSetSize() const { |
| 109 size_t private_bytes = 0; | |
| 110 size_t shared_bytes = 0; | |
| 111 size_t resident_bytes = 0; | 159 size_t resident_bytes = 0; |
| 112 if (!GetMemoryBytes(&private_bytes, &shared_bytes, &resident_bytes)) | 160 if (!GetMemoryBytes(nullptr, nullptr, &resident_bytes, nullptr)) |
| 113 return 0; | 161 return 0; |
| 114 return resident_bytes; | 162 return resident_bytes; |
| 115 } | 163 } |
| 116 | 164 |
| 117 size_t ProcessMetrics::GetPeakWorkingSetSize() const { | 165 size_t ProcessMetrics::GetPeakWorkingSetSize() const { |
| 118 return 0; | 166 return 0; |
| 119 } | 167 } |
| 120 | 168 |
| 169 bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes, | |
| 170 size_t* shared_bytes) const { | |
| 171 return GetMemoryBytes(private_bytes, shared_bytes, nullptr, nullptr); | |
| 172 } | |
| 173 | |
| 121 // This is a rough approximation of the algorithm that libtop uses. | 174 // This is a rough approximation of the algorithm that libtop uses. |
| 122 // private_bytes is the size of private resident memory. | 175 // private_bytes is the size of private resident memory. |
| 123 // shared_bytes is the size of shared resident memory. | 176 // shared_bytes is the size of shared resident memory. |
| 124 bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes, | 177 bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes, |
| 125 size_t* shared_bytes) const { | 178 size_t* shared_bytes, |
| 179 size_t* resident_bytes, | |
| 180 size_t* locked_bytes) const { | |
| 126 size_t private_pages_count = 0; | 181 size_t private_pages_count = 0; |
| 127 size_t shared_pages_count = 0; | 182 size_t shared_pages_count = 0; |
| 128 | 183 size_t wired_pages_count = 0; |
| 129 if (!private_bytes && !shared_bytes) | |
| 130 return true; | |
| 131 | 184 |
| 132 mach_port_t task = TaskForPid(process_); | 185 mach_port_t task = TaskForPid(process_); |
| 133 if (task == MACH_PORT_NULL) { | 186 if (task == MACH_PORT_NULL) { |
| 134 DLOG(ERROR) << "Invalid process"; | 187 DLOG(ERROR) << "Invalid process"; |
| 135 return false; | 188 return false; |
| 136 } | 189 } |
| 137 | 190 |
| 138 cpu_type_t cpu_type; | 191 cpu_type_t cpu_type; |
| 139 if (!GetCPUTypeForProcess(process_, &cpu_type)) | 192 if (!GetCPUTypeForProcess(process_, &cpu_type)) |
| 140 return false; | 193 return false; |
| 141 | 194 |
| 142 // The same region can be referenced multiple times. To avoid double counting | 195 // The same region can be referenced multiple times. To avoid double counting |
| 143 // we need to keep track of which regions we've already counted. | 196 // we need to keep track of which regions we've already counted. |
| 144 hash_set<int> seen_objects; | 197 hash_set<int> seen_objects; |
| 145 | 198 |
| 146 // We iterate through each VM region in the task's address map. For shared | 199 // We iterate through each VM region in the task's address map. For shared |
| 147 // memory we add up all the pages that are marked as shared. Like libtop we | 200 // memory we add up all the pages that are marked as shared. Like libtop we |
| 148 // try to avoid counting pages that are also referenced by other tasks. Since | 201 // try to avoid counting pages that are also referenced by other tasks. Since |
| 149 // we don't have access to the VM regions of other tasks the only hint we have | 202 // we don't have access to the VM regions of other tasks the only hint we have |
| 150 // is if the address is in the shared region area. | 203 // is if the address is in the shared region area. |
| 151 // | 204 // |
| 152 // Private memory is much simpler. We simply count the pages that are marked | 205 // Private memory is much simpler. We simply count the pages that are marked |
| 153 // as private or copy on write (COW). | 206 // as private or copy on write (COW). |
| 154 // | 207 // |
| 155 // See libtop_update_vm_regions in | 208 // See libtop_update_vm_regions in |
| 156 // http://www.opensource.apple.com/source/top/top-67/libtop.c | 209 // http://www.opensource.apple.com/source/top/top-67/libtop.c |
| 157 mach_vm_size_t size = 0; | 210 mach_vm_size_t size = 0; |
| 158 for (mach_vm_address_t address = MACH_VM_MIN_ADDRESS;; address += size) { | 211 for (mach_vm_address_t address = MACH_VM_MIN_ADDRESS;; address += size) { |
| 212 mach_vm_size_t size_copy = size; | |
| 213 mach_vm_address_t address_copy = address; | |
| 214 | |
| 159 vm_region_top_info_data_t info; | 215 vm_region_top_info_data_t info; |
| 160 mach_msg_type_number_t info_count = VM_REGION_TOP_INFO_COUNT; | 216 MachVMRegionResult result = GetTopInfo(task, &size, &address, &info); |
| 161 mach_port_t object_name; | 217 bool finished = false; |
|
Lei Zhang
2017/03/30 04:26:35
Doesn't line 217-229 shorten to the following?
if
erikchen
2017/03/30 18:02:47
Done.
| |
| 162 kern_return_t kr = mach_vm_region(task, | 218 switch (result) { |
| 163 &address, | 219 case Finished: |
| 164 &size, | 220 finished = true; |
| 165 VM_REGION_TOP_INFO, | 221 break; |
| 166 reinterpret_cast<vm_region_info_t>(&info), | 222 case Error: |
| 167 &info_count, | 223 return false; |
| 168 &object_name); | 224 case Success: |
| 169 if (kr == KERN_INVALID_ADDRESS) { | 225 break; |
| 170 // We're at the end of the address space. | |
| 171 break; | |
| 172 } else if (kr != KERN_SUCCESS) { | |
| 173 MACH_DLOG(ERROR, kr) << "mach_vm_region"; | |
| 174 return false; | |
| 175 } | 226 } |
| 176 | 227 |
| 177 // The kernel always returns a null object for VM_REGION_TOP_INFO, but | 228 if (finished) |
| 178 // balance it with a deallocate in case this ever changes. See 10.9.2 | 229 break; |
| 179 // xnu-2422.90.20/osfmk/vm/vm_map.c vm_map_region. | 230 |
| 180 mach_port_deallocate(mach_task_self(), object_name); | 231 vm_region_basic_info_64 basic_info; |
| 232 result = GetBasicInfo(task, &size_copy, &address_copy, &basic_info); | |
| 233 switch (result) { | |
| 234 case Finished: | |
| 235 case Error: | |
| 236 return false; | |
| 237 case Success: | |
| 238 break; | |
| 239 } | |
| 240 bool is_wired = basic_info.user_wired_count > 0; | |
| 181 | 241 |
| 182 if (IsAddressInSharedRegion(address, cpu_type) && | 242 if (IsAddressInSharedRegion(address, cpu_type) && |
| 183 info.share_mode != SM_PRIVATE) | 243 info.share_mode != SM_PRIVATE) |
| 184 continue; | 244 continue; |
| 185 | 245 |
| 186 if (info.share_mode == SM_COW && info.ref_count == 1) | 246 if (info.share_mode == SM_COW && info.ref_count == 1) |
| 187 info.share_mode = SM_PRIVATE; | 247 info.share_mode = SM_PRIVATE; |
| 188 | 248 |
| 189 switch (info.share_mode) { | 249 switch (info.share_mode) { |
| 190 case SM_LARGE_PAGE: | 250 case SM_LARGE_PAGE: |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 201 case SM_SHARED_ALIASED: | 261 case SM_SHARED_ALIASED: |
| 202 if (seen_objects.count(info.obj_id) == 0) { | 262 if (seen_objects.count(info.obj_id) == 0) { |
| 203 // Only count the first reference to this region. | 263 // Only count the first reference to this region. |
| 204 seen_objects.insert(info.obj_id); | 264 seen_objects.insert(info.obj_id); |
| 205 shared_pages_count += info.shared_pages_resident; | 265 shared_pages_count += info.shared_pages_resident; |
| 206 } | 266 } |
| 207 break; | 267 break; |
| 208 default: | 268 default: |
| 209 break; | 269 break; |
| 210 } | 270 } |
| 271 if (is_wired) | |
|
Lei Zhang
2017/03/30 04:26:35
nit: braces
erikchen
2017/03/30 18:02:48
Done.
| |
| 272 wired_pages_count += | |
| 273 info.private_pages_resident + info.shared_pages_resident; | |
| 211 } | 274 } |
| 212 | 275 |
| 213 if (private_bytes) | 276 if (private_bytes) |
| 214 *private_bytes = private_pages_count * PAGE_SIZE; | 277 *private_bytes = private_pages_count * PAGE_SIZE; |
| 215 if (shared_bytes) | 278 if (shared_bytes) |
| 216 *shared_bytes = shared_pages_count * PAGE_SIZE; | 279 *shared_bytes = shared_pages_count * PAGE_SIZE; |
| 280 if (resident_bytes) | |
| 281 *resident_bytes = (private_pages_count + shared_pages_count) * PAGE_SIZE; | |
| 282 if (locked_bytes) | |
| 283 *locked_bytes = wired_pages_count * PAGE_SIZE; | |
| 217 | 284 |
| 218 return true; | 285 return true; |
| 219 } | 286 } |
| 220 | 287 |
| 221 void ProcessMetrics::GetCommittedKBytes(CommittedKBytes* usage) const { | 288 void ProcessMetrics::GetCommittedKBytes(CommittedKBytes* usage) const { |
| 222 WorkingSetKBytes unused; | 289 WorkingSetKBytes unused; |
| 223 if (!GetCommittedAndWorkingSetKBytes(usage, &unused)) { | 290 if (!GetCommittedAndWorkingSetKBytes(usage, &unused)) { |
| 224 *usage = CommittedKBytes(); | 291 *usage = CommittedKBytes(); |
| 225 } | 292 } |
| 226 } | 293 } |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 241 usage->mapped = 0; | 308 usage->mapped = 0; |
| 242 usage->image = 0; | 309 usage->image = 0; |
| 243 | 310 |
| 244 ws_usage->priv = task_info_data.resident_size / 1024; | 311 ws_usage->priv = task_info_data.resident_size / 1024; |
| 245 ws_usage->shareable = 0; | 312 ws_usage->shareable = 0; |
| 246 ws_usage->shared = 0; | 313 ws_usage->shared = 0; |
| 247 | 314 |
| 248 return true; | 315 return true; |
| 249 } | 316 } |
| 250 | 317 |
| 251 bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes, | |
| 252 size_t* shared_bytes, | |
| 253 size_t* resident_bytes) const { | |
| 254 if (!GetMemoryBytes(private_bytes, shared_bytes)) | |
| 255 return false; | |
| 256 *resident_bytes = *private_bytes + *shared_bytes; | |
| 257 return true; | |
| 258 } | |
| 259 | |
| 260 #define TIME_VALUE_TO_TIMEVAL(a, r) do { \ | 318 #define TIME_VALUE_TO_TIMEVAL(a, r) do { \ |
| 261 (r)->tv_sec = (a)->seconds; \ | 319 (r)->tv_sec = (a)->seconds; \ |
| 262 (r)->tv_usec = (a)->microseconds; \ | 320 (r)->tv_usec = (a)->microseconds; \ |
| 263 } while (0) | 321 } while (0) |
| 264 | 322 |
| 265 double ProcessMetrics::GetCPUUsage() { | 323 double ProcessMetrics::GetCPUUsage() { |
| 266 mach_port_t task = TaskForPid(process_); | 324 mach_port_t task = TaskForPid(process_); |
| 267 if (task == MACH_PORT_NULL) | 325 if (task == MACH_PORT_NULL) |
| 268 return 0; | 326 return 0; |
| 269 | 327 |
| (...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 417 saturated_cast<int>(PAGE_SIZE / 1024 * vm_info.speculative_count); | 475 saturated_cast<int>(PAGE_SIZE / 1024 * vm_info.speculative_count); |
| 418 meminfo->file_backed = | 476 meminfo->file_backed = |
| 419 saturated_cast<int>(PAGE_SIZE / 1024 * vm_info.external_page_count); | 477 saturated_cast<int>(PAGE_SIZE / 1024 * vm_info.external_page_count); |
| 420 meminfo->purgeable = | 478 meminfo->purgeable = |
| 421 saturated_cast<int>(PAGE_SIZE / 1024 * vm_info.purgeable_count); | 479 saturated_cast<int>(PAGE_SIZE / 1024 * vm_info.purgeable_count); |
| 422 | 480 |
| 423 return true; | 481 return true; |
| 424 } | 482 } |
| 425 | 483 |
| 426 } // namespace base | 484 } // namespace base |
| OLD | NEW |