rsesek@chromium.org | 32f5e9a | 2013-05-23 12:59:54 | [diff] [blame] | 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> |
davemoore@chromium.org | 7b9c9887 | 2013-05-29 15:42:04 | [diff] [blame] | 8 | #include <fcntl.h> |
| 9 | #include <sys/stat.h> |
rsesek@chromium.org | 32f5e9a | 2013-05-23 12:59:54 | [diff] [blame] | 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" |
rsesek@chromium.org | 32f5e9a | 2013-05-23 12:59:54 | [diff] [blame] | 17 | #include "base/strings/string_number_conversions.h" |
| 18 | #include "base/strings/string_split.h" |
| 19 | #include "base/strings/string_tokenizer.h" |
avi@chromium.org | d1a5a2f | 2013-06-10 21:17:40 | [diff] [blame] | 20 | #include "base/strings/string_util.h" |
rsesek@chromium.org | 32f5e9a | 2013-05-23 12:59:54 | [diff] [blame] | 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 | |
nduca@chromium.org | f413478 | 2013-08-29 21:25:20 | [diff] [blame] | 33 | #ifdef OS_CHROMEOS |
| 34 | // Read a file with a single number string and return the number as a uint64. |
| 35 | static uint64 ReadFileToUint64(const base::FilePath file) { |
| 36 | std::string file_as_string; |
brettw@chromium.org | 82f84b9 | 2013-08-30 18:23:50 | [diff] [blame] | 37 | if (!ReadFileToString(file, &file_as_string)) |
nduca@chromium.org | f413478 | 2013-08-29 21:25:20 | [diff] [blame] | 38 | return 0; |
| 39 | TrimWhitespaceASCII(file_as_string, TRIM_ALL, &file_as_string); |
| 40 | uint64 file_as_uint64 = 0; |
| 41 | if (!base::StringToUint64(file_as_string, &file_as_uint64)) |
| 42 | return 0; |
| 43 | return file_as_uint64; |
| 44 | } |
| 45 | #endif |
| 46 | |
rsesek@chromium.org | 32f5e9a | 2013-05-23 12:59:54 | [diff] [blame] | 47 | // Read /proc/<pid>/status and returns the value for |field|, or 0 on failure. |
| 48 | // Only works for fields in the form of "Field: value kB". |
| 49 | size_t ReadProcStatusAndGetFieldAsSizeT(pid_t pid, const std::string& field) { |
| 50 | FilePath stat_file = internal::GetProcPidDir(pid).Append("status"); |
| 51 | std::string status; |
| 52 | { |
| 53 | // Synchronously reading files in /proc is safe. |
| 54 | ThreadRestrictions::ScopedAllowIO allow_io; |
brettw@chromium.org | 82f84b9 | 2013-08-30 18:23:50 | [diff] [blame] | 55 | if (!ReadFileToString(stat_file, &status)) |
rsesek@chromium.org | 32f5e9a | 2013-05-23 12:59:54 | [diff] [blame] | 56 | return 0; |
| 57 | } |
| 58 | |
| 59 | StringTokenizer tokenizer(status, ":\n"); |
| 60 | ParsingState state = KEY_NAME; |
| 61 | StringPiece last_key_name; |
| 62 | while (tokenizer.GetNext()) { |
| 63 | switch (state) { |
| 64 | case KEY_NAME: |
| 65 | last_key_name = tokenizer.token_piece(); |
| 66 | state = KEY_VALUE; |
| 67 | break; |
| 68 | case KEY_VALUE: |
| 69 | DCHECK(!last_key_name.empty()); |
| 70 | if (last_key_name == field) { |
| 71 | std::string value_str; |
| 72 | tokenizer.token_piece().CopyToString(&value_str); |
| 73 | std::string value_str_trimmed; |
| 74 | TrimWhitespaceASCII(value_str, TRIM_ALL, &value_str_trimmed); |
| 75 | std::vector<std::string> split_value_str; |
| 76 | SplitString(value_str_trimmed, ' ', &split_value_str); |
| 77 | if (split_value_str.size() != 2 || split_value_str[1] != "kB") { |
| 78 | NOTREACHED(); |
| 79 | return 0; |
| 80 | } |
| 81 | size_t value; |
| 82 | if (!StringToSizeT(split_value_str[0], &value)) { |
| 83 | NOTREACHED(); |
| 84 | return 0; |
| 85 | } |
| 86 | return value; |
| 87 | } |
| 88 | state = KEY_NAME; |
| 89 | break; |
| 90 | } |
| 91 | } |
| 92 | NOTREACHED(); |
| 93 | return 0; |
| 94 | } |
| 95 | |
| 96 | // Get the total CPU of a single process. Return value is number of jiffies |
| 97 | // on success or -1 on error. |
| 98 | int GetProcessCPU(pid_t pid) { |
| 99 | // Use /proc/<pid>/task to find all threads and parse their /stat file. |
| 100 | FilePath task_path = internal::GetProcPidDir(pid).Append("task"); |
| 101 | |
| 102 | DIR* dir = opendir(task_path.value().c_str()); |
| 103 | if (!dir) { |
| 104 | DPLOG(ERROR) << "opendir(" << task_path.value() << ")"; |
| 105 | return -1; |
| 106 | } |
| 107 | |
| 108 | int total_cpu = 0; |
| 109 | while (struct dirent* ent = readdir(dir)) { |
| 110 | pid_t tid = internal::ProcDirSlotToPid(ent->d_name); |
| 111 | if (!tid) |
| 112 | continue; |
| 113 | |
| 114 | // Synchronously reading files in /proc is safe. |
| 115 | ThreadRestrictions::ScopedAllowIO allow_io; |
| 116 | |
| 117 | std::string stat; |
| 118 | FilePath stat_path = |
| 119 | task_path.Append(ent->d_name).Append(internal::kStatFile); |
brettw@chromium.org | 82f84b9 | 2013-08-30 18:23:50 | [diff] [blame] | 120 | if (ReadFileToString(stat_path, &stat)) { |
rsesek@chromium.org | 32f5e9a | 2013-05-23 12:59:54 | [diff] [blame] | 121 | int cpu = ParseProcStatCPU(stat); |
| 122 | if (cpu > 0) |
| 123 | total_cpu += cpu; |
| 124 | } |
| 125 | } |
| 126 | closedir(dir); |
| 127 | |
| 128 | return total_cpu; |
| 129 | } |
| 130 | |
| 131 | } // namespace |
| 132 | |
| 133 | // static |
| 134 | ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) { |
| 135 | return new ProcessMetrics(process); |
| 136 | } |
| 137 | |
| 138 | // On linux, we return vsize. |
| 139 | size_t ProcessMetrics::GetPagefileUsage() const { |
| 140 | return internal::ReadProcStatsAndGetFieldAsSizeT(process_, |
| 141 | internal::VM_VSIZE); |
| 142 | } |
| 143 | |
| 144 | // On linux, we return the high water mark of vsize. |
| 145 | size_t ProcessMetrics::GetPeakPagefileUsage() const { |
| 146 | return ReadProcStatusAndGetFieldAsSizeT(process_, "VmPeak") * 1024; |
| 147 | } |
| 148 | |
| 149 | // On linux, we return RSS. |
| 150 | size_t ProcessMetrics::GetWorkingSetSize() const { |
| 151 | return internal::ReadProcStatsAndGetFieldAsSizeT(process_, internal::VM_RSS) * |
| 152 | getpagesize(); |
| 153 | } |
| 154 | |
| 155 | // On linux, we return the high water mark of RSS. |
| 156 | size_t ProcessMetrics::GetPeakWorkingSetSize() const { |
| 157 | return ReadProcStatusAndGetFieldAsSizeT(process_, "VmHWM") * 1024; |
| 158 | } |
| 159 | |
| 160 | bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes, |
| 161 | size_t* shared_bytes) { |
| 162 | WorkingSetKBytes ws_usage; |
| 163 | if (!GetWorkingSetKBytes(&ws_usage)) |
| 164 | return false; |
| 165 | |
| 166 | if (private_bytes) |
| 167 | *private_bytes = ws_usage.priv * 1024; |
| 168 | |
| 169 | if (shared_bytes) |
| 170 | *shared_bytes = ws_usage.shared * 1024; |
| 171 | |
| 172 | return true; |
| 173 | } |
| 174 | |
rsesek@chromium.org | 32f5e9a | 2013-05-23 12:59:54 | [diff] [blame] | 175 | bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const { |
davemoore@chromium.org | 7b9c9887 | 2013-05-29 15:42:04 | [diff] [blame] | 176 | #if defined(OS_CHROMEOS) |
| 177 | if (GetWorkingSetKBytesTotmaps(ws_usage)) |
| 178 | return true; |
| 179 | #endif |
| 180 | return GetWorkingSetKBytesStatm(ws_usage); |
rsesek@chromium.org | 32f5e9a | 2013-05-23 12:59:54 | [diff] [blame] | 181 | } |
| 182 | |
| 183 | double ProcessMetrics::GetCPUUsage() { |
rsesek@chromium.org | 32f5e9a | 2013-05-23 12:59:54 | [diff] [blame] | 184 | struct timeval now; |
| 185 | int retval = gettimeofday(&now, NULL); |
| 186 | if (retval) |
| 187 | return 0; |
| 188 | int64 time = TimeValToMicroseconds(now); |
| 189 | |
| 190 | if (last_time_ == 0) { |
| 191 | // First call, just set the last values. |
| 192 | last_time_ = time; |
| 193 | last_cpu_ = GetProcessCPU(process_); |
| 194 | return 0; |
| 195 | } |
| 196 | |
| 197 | int64 time_delta = time - last_time_; |
| 198 | DCHECK_NE(time_delta, 0); |
| 199 | if (time_delta == 0) |
| 200 | return 0; |
| 201 | |
| 202 | int cpu = GetProcessCPU(process_); |
| 203 | |
| 204 | // We have the number of jiffies in the time period. Convert to percentage. |
| 205 | // Note this means we will go *over* 100 in the case where multiple threads |
| 206 | // are together adding to more than one CPU's worth. |
simonjam@chromium.org | 36e8fd4 | 2013-08-08 17:24:18 | [diff] [blame] | 207 | TimeDelta cpu_time = internal::ClockTicksToTimeDelta(cpu); |
| 208 | TimeDelta last_cpu_time = internal::ClockTicksToTimeDelta(last_cpu_); |
| 209 | int percentage = 100 * (cpu_time - last_cpu_time).InSecondsF() / |
| 210 | TimeDelta::FromMicroseconds(time_delta).InSecondsF(); |
rsesek@chromium.org | 32f5e9a | 2013-05-23 12:59:54 | [diff] [blame] | 211 | |
| 212 | last_time_ = time; |
| 213 | last_cpu_ = cpu; |
| 214 | |
| 215 | return percentage; |
| 216 | } |
| 217 | |
| 218 | // To have /proc/self/io file you must enable CONFIG_TASK_IO_ACCOUNTING |
| 219 | // in your kernel configuration. |
| 220 | bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { |
| 221 | // Synchronously reading files in /proc is safe. |
| 222 | ThreadRestrictions::ScopedAllowIO allow_io; |
| 223 | |
| 224 | std::string proc_io_contents; |
| 225 | FilePath io_file = internal::GetProcPidDir(process_).Append("io"); |
brettw@chromium.org | 82f84b9 | 2013-08-30 18:23:50 | [diff] [blame] | 226 | if (!ReadFileToString(io_file, &proc_io_contents)) |
rsesek@chromium.org | 32f5e9a | 2013-05-23 12:59:54 | [diff] [blame] | 227 | return false; |
| 228 | |
| 229 | (*io_counters).OtherOperationCount = 0; |
| 230 | (*io_counters).OtherTransferCount = 0; |
| 231 | |
| 232 | StringTokenizer tokenizer(proc_io_contents, ": \n"); |
| 233 | ParsingState state = KEY_NAME; |
| 234 | StringPiece last_key_name; |
| 235 | while (tokenizer.GetNext()) { |
| 236 | switch (state) { |
| 237 | case KEY_NAME: |
| 238 | last_key_name = tokenizer.token_piece(); |
| 239 | state = KEY_VALUE; |
| 240 | break; |
| 241 | case KEY_VALUE: |
| 242 | DCHECK(!last_key_name.empty()); |
| 243 | if (last_key_name == "syscr") { |
| 244 | StringToInt64(tokenizer.token_piece(), |
| 245 | reinterpret_cast<int64*>(&(*io_counters).ReadOperationCount)); |
| 246 | } else if (last_key_name == "syscw") { |
| 247 | StringToInt64(tokenizer.token_piece(), |
| 248 | reinterpret_cast<int64*>(&(*io_counters).WriteOperationCount)); |
| 249 | } else if (last_key_name == "rchar") { |
| 250 | StringToInt64(tokenizer.token_piece(), |
| 251 | reinterpret_cast<int64*>(&(*io_counters).ReadTransferCount)); |
| 252 | } else if (last_key_name == "wchar") { |
| 253 | StringToInt64(tokenizer.token_piece(), |
| 254 | reinterpret_cast<int64*>(&(*io_counters).WriteTransferCount)); |
| 255 | } |
| 256 | state = KEY_NAME; |
| 257 | break; |
| 258 | } |
| 259 | } |
| 260 | return true; |
| 261 | } |
| 262 | |
| 263 | ProcessMetrics::ProcessMetrics(ProcessHandle process) |
| 264 | : process_(process), |
| 265 | last_time_(0), |
| 266 | last_system_time_(0), |
| 267 | last_cpu_(0) { |
| 268 | processor_count_ = base::SysInfo::NumberOfProcessors(); |
davemoore@chromium.org | c8db21c | 2013-05-28 20:16:49 | [diff] [blame] | 269 | } |
| 270 | |
davemoore@chromium.org | 7b9c9887 | 2013-05-29 15:42:04 | [diff] [blame] | 271 | #if defined(OS_CHROMEOS) |
| 272 | // Private, Shared and Proportional working set sizes are obtained from |
| 273 | // /proc/<pid>/totmaps |
| 274 | bool ProcessMetrics::GetWorkingSetKBytesTotmaps(WorkingSetKBytes *ws_usage) |
| 275 | const { |
| 276 | // The format of /proc/<pid>/totmaps is: |
| 277 | // |
| 278 | // Rss: 6120 kB |
| 279 | // Pss: 3335 kB |
| 280 | // Shared_Clean: 1008 kB |
| 281 | // Shared_Dirty: 4012 kB |
| 282 | // Private_Clean: 4 kB |
| 283 | // Private_Dirty: 1096 kB |
davemoore@chromium.org | 23deabb7 | 2013-07-16 21:55:20 | [diff] [blame] | 284 | // Referenced: XXX kB |
| 285 | // Anonymous: XXX kB |
| 286 | // AnonHugePages: XXX kB |
| 287 | // Swap: XXX kB |
| 288 | // Locked: XXX kB |
| 289 | const size_t kPssIndex = (1 * 3) + 1; |
| 290 | const size_t kPrivate_CleanIndex = (4 * 3) + 1; |
| 291 | const size_t kPrivate_DirtyIndex = (5 * 3) + 1; |
| 292 | const size_t kSwapIndex = (9 * 3) + 1; |
davemoore@chromium.org | 7b9c9887 | 2013-05-29 15:42:04 | [diff] [blame] | 293 | |
| 294 | std::string totmaps_data; |
| 295 | { |
| 296 | FilePath totmaps_file = internal::GetProcPidDir(process_).Append("totmaps"); |
| 297 | ThreadRestrictions::ScopedAllowIO allow_io; |
brettw@chromium.org | 82f84b9 | 2013-08-30 18:23:50 | [diff] [blame] | 298 | bool ret = ReadFileToString(totmaps_file, &totmaps_data); |
davemoore@chromium.org | 7b9c9887 | 2013-05-29 15:42:04 | [diff] [blame] | 299 | if (!ret || totmaps_data.length() == 0) |
| 300 | return false; |
| 301 | } |
| 302 | |
| 303 | std::vector<std::string> totmaps_fields; |
| 304 | SplitStringAlongWhitespace(totmaps_data, &totmaps_fields); |
| 305 | |
| 306 | DCHECK_EQ("Pss:", totmaps_fields[kPssIndex-1]); |
davemoore@chromium.org | 23deabb7 | 2013-07-16 21:55:20 | [diff] [blame] | 307 | DCHECK_EQ("Private_Clean:", totmaps_fields[kPrivate_CleanIndex - 1]); |
| 308 | DCHECK_EQ("Private_Dirty:", totmaps_fields[kPrivate_DirtyIndex - 1]); |
| 309 | DCHECK_EQ("Swap:", totmaps_fields[kSwapIndex-1]); |
davemoore@chromium.org | 7b9c9887 | 2013-05-29 15:42:04 | [diff] [blame] | 310 | |
davemoore@chromium.org | 23deabb7 | 2013-07-16 21:55:20 | [diff] [blame] | 311 | int pss = 0; |
| 312 | int private_clean = 0; |
| 313 | int private_dirty = 0; |
| 314 | int swap = 0; |
davemoore@chromium.org | 7b9c9887 | 2013-05-29 15:42:04 | [diff] [blame] | 315 | bool ret = true; |
| 316 | ret &= StringToInt(totmaps_fields[kPssIndex], &pss); |
| 317 | ret &= StringToInt(totmaps_fields[kPrivate_CleanIndex], &private_clean); |
| 318 | ret &= StringToInt(totmaps_fields[kPrivate_DirtyIndex], &private_dirty); |
davemoore@chromium.org | 23deabb7 | 2013-07-16 21:55:20 | [diff] [blame] | 319 | ret &= StringToInt(totmaps_fields[kSwapIndex], &swap); |
davemoore@chromium.org | 7b9c9887 | 2013-05-29 15:42:04 | [diff] [blame] | 320 | |
davemoore@chromium.org | 23deabb7 | 2013-07-16 21:55:20 | [diff] [blame] | 321 | // On ChromeOS swap is to zram. We count this as private / shared, as |
| 322 | // increased swap decreases available RAM to user processes, which would |
| 323 | // otherwise create surprising results. |
| 324 | ws_usage->priv = private_clean + private_dirty + swap; |
| 325 | ws_usage->shared = pss + swap; |
davemoore@chromium.org | 7b9c9887 | 2013-05-29 15:42:04 | [diff] [blame] | 326 | ws_usage->shareable = 0; |
davemoore@chromium.org | 23deabb7 | 2013-07-16 21:55:20 | [diff] [blame] | 327 | ws_usage->swapped = swap; |
davemoore@chromium.org | 7b9c9887 | 2013-05-29 15:42:04 | [diff] [blame] | 328 | return ret; |
| 329 | } |
| 330 | #endif |
| 331 | |
| 332 | // Private and Shared working set sizes are obtained from /proc/<pid>/statm. |
| 333 | bool ProcessMetrics::GetWorkingSetKBytesStatm(WorkingSetKBytes* ws_usage) |
| 334 | const { |
| 335 | // Use statm instead of smaps because smaps is: |
| 336 | // a) Large and slow to parse. |
| 337 | // b) Unavailable in the SUID sandbox. |
| 338 | |
| 339 | // First we need to get the page size, since everything is measured in pages. |
| 340 | // For details, see: man 5 proc. |
| 341 | const int page_size_kb = getpagesize() / 1024; |
| 342 | if (page_size_kb <= 0) |
| 343 | return false; |
| 344 | |
| 345 | std::string statm; |
| 346 | { |
| 347 | FilePath statm_file = internal::GetProcPidDir(process_).Append("statm"); |
| 348 | // Synchronously reading files in /proc is safe. |
| 349 | ThreadRestrictions::ScopedAllowIO allow_io; |
brettw@chromium.org | 82f84b9 | 2013-08-30 18:23:50 | [diff] [blame] | 350 | bool ret = ReadFileToString(statm_file, &statm); |
davemoore@chromium.org | 7b9c9887 | 2013-05-29 15:42:04 | [diff] [blame] | 351 | if (!ret || statm.length() == 0) |
| 352 | return false; |
| 353 | } |
| 354 | |
| 355 | std::vector<std::string> statm_vec; |
| 356 | SplitString(statm, ' ', &statm_vec); |
| 357 | if (statm_vec.size() != 7) |
| 358 | return false; // Not the format we expect. |
| 359 | |
| 360 | int statm_rss, statm_shared; |
| 361 | bool ret = true; |
| 362 | ret &= StringToInt(statm_vec[1], &statm_rss); |
| 363 | ret &= StringToInt(statm_vec[2], &statm_shared); |
| 364 | |
| 365 | ws_usage->priv = (statm_rss - statm_shared) * page_size_kb; |
| 366 | ws_usage->shared = statm_shared * page_size_kb; |
| 367 | |
| 368 | // Sharable is not calculated, as it does not provide interesting data. |
| 369 | ws_usage->shareable = 0; |
| 370 | |
davemoore@chromium.org | 23deabb7 | 2013-07-16 21:55:20 | [diff] [blame] | 371 | #if defined(OS_CHROMEOS) |
| 372 | // Can't get swapped memory from statm. |
| 373 | ws_usage->swapped = 0; |
| 374 | #endif |
| 375 | |
davemoore@chromium.org | 7b9c9887 | 2013-05-29 15:42:04 | [diff] [blame] | 376 | return ret; |
| 377 | } |
| 378 | |
rsesek@chromium.org | 32f5e9a | 2013-05-23 12:59:54 | [diff] [blame] | 379 | size_t GetSystemCommitCharge() { |
| 380 | SystemMemoryInfoKB meminfo; |
| 381 | if (!GetSystemMemoryInfo(&meminfo)) |
| 382 | return 0; |
| 383 | return meminfo.total - meminfo.free - meminfo.buffers - meminfo.cached; |
| 384 | } |
| 385 | |
| 386 | // Exposed for testing. |
| 387 | int ParseProcStatCPU(const std::string& input) { |
| 388 | std::vector<std::string> proc_stats; |
| 389 | if (!internal::ParseProcStats(input, &proc_stats)) |
| 390 | return -1; |
| 391 | |
| 392 | if (proc_stats.size() <= internal::VM_STIME) |
| 393 | return -1; |
| 394 | int utime = GetProcStatsFieldAsInt(proc_stats, internal::VM_UTIME); |
| 395 | int stime = GetProcStatsFieldAsInt(proc_stats, internal::VM_STIME); |
| 396 | return utime + stime; |
| 397 | } |
| 398 | |
jwmak@chromium.org | 103ff351 | 2013-08-22 07:19:30 | [diff] [blame] | 399 | const char kProcSelfExe[] = "/proc/self/exe"; |
| 400 | |
| 401 | int GetNumberOfThreads(ProcessHandle process) { |
| 402 | return internal::ReadProcStatsAndGetFieldAsInt(process, |
| 403 | internal::VM_NUMTHREADS); |
| 404 | } |
| 405 | |
rsesek@chromium.org | 32f5e9a | 2013-05-23 12:59:54 | [diff] [blame] | 406 | namespace { |
| 407 | |
| 408 | // The format of /proc/meminfo is: |
| 409 | // |
| 410 | // MemTotal: 8235324 kB |
| 411 | // MemFree: 1628304 kB |
| 412 | // Buffers: 429596 kB |
| 413 | // Cached: 4728232 kB |
| 414 | // ... |
| 415 | const size_t kMemTotalIndex = 1; |
| 416 | const size_t kMemFreeIndex = 4; |
| 417 | const size_t kMemBuffersIndex = 7; |
| 418 | const size_t kMemCachedIndex = 10; |
| 419 | const size_t kMemActiveAnonIndex = 22; |
| 420 | const size_t kMemInactiveAnonIndex = 25; |
| 421 | const size_t kMemActiveFileIndex = 28; |
| 422 | const size_t kMemInactiveFileIndex = 31; |
| 423 | |
jwmak@chromium.org | 49b0cf8 | 2013-09-03 23:46:54 | [diff] [blame] | 424 | // The format of /proc/vmstat is: |
| 425 | // |
| 426 | // nr_free_pages 299878 |
| 427 | // nr_inactive_anon 239863 |
| 428 | // nr_active_anon 1318966 |
| 429 | // nr_inactive_file 2015629 |
| 430 | // ... |
| 431 | const size_t kVMPagesSwappedIn = 75; |
| 432 | const size_t kVMPagesSwappedOut = 77; |
| 433 | const size_t kVMPageMajorFaults = 95; |
| 434 | |
nduca@chromium.org | 669a0933 | 2013-08-30 22:59:14 | [diff] [blame] | 435 | // The format of /proc/diskstats is: |
| 436 | // Device major number |
| 437 | // Device minor number |
| 438 | // Device name |
| 439 | // Field 1 -- # of reads completed |
| 440 | // This is the total number of reads completed successfully. |
| 441 | // Field 2 -- # of reads merged, field 6 -- # of writes merged |
| 442 | // Reads and writes which are adjacent to each other may be merged for |
| 443 | // efficiency. Thus two 4K reads may become one 8K read before it is |
| 444 | // ultimately handed to the disk, and so it will be counted (and queued) |
| 445 | // as only one I/O. This field lets you know how often this was done. |
| 446 | // Field 3 -- # of sectors read |
| 447 | // This is the total number of sectors read successfully. |
| 448 | // Field 4 -- # of milliseconds spent reading |
| 449 | // This is the total number of milliseconds spent by all reads (as |
| 450 | // measured from __make_request() to end_that_request_last()). |
| 451 | // Field 5 -- # of writes completed |
| 452 | // This is the total number of writes completed successfully. |
| 453 | // Field 6 -- # of writes merged |
| 454 | // See the description of field 2. |
| 455 | // Field 7 -- # of sectors written |
| 456 | // This is the total number of sectors written successfully. |
| 457 | // Field 8 -- # of milliseconds spent writing |
| 458 | // This is the total number of milliseconds spent by all writes (as |
| 459 | // measured from __make_request() to end_that_request_last()). |
| 460 | // Field 9 -- # of I/Os currently in progress |
| 461 | // The only field that should go to zero. Incremented as requests are |
| 462 | // given to appropriate struct request_queue and decremented as they |
| 463 | // finish. |
| 464 | // Field 10 -- # of milliseconds spent doing I/Os |
| 465 | // This field increases so long as field 9 is nonzero. |
| 466 | // Field 11 -- weighted # of milliseconds spent doing I/Os |
| 467 | // This field is incremented at each I/O start, I/O completion, I/O |
| 468 | // merge, or read of these stats by the number of I/Os in progress |
| 469 | // (field 9) times the number of milliseconds spent doing I/O since the |
| 470 | // last update of this field. This can provide an easy measure of both |
| 471 | // I/O completion time and the backlog that may be accumulating. |
| 472 | |
| 473 | const size_t kDiskDriveName = 2; |
| 474 | const size_t kDiskReads = 3; |
| 475 | const size_t kDiskReadsMerged = 4; |
| 476 | const size_t kDiskSectorsRead = 5; |
| 477 | const size_t kDiskReadTime = 6; |
| 478 | const size_t kDiskWrites = 7; |
| 479 | const size_t kDiskWritesMerged = 8; |
| 480 | const size_t kDiskSectorsWritten = 9; |
| 481 | const size_t kDiskWriteTime = 10; |
| 482 | const size_t kDiskIO = 11; |
| 483 | const size_t kDiskIOTime = 12; |
| 484 | const size_t kDiskWeightedIOTime = 13; |
| 485 | |
rsesek@chromium.org | 32f5e9a | 2013-05-23 12:59:54 | [diff] [blame] | 486 | } // namespace |
| 487 | |
jwmak@chromium.org | 49b0cf8 | 2013-09-03 23:46:54 | [diff] [blame] | 488 | SystemMemoryInfoKB::SystemMemoryInfoKB() { |
| 489 | total = 0; |
| 490 | free = 0; |
| 491 | buffers = 0; |
| 492 | cached = 0; |
| 493 | active_anon = 0; |
| 494 | inactive_anon = 0; |
| 495 | active_file = 0; |
| 496 | inactive_file = 0; |
| 497 | swap_total = 0; |
| 498 | swap_free = 0; |
| 499 | dirty = 0; |
| 500 | |
| 501 | pswpin = 0; |
| 502 | pswpout = 0; |
| 503 | pgmajfault = 0; |
| 504 | |
| 505 | #ifdef OS_CHROMEOS |
| 506 | shmem = 0; |
| 507 | slab = 0; |
| 508 | gem_objects = -1; |
| 509 | gem_size = -1; |
| 510 | #endif |
rsesek@chromium.org | 32f5e9a | 2013-05-23 12:59:54 | [diff] [blame] | 511 | } |
| 512 | |
jwmak@chromium.org | c2ef94e | 2013-09-06 13:13:23 | [diff] [blame] | 513 | scoped_ptr<Value> SystemMemoryInfoKB::ToValue() const { |
| 514 | scoped_ptr<DictionaryValue> res(new base::DictionaryValue()); |
| 515 | |
| 516 | res->SetInteger("total", total); |
| 517 | res->SetInteger("free", free); |
| 518 | res->SetInteger("buffers", buffers); |
| 519 | res->SetInteger("cached", cached); |
| 520 | res->SetInteger("active_anon", active_anon); |
| 521 | res->SetInteger("inactive_anon", inactive_anon); |
| 522 | res->SetInteger("active_file", active_file); |
| 523 | res->SetInteger("inactive_file", inactive_file); |
| 524 | res->SetInteger("swap_total", swap_total); |
| 525 | res->SetInteger("swap_free", swap_free); |
| 526 | res->SetInteger("swap_used", swap_total - swap_free); |
| 527 | res->SetInteger("dirty", dirty); |
| 528 | res->SetInteger("pswpin", pswpin); |
| 529 | res->SetInteger("pswpout", pswpout); |
| 530 | res->SetInteger("pgmajfault", pgmajfault); |
| 531 | #ifdef OS_CHROMEOS |
| 532 | res->SetInteger("shmem", shmem); |
| 533 | res->SetInteger("slab", slab); |
| 534 | res->SetInteger("gem_objects", gem_objects); |
| 535 | res->SetInteger("gem_size", gem_size); |
| 536 | #endif |
| 537 | |
| 538 | return res.PassAs<Value>(); |
| 539 | } |
| 540 | |
davemoore@chromium.org | 70baebc | 2013-11-02 16:58:44 | [diff] [blame^] | 541 | // exposed for testing |
| 542 | bool ParseProcMeminfo(const std::string& meminfo_data, |
| 543 | SystemMemoryInfoKB* meminfo) { |
rsesek@chromium.org | 32f5e9a | 2013-05-23 12:59:54 | [diff] [blame] | 544 | std::vector<std::string> meminfo_fields; |
| 545 | SplitStringAlongWhitespace(meminfo_data, &meminfo_fields); |
| 546 | |
| 547 | if (meminfo_fields.size() < kMemCachedIndex) { |
rsesek@chromium.org | 32f5e9a | 2013-05-23 12:59:54 | [diff] [blame] | 548 | return false; |
| 549 | } |
| 550 | |
| 551 | DCHECK_EQ(meminfo_fields[kMemTotalIndex-1], "MemTotal:"); |
| 552 | DCHECK_EQ(meminfo_fields[kMemFreeIndex-1], "MemFree:"); |
| 553 | DCHECK_EQ(meminfo_fields[kMemBuffersIndex-1], "Buffers:"); |
| 554 | DCHECK_EQ(meminfo_fields[kMemCachedIndex-1], "Cached:"); |
| 555 | DCHECK_EQ(meminfo_fields[kMemActiveAnonIndex-1], "Active(anon):"); |
| 556 | DCHECK_EQ(meminfo_fields[kMemInactiveAnonIndex-1], "Inactive(anon):"); |
| 557 | DCHECK_EQ(meminfo_fields[kMemActiveFileIndex-1], "Active(file):"); |
| 558 | DCHECK_EQ(meminfo_fields[kMemInactiveFileIndex-1], "Inactive(file):"); |
| 559 | |
| 560 | StringToInt(meminfo_fields[kMemTotalIndex], &meminfo->total); |
| 561 | StringToInt(meminfo_fields[kMemFreeIndex], &meminfo->free); |
| 562 | StringToInt(meminfo_fields[kMemBuffersIndex], &meminfo->buffers); |
| 563 | StringToInt(meminfo_fields[kMemCachedIndex], &meminfo->cached); |
| 564 | StringToInt(meminfo_fields[kMemActiveAnonIndex], &meminfo->active_anon); |
jwmak@chromium.org | 49b0cf8 | 2013-09-03 23:46:54 | [diff] [blame] | 565 | StringToInt(meminfo_fields[kMemInactiveAnonIndex], &meminfo->inactive_anon); |
rsesek@chromium.org | 32f5e9a | 2013-05-23 12:59:54 | [diff] [blame] | 566 | StringToInt(meminfo_fields[kMemActiveFileIndex], &meminfo->active_file); |
jwmak@chromium.org | 49b0cf8 | 2013-09-03 23:46:54 | [diff] [blame] | 567 | StringToInt(meminfo_fields[kMemInactiveFileIndex], &meminfo->inactive_file); |
| 568 | |
| 569 | // We don't know when these fields appear, so we must search for them. |
| 570 | for (size_t i = kMemCachedIndex+2; i < meminfo_fields.size(); i += 3) { |
| 571 | if (meminfo_fields[i] == "SwapTotal:") |
| 572 | StringToInt(meminfo_fields[i+1], &meminfo->swap_total); |
| 573 | if (meminfo_fields[i] == "SwapFree:") |
| 574 | StringToInt(meminfo_fields[i+1], &meminfo->swap_free); |
| 575 | if (meminfo_fields[i] == "Dirty:") |
| 576 | StringToInt(meminfo_fields[i+1], &meminfo->dirty); |
| 577 | } |
| 578 | |
rsesek@chromium.org | 32f5e9a | 2013-05-23 12:59:54 | [diff] [blame] | 579 | #if defined(OS_CHROMEOS) |
| 580 | // Chrome OS has a tweaked kernel that allows us to query Shmem, which is |
| 581 | // usually video memory otherwise invisible to the OS. Unfortunately, the |
| 582 | // meminfo format varies on different hardware so we have to search for the |
| 583 | // string. It always appears after "Cached:". |
| 584 | for (size_t i = kMemCachedIndex+2; i < meminfo_fields.size(); i += 3) { |
jwmak@chromium.org | 49b0cf8 | 2013-09-03 23:46:54 | [diff] [blame] | 585 | if (meminfo_fields[i] == "Shmem:") |
rsesek@chromium.org | 32f5e9a | 2013-05-23 12:59:54 | [diff] [blame] | 586 | StringToInt(meminfo_fields[i+1], &meminfo->shmem); |
jwmak@chromium.org | 49b0cf8 | 2013-09-03 23:46:54 | [diff] [blame] | 587 | if (meminfo_fields[i] == "Slab:") |
| 588 | StringToInt(meminfo_fields[i+1], &meminfo->slab); |
rsesek@chromium.org | 32f5e9a | 2013-05-23 12:59:54 | [diff] [blame] | 589 | } |
davemoore@chromium.org | 70baebc | 2013-11-02 16:58:44 | [diff] [blame^] | 590 | #endif |
rsesek@chromium.org | 32f5e9a | 2013-05-23 12:59:54 | [diff] [blame] | 591 | |
davemoore@chromium.org | 70baebc | 2013-11-02 16:58:44 | [diff] [blame^] | 592 | return true; |
| 593 | } |
| 594 | |
| 595 | // exposed for testing |
| 596 | bool ParseProcVmstat(const std::string& vmstat_data, |
| 597 | SystemMemoryInfoKB* meminfo) { |
| 598 | std::vector<std::string> vmstat_fields; |
| 599 | SplitStringAlongWhitespace(vmstat_data, &vmstat_fields); |
| 600 | if (vmstat_fields[kVMPagesSwappedIn-1] == "pswpin") |
| 601 | StringToInt(vmstat_fields[kVMPagesSwappedIn], &meminfo->pswpin); |
| 602 | if (vmstat_fields[kVMPagesSwappedOut-1] == "pswpout") |
| 603 | StringToInt(vmstat_fields[kVMPagesSwappedOut], &meminfo->pswpout); |
| 604 | if (vmstat_fields[kVMPageMajorFaults-1] == "pgmajfault") |
| 605 | StringToInt(vmstat_fields[kVMPageMajorFaults], &meminfo->pgmajfault); |
| 606 | |
| 607 | return true; |
| 608 | } |
| 609 | |
| 610 | bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo) { |
| 611 | // Synchronously reading files in /proc is safe. |
| 612 | ThreadRestrictions::ScopedAllowIO allow_io; |
| 613 | |
| 614 | // Used memory is: total - free - buffers - caches |
| 615 | FilePath meminfo_file("/proc/meminfo"); |
| 616 | std::string meminfo_data; |
| 617 | if (!ReadFileToString(meminfo_file, &meminfo_data)) { |
| 618 | DLOG(WARNING) << "Failed to open " << meminfo_file.value(); |
| 619 | return false; |
| 620 | } |
| 621 | |
| 622 | if (!ParseProcMeminfo(meminfo_data, meminfo)) { |
| 623 | DLOG(WARNING) << "Failed to parse " << meminfo_file.value(); |
| 624 | return false; |
| 625 | } |
| 626 | |
| 627 | #if defined(OS_CHROMEOS) |
rsesek@chromium.org | 32f5e9a | 2013-05-23 12:59:54 | [diff] [blame] | 628 | // Report on Chrome OS GEM object graphics memory. /var/run/debugfs_gpu is a |
| 629 | // bind mount into /sys/kernel/debug and synchronously reading the in-memory |
| 630 | // files in /sys is fast. |
| 631 | #if defined(ARCH_CPU_ARM_FAMILY) |
| 632 | FilePath geminfo_file("/var/run/debugfs_gpu/exynos_gem_objects"); |
| 633 | #else |
| 634 | FilePath geminfo_file("/var/run/debugfs_gpu/i915_gem_objects"); |
| 635 | #endif |
| 636 | std::string geminfo_data; |
| 637 | meminfo->gem_objects = -1; |
| 638 | meminfo->gem_size = -1; |
brettw@chromium.org | 82f84b9 | 2013-08-30 18:23:50 | [diff] [blame] | 639 | if (ReadFileToString(geminfo_file, &geminfo_data)) { |
rsesek@chromium.org | 32f5e9a | 2013-05-23 12:59:54 | [diff] [blame] | 640 | int gem_objects = -1; |
| 641 | long long gem_size = -1; |
| 642 | int num_res = sscanf(geminfo_data.c_str(), |
| 643 | "%d objects, %lld bytes", |
| 644 | &gem_objects, &gem_size); |
| 645 | if (num_res == 2) { |
| 646 | meminfo->gem_objects = gem_objects; |
| 647 | meminfo->gem_size = gem_size; |
| 648 | } |
| 649 | } |
| 650 | |
| 651 | #if defined(ARCH_CPU_ARM_FAMILY) |
| 652 | // Incorporate Mali graphics memory if present. |
dbehr@chromium.org | dc5f8fd | 2013-09-04 04:07:27 | [diff] [blame] | 653 | FilePath mali_memory_file("/sys/class/misc/mali0/device/memory"); |
rsesek@chromium.org | 32f5e9a | 2013-05-23 12:59:54 | [diff] [blame] | 654 | std::string mali_memory_data; |
brettw@chromium.org | 82f84b9 | 2013-08-30 18:23:50 | [diff] [blame] | 655 | if (ReadFileToString(mali_memory_file, &mali_memory_data)) { |
rsesek@chromium.org | 32f5e9a | 2013-05-23 12:59:54 | [diff] [blame] | 656 | long long mali_size = -1; |
| 657 | int num_res = sscanf(mali_memory_data.c_str(), "%lld bytes", &mali_size); |
| 658 | if (num_res == 1) |
| 659 | meminfo->gem_size += mali_size; |
| 660 | } |
| 661 | #endif // defined(ARCH_CPU_ARM_FAMILY) |
| 662 | #endif // defined(OS_CHROMEOS) |
| 663 | |
jwmak@chromium.org | 49b0cf8 | 2013-09-03 23:46:54 | [diff] [blame] | 664 | FilePath vmstat_file("/proc/vmstat"); |
| 665 | std::string vmstat_data; |
| 666 | if (!ReadFileToString(vmstat_file, &vmstat_data)) { |
| 667 | DLOG(WARNING) << "Failed to open " << vmstat_file.value(); |
| 668 | return false; |
| 669 | } |
davemoore@chromium.org | 70baebc | 2013-11-02 16:58:44 | [diff] [blame^] | 670 | if (!ParseProcVmstat(vmstat_data, meminfo)) { |
| 671 | DLOG(WARNING) << "Failed to parse " << vmstat_file.value(); |
| 672 | return false; |
| 673 | } |
jwmak@chromium.org | 49b0cf8 | 2013-09-03 23:46:54 | [diff] [blame] | 674 | |
rsesek@chromium.org | 32f5e9a | 2013-05-23 12:59:54 | [diff] [blame] | 675 | return true; |
rsesek@chromium.org | 992a6065 | 2013-07-15 18:29:35 | [diff] [blame] | 676 | } |
| 677 | |
nduca@chromium.org | 669a0933 | 2013-08-30 22:59:14 | [diff] [blame] | 678 | SystemDiskInfo::SystemDiskInfo() { |
| 679 | reads = 0; |
| 680 | reads_merged = 0; |
| 681 | sectors_read = 0; |
| 682 | read_time = 0; |
| 683 | writes = 0; |
| 684 | writes_merged = 0; |
| 685 | sectors_written = 0; |
| 686 | write_time = 0; |
| 687 | io = 0; |
| 688 | io_time = 0; |
| 689 | weighted_io_time = 0; |
| 690 | } |
| 691 | |
jwmak@chromium.org | c2ef94e | 2013-09-06 13:13:23 | [diff] [blame] | 692 | scoped_ptr<Value> SystemDiskInfo::ToValue() const { |
| 693 | scoped_ptr<DictionaryValue> res(new base::DictionaryValue()); |
| 694 | |
| 695 | // Write out uint64 variables as doubles. |
| 696 | // Note: this may discard some precision, but for JS there's no other option. |
| 697 | res->SetDouble("reads", static_cast<double>(reads)); |
| 698 | res->SetDouble("reads_merged", static_cast<double>(reads_merged)); |
| 699 | res->SetDouble("sectors_read", static_cast<double>(sectors_read)); |
| 700 | res->SetDouble("read_time", static_cast<double>(read_time)); |
| 701 | res->SetDouble("writes", static_cast<double>(writes)); |
| 702 | res->SetDouble("writes_merged", static_cast<double>(writes_merged)); |
| 703 | res->SetDouble("sectors_written", static_cast<double>(sectors_written)); |
| 704 | res->SetDouble("write_time", static_cast<double>(write_time)); |
| 705 | res->SetDouble("io", static_cast<double>(io)); |
| 706 | res->SetDouble("io_time", static_cast<double>(io_time)); |
| 707 | res->SetDouble("weighted_io_time", static_cast<double>(weighted_io_time)); |
| 708 | |
| 709 | return res.PassAs<Value>(); |
| 710 | } |
| 711 | |
nduca@chromium.org | 669a0933 | 2013-08-30 22:59:14 | [diff] [blame] | 712 | bool IsValidDiskName(const std::string& candidate) { |
| 713 | if (candidate.length() < 3) |
| 714 | return false; |
| 715 | if (candidate.substr(0,2) == "sd" || candidate.substr(0,2) == "hd") { |
| 716 | // [sh]d[a-z]+ case |
| 717 | for (size_t i = 2; i < candidate.length(); i++) { |
| 718 | if (!islower(candidate[i])) |
| 719 | return false; |
| 720 | } |
| 721 | } else { |
| 722 | if (candidate.length() < 7) { |
| 723 | return false; |
| 724 | } |
| 725 | if (candidate.substr(0,6) == "mmcblk") { |
| 726 | // mmcblk[0-9]+ case |
| 727 | for (size_t i = 6; i < candidate.length(); i++) { |
| 728 | if (!isdigit(candidate[i])) |
| 729 | return false; |
| 730 | } |
| 731 | } else { |
| 732 | return false; |
| 733 | } |
| 734 | } |
| 735 | |
| 736 | return true; |
| 737 | } |
| 738 | |
| 739 | bool GetSystemDiskInfo(SystemDiskInfo* diskinfo) { |
| 740 | // Synchronously reading files in /proc is safe. |
| 741 | ThreadRestrictions::ScopedAllowIO allow_io; |
| 742 | |
| 743 | FilePath diskinfo_file("/proc/diskstats"); |
| 744 | std::string diskinfo_data; |
| 745 | if (!ReadFileToString(diskinfo_file, &diskinfo_data)) { |
| 746 | DLOG(WARNING) << "Failed to open " << diskinfo_file.value(); |
| 747 | return false; |
| 748 | } |
| 749 | |
| 750 | std::vector<std::string> diskinfo_lines; |
| 751 | size_t line_count = Tokenize(diskinfo_data, "\n", &diskinfo_lines); |
| 752 | if (line_count == 0) { |
| 753 | DLOG(WARNING) << "No lines found"; |
| 754 | return false; |
| 755 | } |
| 756 | |
| 757 | diskinfo->reads = 0; |
| 758 | diskinfo->reads_merged = 0; |
| 759 | diskinfo->sectors_read = 0; |
| 760 | diskinfo->read_time = 0; |
| 761 | diskinfo->writes = 0; |
| 762 | diskinfo->writes_merged = 0; |
| 763 | diskinfo->sectors_written = 0; |
| 764 | diskinfo->write_time = 0; |
| 765 | diskinfo->io = 0; |
| 766 | diskinfo->io_time = 0; |
| 767 | diskinfo->weighted_io_time = 0; |
| 768 | |
| 769 | uint64 reads = 0; |
| 770 | uint64 reads_merged = 0; |
| 771 | uint64 sectors_read = 0; |
| 772 | uint64 read_time = 0; |
| 773 | uint64 writes = 0; |
| 774 | uint64 writes_merged = 0; |
| 775 | uint64 sectors_written = 0; |
| 776 | uint64 write_time = 0; |
| 777 | uint64 io = 0; |
| 778 | uint64 io_time = 0; |
| 779 | uint64 weighted_io_time = 0; |
| 780 | |
| 781 | for (size_t i = 0; i < line_count; i++) { |
| 782 | std::vector<std::string> disk_fields; |
| 783 | SplitStringAlongWhitespace(diskinfo_lines[i], &disk_fields); |
| 784 | |
| 785 | // Fields may have overflowed and reset to zero. |
| 786 | if (IsValidDiskName(disk_fields[kDiskDriveName])) { |
| 787 | StringToUint64(disk_fields[kDiskReads], &reads); |
| 788 | StringToUint64(disk_fields[kDiskReadsMerged], &reads_merged); |
| 789 | StringToUint64(disk_fields[kDiskSectorsRead], §ors_read); |
| 790 | StringToUint64(disk_fields[kDiskReadTime], &read_time); |
| 791 | StringToUint64(disk_fields[kDiskWrites], &writes); |
| 792 | StringToUint64(disk_fields[kDiskWritesMerged], &writes_merged); |
| 793 | StringToUint64(disk_fields[kDiskSectorsWritten], §ors_written); |
| 794 | StringToUint64(disk_fields[kDiskWriteTime], &write_time); |
| 795 | StringToUint64(disk_fields[kDiskIO], &io); |
| 796 | StringToUint64(disk_fields[kDiskIOTime], &io_time); |
| 797 | StringToUint64(disk_fields[kDiskWeightedIOTime], &weighted_io_time); |
| 798 | |
| 799 | diskinfo->reads += reads; |
| 800 | diskinfo->reads_merged += reads_merged; |
| 801 | diskinfo->sectors_read += sectors_read; |
| 802 | diskinfo->read_time += read_time; |
| 803 | diskinfo->writes += writes; |
| 804 | diskinfo->writes_merged += writes_merged; |
| 805 | diskinfo->sectors_written += sectors_written; |
| 806 | diskinfo->write_time += write_time; |
| 807 | diskinfo->io += io; |
| 808 | diskinfo->io_time += io_time; |
| 809 | diskinfo->weighted_io_time += weighted_io_time; |
| 810 | } |
| 811 | } |
| 812 | |
| 813 | return true; |
| 814 | } |
| 815 | |
nduca@chromium.org | f413478 | 2013-08-29 21:25:20 | [diff] [blame] | 816 | #if defined(OS_CHROMEOS) |
jwmak@chromium.org | c2ef94e | 2013-09-06 13:13:23 | [diff] [blame] | 817 | scoped_ptr<Value> SwapInfo::ToValue() const { |
| 818 | scoped_ptr<DictionaryValue> res(new DictionaryValue()); |
| 819 | |
| 820 | // Write out uint64 variables as doubles. |
| 821 | // Note: this may discard some precision, but for JS there's no other option. |
| 822 | res->SetDouble("num_reads", static_cast<double>(num_reads)); |
| 823 | res->SetDouble("num_writes", static_cast<double>(num_writes)); |
| 824 | res->SetDouble("orig_data_size", static_cast<double>(orig_data_size)); |
| 825 | res->SetDouble("compr_data_size", static_cast<double>(compr_data_size)); |
| 826 | res->SetDouble("mem_used_total", static_cast<double>(mem_used_total)); |
| 827 | if (compr_data_size > 0) |
| 828 | res->SetDouble("compression_ratio", static_cast<double>(orig_data_size) / |
| 829 | static_cast<double>(compr_data_size)); |
| 830 | else |
| 831 | res->SetDouble("compression_ratio", 0); |
| 832 | |
| 833 | return res.PassAs<Value>(); |
| 834 | } |
| 835 | |
nduca@chromium.org | f413478 | 2013-08-29 21:25:20 | [diff] [blame] | 836 | void GetSwapInfo(SwapInfo* swap_info) { |
| 837 | // Synchronously reading files in /sys/block/zram0 is safe. |
| 838 | ThreadRestrictions::ScopedAllowIO allow_io; |
| 839 | |
| 840 | base::FilePath zram_path("/sys/block/zram0"); |
| 841 | uint64 orig_data_size = ReadFileToUint64(zram_path.Append("orig_data_size")); |
| 842 | if (orig_data_size <= 4096) { |
| 843 | // A single page is compressed at startup, and has a high compression |
| 844 | // ratio. We ignore this as it doesn't indicate any real swapping. |
| 845 | swap_info->orig_data_size = 0; |
| 846 | swap_info->num_reads = 0; |
| 847 | swap_info->num_writes = 0; |
| 848 | swap_info->compr_data_size = 0; |
| 849 | swap_info->mem_used_total = 0; |
| 850 | return; |
| 851 | } |
| 852 | swap_info->orig_data_size = orig_data_size; |
| 853 | swap_info->num_reads = ReadFileToUint64(zram_path.Append("num_reads")); |
| 854 | swap_info->num_writes = ReadFileToUint64(zram_path.Append("num_writes")); |
| 855 | swap_info->compr_data_size = |
| 856 | ReadFileToUint64(zram_path.Append("compr_data_size")); |
| 857 | swap_info->mem_used_total = |
| 858 | ReadFileToUint64(zram_path.Append("mem_used_total")); |
| 859 | } |
| 860 | #endif // defined(OS_CHROMEOS) |
| 861 | |
rsesek@chromium.org | 32f5e9a | 2013-05-23 12:59:54 | [diff] [blame] | 862 | } // namespace base |