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:
LineReader(char * buf,size_t size)22 LineReader(char* buf, size_t size) : ptr_(buf), end_(buf + size) {}
23
NextLine()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
ReadSmapsMetric(const char * line,const char * metric,uint64_t * res)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
ProcessMemoryStats(int pid)55 ProcessMemoryStats::ProcessMemoryStats(int pid) : pid_(pid) {}
56
ReadLightStats()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
ReadFullStats()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