OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 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 #include <ctype.h> | 7 #include <ctype.h> |
8 #include <dirent.h> | 8 #include <dirent.h> |
9 #include <errno.h> | 9 #include <errno.h> |
10 #include <fcntl.h> | 10 #include <fcntl.h> |
11 #include <sys/time.h> | 11 #include <sys/time.h> |
12 #include <sys/types.h> | 12 #include <sys/types.h> |
13 #include <sys/wait.h> | 13 #include <sys/wait.h> |
14 #include <time.h> | 14 #include <time.h> |
15 #include <unistd.h> | 15 #include <unistd.h> |
16 | 16 |
17 #include <string> | |
18 | |
19 #include "base/file_util.h" | 17 #include "base/file_util.h" |
20 #include "base/logging.h" | 18 #include "base/logging.h" |
21 #include "base/string_tokenizer.h" | 19 #include "base/string_tokenizer.h" |
22 #include "base/string_util.h" | 20 #include "base/string_util.h" |
23 | 21 |
24 namespace { | 22 namespace { |
25 | 23 |
26 enum ParsingState { | 24 enum ParsingState { |
27 KEY_NAME, | 25 KEY_NAME, |
28 KEY_VALUE | 26 KEY_VALUE |
(...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
246 } | 244 } |
247 return 0; | 245 return 0; |
248 } | 246 } |
249 | 247 |
250 size_t ProcessMetrics::GetPrivateBytes() const { | 248 size_t ProcessMetrics::GetPrivateBytes() const { |
251 WorkingSetKBytes ws_usage; | 249 WorkingSetKBytes ws_usage; |
252 GetWorkingSetKBytes(&ws_usage); | 250 GetWorkingSetKBytes(&ws_usage); |
253 return ws_usage.priv << 10; | 251 return ws_usage.priv << 10; |
254 } | 252 } |
255 | 253 |
256 // Private and Shared working set sizes are obtained from /proc/<pid>/smaps, | 254 // Private and Shared working set sizes are obtained from /proc/<pid>/smaps. |
257 // as in http://www.pixelbeat.org/scripts/ps_mem.py | 255 // When that's not available, use the values from /proc<pid>/statm as a |
| 256 // close approximation. |
| 257 // See http://www.pixelbeat.org/scripts/ps_mem.py |
258 bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const { | 258 bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const { |
259 FilePath stat_file = | 259 FilePath stat_file = |
260 FilePath("/proc").Append(IntToString(process_)).Append("smaps"); | 260 FilePath("/proc").Append(IntToString(process_)).Append("smaps"); |
261 std::string smaps; | 261 std::string smaps; |
262 int private_kb = 0; | 262 int private_kb = 0; |
263 int pss_kb = 0; | 263 int pss_kb = 0; |
264 bool have_pss = false; | 264 bool have_pss = false; |
265 if (!file_util::ReadFileToString(stat_file, &smaps) || smaps.length() == 0) | 265 if (file_util::ReadFileToString(stat_file, &smaps) && smaps.length() > 0) { |
266 return false; | 266 StringTokenizer tokenizer(smaps, ":\n"); |
| 267 ParsingState state = KEY_NAME; |
| 268 std::string last_key_name; |
| 269 while (tokenizer.GetNext()) { |
| 270 switch (state) { |
| 271 case KEY_NAME: |
| 272 last_key_name = tokenizer.token(); |
| 273 state = KEY_VALUE; |
| 274 break; |
| 275 case KEY_VALUE: |
| 276 if (last_key_name.empty()) { |
| 277 NOTREACHED(); |
| 278 return false; |
| 279 } |
| 280 if (StartsWithASCII(last_key_name, "Private_", 1)) { |
| 281 private_kb += StringToInt(tokenizer.token()); |
| 282 } else if (StartsWithASCII(last_key_name, "Pss", 1)) { |
| 283 have_pss = true; |
| 284 pss_kb += StringToInt(tokenizer.token()); |
| 285 } |
| 286 state = KEY_NAME; |
| 287 break; |
| 288 } |
| 289 } |
| 290 } else { |
| 291 // Try statm if smaps is empty because of the SUID sandbox. |
| 292 // First we need to get the page size though. |
| 293 int page_size_kb = sysconf(_SC_PAGE_SIZE) / 1024; |
| 294 if (page_size_kb <= 0) |
| 295 return false; |
267 | 296 |
268 StringTokenizer tokenizer(smaps, ":\n"); | 297 stat_file = |
269 ParsingState state = KEY_NAME; | 298 FilePath("/proc").Append(IntToString(process_)).Append("statm"); |
270 std::string last_key_name; | 299 std::string statm; |
271 while (tokenizer.GetNext()) { | 300 if (!file_util::ReadFileToString(stat_file, &statm) || statm.length() == 0) |
272 switch (state) { | 301 return false; |
273 case KEY_NAME: | 302 |
274 last_key_name = tokenizer.token(); | 303 std::vector<std::string> statm_vec; |
275 state = KEY_VALUE; | 304 SplitString(statm, ' ', &statm_vec); |
276 break; | 305 if (statm_vec.size() != 7) |
277 case KEY_VALUE: | 306 return false; // Not the format we expect. |
278 if (last_key_name.empty()) { | 307 private_kb = StringToInt(statm_vec[1]) - StringToInt(statm_vec[2]); |
279 NOTREACHED(); | 308 private_kb *= page_size_kb; |
280 return false; | |
281 } | |
282 if (StartsWithASCII(last_key_name, "Private_", 1)) { | |
283 private_kb += StringToInt(tokenizer.token()); | |
284 } else if (StartsWithASCII(last_key_name, "Pss", 1)) { | |
285 have_pss = true; | |
286 pss_kb += StringToInt(tokenizer.token()); | |
287 } | |
288 state = KEY_NAME; | |
289 break; | |
290 } | |
291 } | 309 } |
292 ws_usage->priv = private_kb; | 310 ws_usage->priv = private_kb; |
293 // Sharable is not calculated, as it does not provide interesting data. | 311 // Sharable is not calculated, as it does not provide interesting data. |
294 ws_usage->shareable = 0; | 312 ws_usage->shareable = 0; |
295 | 313 |
296 ws_usage->shared = 0; | 314 ws_usage->shared = 0; |
297 if (have_pss) | 315 if (have_pss) |
298 ws_usage->shared = pss_kb; | 316 ws_usage->shared = pss_kb; |
299 return true; | 317 return true; |
300 } | 318 } |
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
407 int64 time = TimeValToMicroseconds(now); | 425 int64 time = TimeValToMicroseconds(now); |
408 | 426 |
409 if (last_time_ == 0) { | 427 if (last_time_ == 0) { |
410 // First call, just set the last values. | 428 // First call, just set the last values. |
411 last_time_ = time; | 429 last_time_ = time; |
412 last_cpu_ = GetProcessCPU(process_); | 430 last_cpu_ = GetProcessCPU(process_); |
413 return 0; | 431 return 0; |
414 } | 432 } |
415 | 433 |
416 int64 time_delta = time - last_time_; | 434 int64 time_delta = time - last_time_; |
417 DCHECK(time_delta != 0); | 435 DCHECK_NE(time_delta, 0); |
418 if (time_delta == 0) | 436 if (time_delta == 0) |
419 return 0; | 437 return 0; |
420 | 438 |
421 int cpu = GetProcessCPU(process_); | 439 int cpu = GetProcessCPU(process_); |
422 | 440 |
423 // We have the number of jiffies in the time period. Convert to percentage. | 441 // We have the number of jiffies in the time period. Convert to percentage. |
424 // Note this means we will go *over* 100 in the case where multiple threads | 442 // Note this means we will go *over* 100 in the case where multiple threads |
425 // are together adding to more than one CPU's worth. | 443 // are together adding to more than one CPU's worth. |
426 int percentage = 100 * (cpu - last_cpu_) / | 444 int percentage = 100 * (cpu - last_cpu_) / |
427 (kHertz * TimeDelta::FromMicroseconds(time_delta).InSecondsF()); | 445 (kHertz * TimeDelta::FromMicroseconds(time_delta).InSecondsF()); |
428 | 446 |
429 last_time_ = time; | 447 last_time_ = time; |
430 last_cpu_ = cpu; | 448 last_cpu_ = cpu; |
431 | 449 |
432 return percentage; | 450 return percentage; |
433 } | 451 } |
434 | 452 |
435 } // namespace base | 453 } // namespace base |
OLD | NEW |