1 // Copyright 2017 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 "process_memory_stats.h" 6 7 #include <stdio.h> 8 #include <stdlib.h> 9 10 #include <memory> 11 12 #include "file_utils.h" 13 #include "logging.h" 14 15 namespace { 16 const int kKbPerPage = 4; 17 18 // Takes a C string buffer and chunks it into lines without creating any 19 // copies. It modifies the original buffer, by replacing \n with \0. 20 class LineReader { 21 public: 22 LineReader(char* buf, size_t size) : ptr_(buf), end_(buf + size) {} 23 24 const char* NextLine() { 25 if (ptr_ >= end_) 26 return nullptr; 27 const char* cur = ptr_; 28 char* next = strchr(ptr_, '\n'); 29 if (next) { 30 *next = '\0'; 31 ptr_ = next + 1; 32 } else { 33 ptr_ = end_; 34 } 35 return cur; 36 } 37 38 private: 39 char* ptr_; 40 char* end_; 41 }; 42 43 bool ReadSmapsMetric(const char* line, const char* metric, uint64_t* res) { 44 if (strncmp(line, metric, strlen(metric))) 45 return false; 46 line = strchr(line, ':'); 47 if (!line) 48 return false; 49 *res = strtoull(line + 1, nullptr, 10); 50 return true; 51 } 52 53 } // namespace 54 55 ProcessMemoryStats::ProcessMemoryStats(int pid) : pid_(pid) {} 56 57 bool ProcessMemoryStats::ReadLightStats() { 58 char buf[64]; 59 if (file_utils::ReadProcFile(pid_, "statm", buf, sizeof(buf)) <= 0) 60 return false; 61 uint32_t vm_size_pages; 62 uint32_t rss_pages; 63 int res = sscanf(buf, "%u %u", &vm_size_pages, &rss_pages); 64 CHECK(res == 2); 65 rss_kb_ = rss_pages * kKbPerPage; 66 virt_kb_ = vm_size_pages * kKbPerPage; 67 return true; 68 } 69 70 bool ProcessMemoryStats::ReadFullStats() { 71 const size_t kBufSize = 32u * 1024 * 1024; 72 std::unique_ptr<char[]> buf(new char[kBufSize]); 73 ssize_t rsize = file_utils::ReadProcFile(pid_, "smaps", &buf[0], kBufSize); 74 if (rsize <= 0) 75 return false; 76 MmapInfo* last_mmap_entry = nullptr; 77 std::unique_ptr<MmapInfo> new_mmap(new MmapInfo()); 78 CHECK(mmaps_.empty()); 79 CHECK(rss_kb_ == 0); 80 81 // Iterate over all lines in /proc/PID/smaps. 82 LineReader rd(&buf[0], rsize); 83 for (const char* line = rd.NextLine(); line; line = rd.NextLine()) { 84 // Check if the current line is the beginning of a new mmaps entry, e.g.: 85 // be7f7000-be818000 rw-p 00000000 00:00 0 [stack] 86 // Note that the mapped file name ([stack]) is optional and won't be 87 // present 88 // on anonymous memory maps (hence res >= 3 below). 89 int res = sscanf( 90 line, "%llx-%llx %4s %*llx %*[:0-9a-f] %*[0-9a-f]%*[ \t]%128[^\n]", 91 &new_mmap->start_addr, &new_mmap->end_addr, new_mmap->prot_flags, 92 new_mmap->mapped_file); 93 if (res >= 3) { 94 last_mmap_entry = new_mmap.get(); 95 CHECK(new_mmap->end_addr >= new_mmap->start_addr); 96 new_mmap->virt_kb = (new_mmap->end_addr - new_mmap->start_addr) / 1024; 97 if (res == 3) 98 new_mmap->mapped_file[0] = '\0'; 99 virt_kb_ += new_mmap->virt_kb; 100 mmaps_[new_mmap->start_addr] = std::move(new_mmap); 101 new_mmap.reset(new MmapInfo()); 102 } else { 103 // The current line is a metrics line within a mmap entry, e.g.: 104 // Size: 4 kB 105 uint64_t size = 0; 106 CHECK(last_mmap_entry); 107 if (ReadSmapsMetric(line, "Rss:", &size)) { 108 last_mmap_entry->rss_kb = size; 109 rss_kb_ += size; 110 } else if (ReadSmapsMetric(line, "Pss:", &size)) { 111 last_mmap_entry->pss_kb = size; 112 pss_kb_ += size; 113 } else if (ReadSmapsMetric(line, "Swap:", &size)) { 114 last_mmap_entry->swapped_kb = size; 115 swapped_kb_ += size; 116 } else if (ReadSmapsMetric(line, "Shared_Clean:", &size)) { 117 last_mmap_entry->shared_clean_kb = size; 118 shared_clean_kb_ += size; 119 } else if (ReadSmapsMetric(line, "Shared_Dirty:", &size)) { 120 last_mmap_entry->shared_dirty_kb = size; 121 shared_dirty_kb_ += size; 122 } else if (ReadSmapsMetric(line, "Private_Clean:", &size)) { 123 last_mmap_entry->private_clean_kb = size; 124 private_clean_kb_ += size; 125 } else if (ReadSmapsMetric(line, "Private_Dirty:", &size)) { 126 last_mmap_entry->private_dirty_kb = size; 127 private_dirty_kb_ += size; 128 } 129 } 130 } 131 return true; 132 } 133