Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(112)

Side by Side Diff: base/process/process_metrics_mac.cc

Issue 2782503002: Add new wired memory metric to memory-infra dumps. (Closed)
Patch Set: Remove test iOS. Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698