| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "process_memory_stats.h" |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| #include <memory> |
| |
| #include "file_utils.h" |
| #include "libmemtrack_wrapper.h" |
| #include "logging.h" |
| |
| namespace { |
| |
| const int kKbPerPage = 4; |
| |
| const char kRss[] = "Rss"; |
| const char kPss[] = "Pss"; |
| const char kSwap[] = "Swap"; |
| const char kSharedClean[] = "Shared_Clean"; |
| const char kSharedDirty[] = "Shared_Dirty"; |
| const char kPrivateClean[] = "Private_Clean"; |
| const char kPrivateDirty[] = "Private_Dirty"; |
| |
| bool ReadSmapsMetric( |
| const char* line, const char* metric, int metric_size, uint64_t* res) { |
| if (strncmp(line, metric, metric_size - 1)) |
| return false; |
| if (line[metric_size - 1] != ':') |
| return false; |
| *res = strtoull(line + metric_size, nullptr, 10); |
| return true; |
| } |
| |
| } // namespace |
| |
| bool ProcessMemoryStats::ReadLightStats(int pid) { |
| char buf[64]; |
| if (file_utils::ReadProcFile(pid, "statm", buf, sizeof(buf)) <= 0) |
| return false; |
| uint32_t vm_size_pages; |
| uint32_t rss_pages; |
| int res = sscanf(buf, "%u %u", &vm_size_pages, &rss_pages); |
| CHECK(res == 2); |
| rss_kb_ = rss_pages * kKbPerPage; |
| virt_kb_ = vm_size_pages * kKbPerPage; |
| return true; |
| } |
| |
| bool ProcessMemoryStats::ReadFullStats(int pid) { |
| const size_t kBufSize = 8u * 1024 * 1024; |
| std::unique_ptr<char[]> buf(new char[kBufSize]); |
| ssize_t rsize = file_utils::ReadProcFile(pid, "smaps", &buf[0], kBufSize); |
| if (rsize <= 0) |
| return false; |
| MmapInfo* last_mmap_entry = nullptr; |
| std::unique_ptr<MmapInfo> new_mmap(new MmapInfo()); |
| CHECK(mmaps_.empty()); |
| CHECK(rss_kb_ == 0); |
| |
| // Iterate over all lines in /proc/PID/smaps. |
| file_utils::LineReader rd(&buf[0], rsize); |
| for (const char* line = rd.NextLine(); line; line = rd.NextLine()) { |
| if (!line[0]) |
| continue; |
| // Performance optimization (hack). |
| // Any header line starts with lowercase hex digit but subsequent lines |
| // start with uppercase letter. |
| if (line[0] < 'A' || line[0] > 'Z') { |
| // Note that the mapped file name ([stack]) is optional and won't be |
| // present on anonymous memory maps (hence res >= 3 below). |
| int res = sscanf(line, |
| "%llx-%llx %4s %*llx %*[:0-9a-f] %*[0-9a-f]%*[ \t]%127[^\n]", |
| &new_mmap->start_addr, &new_mmap->end_addr, new_mmap->prot_flags, |
| new_mmap->mapped_file); |
| last_mmap_entry = new_mmap.get(); |
| CHECK(new_mmap->end_addr >= new_mmap->start_addr); |
| new_mmap->virt_kb = |
| (new_mmap->end_addr - new_mmap->start_addr) / 1024; |
| if (res == 3) |
| new_mmap->mapped_file[0] = '\0'; |
| virt_kb_ += new_mmap->virt_kb; |
| mmaps_.push_back(std::move(new_mmap)); |
| new_mmap.reset(new MmapInfo()); |
| } else { |
| // The current line is a metrics line within a mmap entry, e.g.: |
| // Size: 4 kB |
| uint64_t size = 0; |
| CHECK(last_mmap_entry); |
| if (ReadSmapsMetric(line, kRss, sizeof(kRss), &size)) { |
| last_mmap_entry->rss_kb = size; |
| rss_kb_ += size; |
| } else if (ReadSmapsMetric(line, kPss, sizeof(kPss), &size)) { |
| last_mmap_entry->pss_kb = size; |
| pss_kb_ += size; |
| } else if (ReadSmapsMetric(line, kSwap, sizeof(kSwap), &size)) { |
| last_mmap_entry->swapped_kb = size; |
| swapped_kb_ += size; |
| } else if (ReadSmapsMetric( |
| line, kSharedClean, sizeof(kSharedClean), &size)) { |
| last_mmap_entry->shared_clean_kb = size; |
| shared_clean_kb_ += size; |
| } else if (ReadSmapsMetric( |
| line, kSharedDirty, sizeof(kSharedDirty), &size)) { |
| last_mmap_entry->shared_dirty_kb = size; |
| shared_dirty_kb_ += size; |
| } else if (ReadSmapsMetric( |
| line, kPrivateClean, sizeof(kPrivateClean), &size)) { |
| last_mmap_entry->private_clean_kb = size; |
| private_clean_kb_ += size; |
| } else if (ReadSmapsMetric( |
| line, kPrivateDirty, sizeof(kPrivateDirty), &size)) { |
| last_mmap_entry->private_dirty_kb = size; |
| private_dirty_kb_ += size; |
| } |
| } |
| } |
| full_stats_ = true; |
| return true; |
| } |
| |
| bool ProcessMemoryStats::ReadGpuStats(int pid) { |
| MemtrackProc mt(pid); |
| gpu_graphics_kb_ = mt.graphics_total() / 1024; |
| gpu_graphics_pss_kb_ = mt.graphics_pss() / 1024; |
| gpu_gl_kb_ = mt.gl_total() / 1024; |
| gpu_gl_pss_kb_ = mt.gl_pss() / 1024; |
| gpu_other_kb_ = mt.other_total() / 1024; |
| gpu_other_pss_kb_ = mt.other_pss() / 1024; |
| |
| gpu_stats_ = !mt.has_errors() && |
| (gpu_graphics_kb_ != 0 || gpu_gl_kb_ != 0 || gpu_other_kb_ != 0); |
| return gpu_stats_; |
| } |