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 3 // found in the LICENSE file. 4 5 #include "base/process/process_metrics.h" 6 7 #include <dirent.h> 8 #include <fcntl.h> 9 #include <sys/stat.h> 10 #include <sys/time.h> 11 #include <sys/types.h> 12 #include <unistd.h> 13 14 #include "base/file_util.h" 15 #include "base/logging.h" 16 #include "base/process/internal_linux.h" 17 #include "base/strings/string_number_conversions.h" 18 #include "base/strings/string_split.h" 19 #include "base/strings/string_tokenizer.h" 20 #include "base/strings/string_util.h" 21 #include "base/sys_info.h" 22 #include "base/threading/thread_restrictions.h" 23 24 namespace base { 25 26 namespace { 27 28 enum ParsingState { 29 KEY_NAME, 30 KEY_VALUE 31 }; 32 33 // Read /proc/<pid>/status and returns the value for |field|, or 0 on failure. 34 // Only works for fields in the form of "Field: value kB". 35 size_t ReadProcStatusAndGetFieldAsSizeT(pid_t pid, const std::string& field) { 36 FilePath stat_file = internal::GetProcPidDir(pid).Append("status"); 37 std::string status; 38 { 39 // Synchronously reading files in /proc is safe. 40 ThreadRestrictions::ScopedAllowIO allow_io; 41 if (!file_util::ReadFileToString(stat_file, &status)) 42 return 0; 43 } 44 45 StringTokenizer tokenizer(status, ":\n"); 46 ParsingState state = KEY_NAME; 47 StringPiece last_key_name; 48 while (tokenizer.GetNext()) { 49 switch (state) { 50 case KEY_NAME: 51 last_key_name = tokenizer.token_piece(); 52 state = KEY_VALUE; 53 break; 54 case KEY_VALUE: 55 DCHECK(!last_key_name.empty()); 56 if (last_key_name == field) { 57 std::string value_str; 58 tokenizer.token_piece().CopyToString(&value_str); 59 std::string value_str_trimmed; 60 TrimWhitespaceASCII(value_str, TRIM_ALL, &value_str_trimmed); 61 std::vector<std::string> split_value_str; 62 SplitString(value_str_trimmed, ' ', &split_value_str); 63 if (split_value_str.size() != 2 || split_value_str[1] != "kB") { 64 NOTREACHED(); 65 return 0; 66 } 67 size_t value; 68 if (!StringToSizeT(split_value_str[0], &value)) { 69 NOTREACHED(); 70 return 0; 71 } 72 return value; 73 } 74 state = KEY_NAME; 75 break; 76 } 77 } 78 NOTREACHED(); 79 return 0; 80 } 81 82 // Get the total CPU of a single process. Return value is number of jiffies 83 // on success or -1 on error. 84 int GetProcessCPU(pid_t pid) { 85 // Use /proc/<pid>/task to find all threads and parse their /stat file. 86 FilePath task_path = internal::GetProcPidDir(pid).Append("task"); 87 88 DIR* dir = opendir(task_path.value().c_str()); 89 if (!dir) { 90 DPLOG(ERROR) << "opendir(" << task_path.value() << ")"; 91 return -1; 92 } 93 94 int total_cpu = 0; 95 while (struct dirent* ent = readdir(dir)) { 96 pid_t tid = internal::ProcDirSlotToPid(ent->d_name); 97 if (!tid) 98 continue; 99 100 // Synchronously reading files in /proc is safe. 101 ThreadRestrictions::ScopedAllowIO allow_io; 102 103 std::string stat; 104 FilePath stat_path = 105 task_path.Append(ent->d_name).Append(internal::kStatFile); 106 if (file_util::ReadFileToString(stat_path, &stat)) { 107 int cpu = ParseProcStatCPU(stat); 108 if (cpu > 0) 109 total_cpu += cpu; 110 } 111 } 112 closedir(dir); 113 114 return total_cpu; 115 } 116 117 } // namespace 118 119 // static 120 ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) { 121 return new ProcessMetrics(process); 122 } 123 124 // On linux, we return vsize. 125 size_t ProcessMetrics::GetPagefileUsage() const { 126 return internal::ReadProcStatsAndGetFieldAsSizeT(process_, 127 internal::VM_VSIZE); 128 } 129 130 // On linux, we return the high water mark of vsize. 131 size_t ProcessMetrics::GetPeakPagefileUsage() const { 132 return ReadProcStatusAndGetFieldAsSizeT(process_, "VmPeak") * 1024; 133 } 134 135 // On linux, we return RSS. 136 size_t ProcessMetrics::GetWorkingSetSize() const { 137 return internal::ReadProcStatsAndGetFieldAsSizeT(process_, internal::VM_RSS) * 138 getpagesize(); 139 } 140 141 // On linux, we return the high water mark of RSS. 142 size_t ProcessMetrics::GetPeakWorkingSetSize() const { 143 return ReadProcStatusAndGetFieldAsSizeT(process_, "VmHWM") * 1024; 144 } 145 146 bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes, 147 size_t* shared_bytes) { 148 WorkingSetKBytes ws_usage; 149 if (!GetWorkingSetKBytes(&ws_usage)) 150 return false; 151 152 if (private_bytes) 153 *private_bytes = ws_usage.priv * 1024; 154 155 if (shared_bytes) 156 *shared_bytes = ws_usage.shared * 1024; 157 158 return true; 159 } 160 161 bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const { 162 #if defined(OS_CHROMEOS) 163 if (GetWorkingSetKBytesTotmaps(ws_usage)) 164 return true; 165 #endif 166 return GetWorkingSetKBytesStatm(ws_usage); 167 } 168 169 double ProcessMetrics::GetCPUUsage() { 170 struct timeval now; 171 int retval = gettimeofday(&now, NULL); 172 if (retval) 173 return 0; 174 int64 time = TimeValToMicroseconds(now); 175 176 if (last_time_ == 0) { 177 // First call, just set the last values. 178 last_time_ = time; 179 last_cpu_ = GetProcessCPU(process_); 180 return 0; 181 } 182 183 int64 time_delta = time - last_time_; 184 DCHECK_NE(time_delta, 0); 185 if (time_delta == 0) 186 return 0; 187 188 int cpu = GetProcessCPU(process_); 189 190 // We have the number of jiffies in the time period. Convert to percentage. 191 // Note this means we will go *over* 100 in the case where multiple threads 192 // are together adding to more than one CPU's worth. 193 TimeDelta cpu_time = internal::ClockTicksToTimeDelta(cpu); 194 TimeDelta last_cpu_time = internal::ClockTicksToTimeDelta(last_cpu_); 195 int percentage = 100 * (cpu_time - last_cpu_time).InSecondsF() / 196 TimeDelta::FromMicroseconds(time_delta).InSecondsF(); 197 198 last_time_ = time; 199 last_cpu_ = cpu; 200 201 return percentage; 202 } 203 204 // To have /proc/self/io file you must enable CONFIG_TASK_IO_ACCOUNTING 205 // in your kernel configuration. 206 bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { 207 // Synchronously reading files in /proc is safe. 208 ThreadRestrictions::ScopedAllowIO allow_io; 209 210 std::string proc_io_contents; 211 FilePath io_file = internal::GetProcPidDir(process_).Append("io"); 212 if (!file_util::ReadFileToString(io_file, &proc_io_contents)) 213 return false; 214 215 (*io_counters).OtherOperationCount = 0; 216 (*io_counters).OtherTransferCount = 0; 217 218 StringTokenizer tokenizer(proc_io_contents, ": \n"); 219 ParsingState state = KEY_NAME; 220 StringPiece last_key_name; 221 while (tokenizer.GetNext()) { 222 switch (state) { 223 case KEY_NAME: 224 last_key_name = tokenizer.token_piece(); 225 state = KEY_VALUE; 226 break; 227 case KEY_VALUE: 228 DCHECK(!last_key_name.empty()); 229 if (last_key_name == "syscr") { 230 StringToInt64(tokenizer.token_piece(), 231 reinterpret_cast<int64*>(&(*io_counters).ReadOperationCount)); 232 } else if (last_key_name == "syscw") { 233 StringToInt64(tokenizer.token_piece(), 234 reinterpret_cast<int64*>(&(*io_counters).WriteOperationCount)); 235 } else if (last_key_name == "rchar") { 236 StringToInt64(tokenizer.token_piece(), 237 reinterpret_cast<int64*>(&(*io_counters).ReadTransferCount)); 238 } else if (last_key_name == "wchar") { 239 StringToInt64(tokenizer.token_piece(), 240 reinterpret_cast<int64*>(&(*io_counters).WriteTransferCount)); 241 } 242 state = KEY_NAME; 243 break; 244 } 245 } 246 return true; 247 } 248 249 ProcessMetrics::ProcessMetrics(ProcessHandle process) 250 : process_(process), 251 last_time_(0), 252 last_system_time_(0), 253 last_cpu_(0) { 254 processor_count_ = base::SysInfo::NumberOfProcessors(); 255 } 256 257 #if defined(OS_CHROMEOS) 258 // Private, Shared and Proportional working set sizes are obtained from 259 // /proc/<pid>/totmaps 260 bool ProcessMetrics::GetWorkingSetKBytesTotmaps(WorkingSetKBytes *ws_usage) 261 const { 262 // The format of /proc/<pid>/totmaps is: 263 // 264 // Rss: 6120 kB 265 // Pss: 3335 kB 266 // Shared_Clean: 1008 kB 267 // Shared_Dirty: 4012 kB 268 // Private_Clean: 4 kB 269 // Private_Dirty: 1096 kB 270 // Referenced: XXX kB 271 // Anonymous: XXX kB 272 // AnonHugePages: XXX kB 273 // Swap: XXX kB 274 // Locked: XXX kB 275 const size_t kPssIndex = (1 * 3) + 1; 276 const size_t kPrivate_CleanIndex = (4 * 3) + 1; 277 const size_t kPrivate_DirtyIndex = (5 * 3) + 1; 278 const size_t kSwapIndex = (9 * 3) + 1; 279 280 std::string totmaps_data; 281 { 282 FilePath totmaps_file = internal::GetProcPidDir(process_).Append("totmaps"); 283 ThreadRestrictions::ScopedAllowIO allow_io; 284 bool ret = file_util::ReadFileToString(totmaps_file, &totmaps_data); 285 if (!ret || totmaps_data.length() == 0) 286 return false; 287 } 288 289 std::vector<std::string> totmaps_fields; 290 SplitStringAlongWhitespace(totmaps_data, &totmaps_fields); 291 292 DCHECK_EQ("Pss:", totmaps_fields[kPssIndex-1]); 293 DCHECK_EQ("Private_Clean:", totmaps_fields[kPrivate_CleanIndex - 1]); 294 DCHECK_EQ("Private_Dirty:", totmaps_fields[kPrivate_DirtyIndex - 1]); 295 DCHECK_EQ("Swap:", totmaps_fields[kSwapIndex-1]); 296 297 int pss = 0; 298 int private_clean = 0; 299 int private_dirty = 0; 300 int swap = 0; 301 bool ret = true; 302 ret &= StringToInt(totmaps_fields[kPssIndex], &pss); 303 ret &= StringToInt(totmaps_fields[kPrivate_CleanIndex], &private_clean); 304 ret &= StringToInt(totmaps_fields[kPrivate_DirtyIndex], &private_dirty); 305 ret &= StringToInt(totmaps_fields[kSwapIndex], &swap); 306 307 // On ChromeOS swap is to zram. We count this as private / shared, as 308 // increased swap decreases available RAM to user processes, which would 309 // otherwise create surprising results. 310 ws_usage->priv = private_clean + private_dirty + swap; 311 ws_usage->shared = pss + swap; 312 ws_usage->shareable = 0; 313 ws_usage->swapped = swap; 314 return ret; 315 } 316 #endif 317 318 // Private and Shared working set sizes are obtained from /proc/<pid>/statm. 319 bool ProcessMetrics::GetWorkingSetKBytesStatm(WorkingSetKBytes* ws_usage) 320 const { 321 // Use statm instead of smaps because smaps is: 322 // a) Large and slow to parse. 323 // b) Unavailable in the SUID sandbox. 324 325 // First we need to get the page size, since everything is measured in pages. 326 // For details, see: man 5 proc. 327 const int page_size_kb = getpagesize() / 1024; 328 if (page_size_kb <= 0) 329 return false; 330 331 std::string statm; 332 { 333 FilePath statm_file = internal::GetProcPidDir(process_).Append("statm"); 334 // Synchronously reading files in /proc is safe. 335 ThreadRestrictions::ScopedAllowIO allow_io; 336 bool ret = file_util::ReadFileToString(statm_file, &statm); 337 if (!ret || statm.length() == 0) 338 return false; 339 } 340 341 std::vector<std::string> statm_vec; 342 SplitString(statm, ' ', &statm_vec); 343 if (statm_vec.size() != 7) 344 return false; // Not the format we expect. 345 346 int statm_rss, statm_shared; 347 bool ret = true; 348 ret &= StringToInt(statm_vec[1], &statm_rss); 349 ret &= StringToInt(statm_vec[2], &statm_shared); 350 351 ws_usage->priv = (statm_rss - statm_shared) * page_size_kb; 352 ws_usage->shared = statm_shared * page_size_kb; 353 354 // Sharable is not calculated, as it does not provide interesting data. 355 ws_usage->shareable = 0; 356 357 #if defined(OS_CHROMEOS) 358 // Can't get swapped memory from statm. 359 ws_usage->swapped = 0; 360 #endif 361 362 return ret; 363 } 364 365 size_t GetSystemCommitCharge() { 366 SystemMemoryInfoKB meminfo; 367 if (!GetSystemMemoryInfo(&meminfo)) 368 return 0; 369 return meminfo.total - meminfo.free - meminfo.buffers - meminfo.cached; 370 } 371 372 // Exposed for testing. 373 int ParseProcStatCPU(const std::string& input) { 374 std::vector<std::string> proc_stats; 375 if (!internal::ParseProcStats(input, &proc_stats)) 376 return -1; 377 378 if (proc_stats.size() <= internal::VM_STIME) 379 return -1; 380 int utime = GetProcStatsFieldAsInt(proc_stats, internal::VM_UTIME); 381 int stime = GetProcStatsFieldAsInt(proc_stats, internal::VM_STIME); 382 return utime + stime; 383 } 384 385 namespace { 386 387 // The format of /proc/meminfo is: 388 // 389 // MemTotal: 8235324 kB 390 // MemFree: 1628304 kB 391 // Buffers: 429596 kB 392 // Cached: 4728232 kB 393 // ... 394 const size_t kMemTotalIndex = 1; 395 const size_t kMemFreeIndex = 4; 396 const size_t kMemBuffersIndex = 7; 397 const size_t kMemCachedIndex = 10; 398 const size_t kMemActiveAnonIndex = 22; 399 const size_t kMemInactiveAnonIndex = 25; 400 const size_t kMemActiveFileIndex = 28; 401 const size_t kMemInactiveFileIndex = 31; 402 403 } // namespace 404 405 SystemMemoryInfoKB::SystemMemoryInfoKB() 406 : total(0), 407 free(0), 408 buffers(0), 409 cached(0), 410 active_anon(0), 411 inactive_anon(0), 412 active_file(0), 413 inactive_file(0), 414 shmem(0), 415 gem_objects(-1), 416 gem_size(-1) { 417 } 418 419 bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo) { 420 // Synchronously reading files in /proc is safe. 421 ThreadRestrictions::ScopedAllowIO allow_io; 422 423 // Used memory is: total - free - buffers - caches 424 FilePath meminfo_file("/proc/meminfo"); 425 std::string meminfo_data; 426 if (!file_util::ReadFileToString(meminfo_file, &meminfo_data)) { 427 DLOG(WARNING) << "Failed to open " << meminfo_file.value(); 428 return false; 429 } 430 std::vector<std::string> meminfo_fields; 431 SplitStringAlongWhitespace(meminfo_data, &meminfo_fields); 432 433 if (meminfo_fields.size() < kMemCachedIndex) { 434 DLOG(WARNING) << "Failed to parse " << meminfo_file.value() 435 << ". Only found " << meminfo_fields.size() << " fields."; 436 return false; 437 } 438 439 DCHECK_EQ(meminfo_fields[kMemTotalIndex-1], "MemTotal:"); 440 DCHECK_EQ(meminfo_fields[kMemFreeIndex-1], "MemFree:"); 441 DCHECK_EQ(meminfo_fields[kMemBuffersIndex-1], "Buffers:"); 442 DCHECK_EQ(meminfo_fields[kMemCachedIndex-1], "Cached:"); 443 DCHECK_EQ(meminfo_fields[kMemActiveAnonIndex-1], "Active(anon):"); 444 DCHECK_EQ(meminfo_fields[kMemInactiveAnonIndex-1], "Inactive(anon):"); 445 DCHECK_EQ(meminfo_fields[kMemActiveFileIndex-1], "Active(file):"); 446 DCHECK_EQ(meminfo_fields[kMemInactiveFileIndex-1], "Inactive(file):"); 447 448 StringToInt(meminfo_fields[kMemTotalIndex], &meminfo->total); 449 StringToInt(meminfo_fields[kMemFreeIndex], &meminfo->free); 450 StringToInt(meminfo_fields[kMemBuffersIndex], &meminfo->buffers); 451 StringToInt(meminfo_fields[kMemCachedIndex], &meminfo->cached); 452 StringToInt(meminfo_fields[kMemActiveAnonIndex], &meminfo->active_anon); 453 StringToInt(meminfo_fields[kMemInactiveAnonIndex], 454 &meminfo->inactive_anon); 455 StringToInt(meminfo_fields[kMemActiveFileIndex], &meminfo->active_file); 456 StringToInt(meminfo_fields[kMemInactiveFileIndex], 457 &meminfo->inactive_file); 458 #if defined(OS_CHROMEOS) 459 // Chrome OS has a tweaked kernel that allows us to query Shmem, which is 460 // usually video memory otherwise invisible to the OS. Unfortunately, the 461 // meminfo format varies on different hardware so we have to search for the 462 // string. It always appears after "Cached:". 463 for (size_t i = kMemCachedIndex+2; i < meminfo_fields.size(); i += 3) { 464 if (meminfo_fields[i] == "Shmem:") { 465 StringToInt(meminfo_fields[i+1], &meminfo->shmem); 466 break; 467 } 468 } 469 470 // Report on Chrome OS GEM object graphics memory. /var/run/debugfs_gpu is a 471 // bind mount into /sys/kernel/debug and synchronously reading the in-memory 472 // files in /sys is fast. 473 #if defined(ARCH_CPU_ARM_FAMILY) 474 FilePath geminfo_file("/var/run/debugfs_gpu/exynos_gem_objects"); 475 #else 476 FilePath geminfo_file("/var/run/debugfs_gpu/i915_gem_objects"); 477 #endif 478 std::string geminfo_data; 479 meminfo->gem_objects = -1; 480 meminfo->gem_size = -1; 481 if (file_util::ReadFileToString(geminfo_file, &geminfo_data)) { 482 int gem_objects = -1; 483 long long gem_size = -1; 484 int num_res = sscanf(geminfo_data.c_str(), 485 "%d objects, %lld bytes", 486 &gem_objects, &gem_size); 487 if (num_res == 2) { 488 meminfo->gem_objects = gem_objects; 489 meminfo->gem_size = gem_size; 490 } 491 } 492 493 #if defined(ARCH_CPU_ARM_FAMILY) 494 // Incorporate Mali graphics memory if present. 495 FilePath mali_memory_file("/sys/devices/platform/mali.0/memory"); 496 std::string mali_memory_data; 497 if (file_util::ReadFileToString(mali_memory_file, &mali_memory_data)) { 498 long long mali_size = -1; 499 int num_res = sscanf(mali_memory_data.c_str(), "%lld bytes", &mali_size); 500 if (num_res == 1) 501 meminfo->gem_size += mali_size; 502 } 503 #endif // defined(ARCH_CPU_ARM_FAMILY) 504 #endif // defined(OS_CHROMEOS) 505 506 return true; 507 } 508 509 const char kProcSelfExe[] = "/proc/self/exe"; 510 511 int GetNumberOfThreads(ProcessHandle process) { 512 return internal::ReadProcStatsAndGetFieldAsInt(process, 513 internal::VM_NUMTHREADS); 514 } 515 516 } // namespace base 517