• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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