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

Side by Side Diff: chrome/browser/process_info_snapshot_mac.cc

Issue 1874483002: Remove "from all browsers" memory details mode (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Round 2 for windows bots Created 4 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
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "chrome/browser/process_info_snapshot.h"
6
7 #include <stddef.h>
8 #include <stdint.h>
9 #include <sys/sysctl.h>
10
11 #include <sstream>
12
13 #include "base/command_line.h"
14 #include "base/files/file_path.h"
15 #include "base/logging.h"
16 #include "base/mac/mac_util.h"
17 #include "base/macros.h"
18 #include "base/process/launch.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/string_util.h"
21 #include "base/threading/thread.h"
22
23 // Default constructor.
24 ProcessInfoSnapshot::ProcessInfoSnapshot() { }
25
26 // Destructor: just call |Reset()| to release everything.
27 ProcessInfoSnapshot::~ProcessInfoSnapshot() {
28 Reset();
29 }
30
31 const size_t ProcessInfoSnapshot::kMaxPidListSize = 1000;
32
33 static bool GetKInfoForProcessID(pid_t pid, kinfo_proc* kinfo) {
34 int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
35 size_t len = sizeof(*kinfo);
36 if (sysctl(mib, arraysize(mib), kinfo, &len, NULL, 0) != 0) {
37 PLOG(ERROR) << "sysctl() for KERN_PROC";
38 return false;
39 }
40
41 if (len == 0) {
42 // If the process isn't found then sysctl returns a length of 0.
43 return false;
44 }
45
46 return true;
47 }
48
49 static bool GetExecutableNameForProcessID(
50 pid_t pid,
51 std::string* executable_name) {
52 if (!executable_name) {
53 NOTREACHED();
54 return false;
55 }
56
57 static int s_arg_max = 0;
58 if (s_arg_max == 0) {
59 int mib[] = {CTL_KERN, KERN_ARGMAX};
60 size_t size = sizeof(s_arg_max);
61 if (sysctl(mib, arraysize(mib), &s_arg_max, &size, NULL, 0) != 0)
62 PLOG(ERROR) << "sysctl() for KERN_ARGMAX";
63 }
64
65 if (s_arg_max == 0)
66 return false;
67
68 int mib[] = {CTL_KERN, KERN_PROCARGS, pid};
69 size_t size = s_arg_max;
70 executable_name->resize(s_arg_max + 1);
71 if (sysctl(mib, arraysize(mib), &(*executable_name)[0],
72 &size, NULL, 0) != 0) {
73 // Don't log the error since it's normal for this to fail.
74 return false;
75 }
76
77 // KERN_PROCARGS returns multiple NULL terminated strings. Truncate
78 // executable_name to just the first string.
79 size_t end_pos = executable_name->find('\0');
80 if (end_pos == std::string::npos) {
81 return false;
82 }
83
84 executable_name->resize(end_pos);
85 return true;
86 }
87
88 // Converts a byte unit such as 'K' or 'M' into the scale for the unit.
89 // The scale can then be used to calculate the number of bytes in a value.
90 // The units are based on humanize_number(). See:
91 // http://www.opensource.apple.com/source/libutil/libutil-21/humanize_number.c
92 static bool ConvertByteUnitToScale(char unit, uint64_t* out_scale) {
93 int shift = 0;
94 switch (unit) {
95 case 'B':
96 shift = 0;
97 break;
98 case 'K':
99 case 'k':
100 shift = 1;
101 break;
102 case 'M':
103 shift = 2;
104 break;
105 case 'G':
106 shift = 3;
107 break;
108 case 'T':
109 shift = 4;
110 break;
111 case 'P':
112 shift = 5;
113 break;
114 case 'E':
115 shift = 6;
116 break;
117 default:
118 return false;
119 }
120
121 uint64_t scale = 1;
122 for (int i = 0; i < shift; i++)
123 scale *= 1024;
124 *out_scale = scale;
125
126 return true;
127 }
128
129 // Capture the information by calling '/bin/ps'.
130 // Note: we ignore the "tsiz" (text size) display option of ps because it's
131 // always zero (tested on 10.5 and 10.6).
132 static bool GetProcessMemoryInfoUsingPS(
133 const std::vector<base::ProcessId>& pid_list,
134 std::map<int,ProcessInfoSnapshot::ProcInfoEntry>& proc_info_entries) {
135 const base::FilePath kProgram("/bin/ps");
136 base::CommandLine command_line(kProgram);
137
138 // Get resident set size, virtual memory size.
139 command_line.AppendArg("-o");
140 command_line.AppendArg("pid=,rss=,vsz=");
141 // Only display the specified PIDs.
142 for (std::vector<base::ProcessId>::const_iterator it = pid_list.begin();
143 it != pid_list.end(); ++it) {
144 command_line.AppendArg("-p");
145 command_line.AppendArg(base::Int64ToString(static_cast<int64_t>(*it)));
146 }
147
148 std::string output;
149 // Limit output read to a megabyte for safety.
150 if (!base::GetAppOutputRestricted(command_line, &output, 1024 * 1024)) {
151 LOG(ERROR) << "Failure running " << kProgram.value() << " to acquire data.";
152 return false;
153 }
154
155 std::istringstream in(output, std::istringstream::in);
156
157 // Process lines until done.
158 while (true) {
159 // The format is as specified above to ps (see ps(1)):
160 // "-o pid=,rss=,vsz=".
161 // Try to read the PID; if we get it, we should be able to get the rest of
162 // the line.
163 pid_t pid;
164 in >> pid;
165 if (in.eof())
166 break;
167
168 ProcessInfoSnapshot::ProcInfoEntry proc_info = proc_info_entries[pid];
169 proc_info.pid = pid;
170 in >> proc_info.rss;
171 in >> proc_info.vsize;
172 proc_info.rss *= 1024; // Convert from kilobytes to bytes.
173 proc_info.vsize *= 1024;
174
175 // If the fail or bad bits were set, then there was an error reading input.
176 if (in.fail()) {
177 LOG(ERROR) << "Error parsing output from " << kProgram.value() << ".";
178 return false;
179 }
180
181 if (!proc_info.pid || ! proc_info.vsize) {
182 LOG(WARNING) << "Invalid data from " << kProgram.value() << ".";
183 return false;
184 }
185
186 // Record the process information.
187 proc_info_entries[proc_info.pid] = proc_info;
188
189 // Ignore the rest of the line.
190 in.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
191 }
192
193 return true;
194 }
195
196 static bool GetProcessMemoryInfoUsingTop(
197 std::map<int,ProcessInfoSnapshot::ProcInfoEntry>& proc_info_entries) {
198 const base::FilePath kProgram("/usr/bin/top");
199 base::CommandLine command_line(kProgram);
200
201 // -stats tells top to print just the given fields as ordered.
202 command_line.AppendArg("-stats");
203 command_line.AppendArg("pid," // Process ID
204 "rsize," // Resident memory
205 "rshrd," // Resident shared memory
206 "rprvt," // Resident private memory
207 "vsize"); // Total virtual memory
208 // Run top in logging (non-interactive) mode.
209 command_line.AppendArg("-l");
210 command_line.AppendArg("1");
211 // Set the delay between updates to 0.
212 command_line.AppendArg("-s");
213 command_line.AppendArg("0");
214
215 std::string output;
216 // Limit output read to a megabyte for safety.
217 if (!base::GetAppOutputRestricted(command_line, &output, 1024 * 1024)) {
218 LOG(ERROR) << "Failure running " << kProgram.value() << " to acquire data.";
219 return false;
220 }
221
222 // Process lines until done. Lines should look something like this:
223 // PID RSIZE RSHRD RPRVT VSIZE
224 // 58539 1276K+ 336K+ 740K+ 2378M+
225 // 58485 1888K+ 592K+ 1332K+ 2383M+
226 std::istringstream top_in(output, std::istringstream::in);
227 std::string line;
228 while (std::getline(top_in, line)) {
229 std::istringstream in(line, std::istringstream::in);
230
231 // Try to read the PID.
232 pid_t pid;
233 in >> pid;
234 if (in.fail())
235 continue;
236
237 // Make sure that caller is interested in this process.
238 if (proc_info_entries.find(pid) == proc_info_entries.end())
239 continue;
240
241 // Skip the - or + sign that top puts after the pid.
242 in.get();
243
244 uint64_t values[4];
245 size_t i;
246 for (i = 0; i < arraysize(values); i++) {
247 in >> values[i];
248 if (in.fail())
249 break;
250 std::string unit;
251 in >> unit;
252 if (in.fail())
253 break;
254
255 if (unit.empty())
256 break;
257
258 uint64_t scale;
259 if (!ConvertByteUnitToScale(unit[0], &scale))
260 break;
261 values[i] *= scale;
262 }
263 if (i != arraysize(values))
264 continue;
265
266 ProcessInfoSnapshot::ProcInfoEntry proc_info = proc_info_entries[pid];
267 proc_info.rss = values[0];
268 proc_info.rshrd = values[1];
269 proc_info.rprvt = values[2];
270 proc_info.vsize = values[3];
271 // Record the process information.
272 proc_info_entries[proc_info.pid] = proc_info;
273 }
274
275 return true;
276 }
277
278 bool ProcessInfoSnapshot::Sample(std::vector<base::ProcessId> pid_list) {
279 Reset();
280
281 // Nothing to do if no PIDs given.
282 if (pid_list.empty())
283 return true;
284 if (pid_list.size() > kMaxPidListSize) {
285 // The spec says |pid_list| *must* not have more than this many entries.
286 NOTREACHED();
287 return false;
288 }
289
290 // Get basic process info from KERN_PROC.
291 for (std::vector<base::ProcessId>::iterator it = pid_list.begin();
292 it != pid_list.end(); ++it) {
293 ProcInfoEntry proc_info;
294 proc_info.pid = *it;
295
296 kinfo_proc kinfo;
297 if (!GetKInfoForProcessID(*it, &kinfo))
298 return false;
299
300 proc_info.ppid = kinfo.kp_eproc.e_ppid;
301 proc_info.uid = kinfo.kp_eproc.e_pcred.p_ruid;
302 proc_info.euid = kinfo.kp_eproc.e_ucred.cr_uid;
303 // Note, p_comm is truncated to 16 characters.
304 proc_info.command = kinfo.kp_proc.p_comm;
305 proc_info_entries_[*it] = proc_info;
306 }
307
308 // Use KERN_PROCARGS to get the full executable name. This may fail if this
309 // process doesn't have privileges to inspect the target process.
310 for (std::vector<base::ProcessId>::iterator it = pid_list.begin();
311 it != pid_list.end(); ++it) {
312 std::string exectuable_name;
313 if (GetExecutableNameForProcessID(*it, &exectuable_name)) {
314 ProcInfoEntry proc_info = proc_info_entries_[*it];
315 proc_info.command = exectuable_name;
316 }
317 }
318
319 // In OSX 10.9+, top no longer returns any useful information. 'rshrd' is no
320 // longer supported, and 'rprvt' and 'vsize' return N/A. 'rsize' still works,
321 // but the information is also available from ps.
322 // http://crbug.com/383553
323 if (base::mac::IsOSMavericksOrLater())
324 return GetProcessMemoryInfoUsingPS(pid_list, proc_info_entries_);
325
326 // Get memory information using top.
327 bool memory_info_success = GetProcessMemoryInfoUsingTop(proc_info_entries_);
328
329 // If top didn't work then fall back to ps.
330 if (!memory_info_success) {
331 memory_info_success = GetProcessMemoryInfoUsingPS(pid_list,
332 proc_info_entries_);
333 }
334
335 return memory_info_success;
336 }
337
338 // Clear all the stored information.
339 void ProcessInfoSnapshot::Reset() {
340 proc_info_entries_.clear();
341 }
342
343 ProcessInfoSnapshot::ProcInfoEntry::ProcInfoEntry()
344 : pid(0),
345 ppid(0),
346 uid(0),
347 euid(0),
348 rss(0),
349 rshrd(0),
350 rprvt(0),
351 vsize(0) {
352 }
353
354 ProcessInfoSnapshot::ProcInfoEntry::ProcInfoEntry(const ProcInfoEntry& other) =
355 default;
356
357 bool ProcessInfoSnapshot::GetProcInfo(int pid,
358 ProcInfoEntry* proc_info) const {
359 std::map<int,ProcInfoEntry>::const_iterator it = proc_info_entries_.find(pid);
360 if (it == proc_info_entries_.end())
361 return false;
362
363 *proc_info = it->second;
364 return true;
365 }
366
367 bool ProcessInfoSnapshot::GetCommittedKBytesOfPID(
368 int pid,
369 base::CommittedKBytes* usage) const {
370 // Try to avoid crashing on a bug; stats aren't usually so crucial.
371 if (!usage) {
372 NOTREACHED();
373 return false;
374 }
375
376 // Failure of |GetProcInfo()| is "normal", due to racing.
377 ProcInfoEntry proc_info;
378 if (!GetProcInfo(pid, &proc_info)) {
379 usage->priv = 0;
380 usage->mapped = 0;
381 usage->image = 0;
382 return false;
383 }
384
385 usage->priv = proc_info.vsize / 1024;
386 usage->mapped = 0;
387 usage->image = 0;
388 return true;
389 }
390
391 bool ProcessInfoSnapshot::GetWorkingSetKBytesOfPID(
392 int pid,
393 base::WorkingSetKBytes* ws_usage) const {
394 // Try to avoid crashing on a bug; stats aren't usually so crucial.
395 if (!ws_usage) {
396 NOTREACHED();
397 return false;
398 }
399
400 // Failure of |GetProcInfo()| is "normal", due to racing.
401 ProcInfoEntry proc_info;
402 if (!GetProcInfo(pid, &proc_info)) {
403 ws_usage->priv = 0;
404 ws_usage->shareable = 0;
405 ws_usage->shared = 0;
406 return false;
407 }
408
409 ws_usage->priv = proc_info.rprvt / 1024;
410 ws_usage->shareable = proc_info.rss / 1024;
411 ws_usage->shared = proc_info.rshrd / 1024;
412 return true;
413 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698