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 <inttypes.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 
11 #include <memory>
12 
13 #include "file_utils.h"
14 #include "libmemtrack_wrapper.h"
15 #include "logging.h"
16 
17 namespace {
18 
19 const int kKbPerPage = 4;
20 
21 const char kRss[] = "Rss";
22 const char kPss[] = "Pss";
23 const char kSwap[] = "Swap";
24 const char kSharedClean[] = "Shared_Clean";
25 const char kSharedDirty[] = "Shared_Dirty";
26 const char kPrivateClean[] = "Private_Clean";
27 const char kPrivateDirty[] = "Private_Dirty";
28 
ReadSmapsMetric(const char * line,const char * metric,int metric_size,uint64_t * res)29 bool ReadSmapsMetric(
30     const char* line, const char* metric, int metric_size, uint64_t* res) {
31   if (strncmp(line, metric, metric_size - 1))
32     return false;
33   if (line[metric_size - 1] != ':')
34     return false;
35   *res = strtoull(line + metric_size, nullptr, 10);
36   return true;
37 }
38 
39 }  // namespace
40 
ReadLightStats(int pid)41 bool ProcessMemoryStats::ReadLightStats(int pid) {
42   char buf[64];
43   if (file_utils::ReadProcFile(pid, "statm", buf, sizeof(buf)) <= 0)
44     return false;
45   uint32_t vm_size_pages;
46   uint32_t rss_pages;
47   int res = sscanf(buf, "%u %u", &vm_size_pages, &rss_pages);
48   CHECK(res == 2);
49   rss_kb_ = rss_pages * kKbPerPage;
50   virt_kb_ = vm_size_pages * kKbPerPage;
51   return true;
52 }
53 
ReadFullStats(int pid)54 bool ProcessMemoryStats::ReadFullStats(int pid) {
55   const size_t kBufSize = 8u * 1024 * 1024;
56   std::unique_ptr<char[]> buf(new char[kBufSize]);
57   ssize_t rsize = file_utils::ReadProcFile(pid, "smaps", &buf[0], kBufSize);
58   if (rsize <= 0)
59     return false;
60   MmapInfo* last_mmap_entry = nullptr;
61   std::unique_ptr<MmapInfo> new_mmap(new MmapInfo());
62   CHECK(mmaps_.empty());
63   CHECK(rss_kb_ == 0);
64 
65   // Iterate over all lines in /proc/PID/smaps.
66   file_utils::LineReader rd(&buf[0], rsize);
67   for (const char* line = rd.NextLine(); line; line = rd.NextLine()) {
68     if (!line[0])
69       continue;
70     // Performance optimization (hack).
71     // Any header line starts with lowercase hex digit but subsequent lines
72     // start with uppercase letter.
73     if (line[0] < 'A' || line[0] > 'Z') {
74       // Note that the mapped file name ([stack]) is optional and won't be
75       // present on anonymous memory maps (hence res >= 3 below).
76       int res = sscanf(line,
77           "%" PRIx64 "-%" PRIx64 " %4s %*" PRIx64 " %*[:0-9a-f] "
78           "%*[0-9a-f]%*[ \t]%127[^\n]",
79           &new_mmap->start_addr, &new_mmap->end_addr, new_mmap->prot_flags,
80           new_mmap->mapped_file);
81       last_mmap_entry = new_mmap.get();
82       CHECK(new_mmap->end_addr >= new_mmap->start_addr);
83       new_mmap->virt_kb =
84           (new_mmap->end_addr - new_mmap->start_addr) / 1024;
85       if (res == 3)
86         new_mmap->mapped_file[0] = '\0';
87       virt_kb_ += new_mmap->virt_kb;
88       mmaps_.push_back(std::move(new_mmap));
89       new_mmap.reset(new MmapInfo());
90     } else {
91       // The current line is a metrics line within a mmap entry, e.g.:
92       // Size:   4 kB
93       uint64_t size = 0;
94       CHECK(last_mmap_entry);
95       if (ReadSmapsMetric(line, kRss, sizeof(kRss), &size)) {
96         last_mmap_entry->rss_kb = size;
97         rss_kb_ += size;
98       } else if (ReadSmapsMetric(line, kPss, sizeof(kPss), &size)) {
99         last_mmap_entry->pss_kb = size;
100         pss_kb_ += size;
101       } else if (ReadSmapsMetric(line, kSwap, sizeof(kSwap), &size)) {
102         last_mmap_entry->swapped_kb = size;
103         swapped_kb_ += size;
104       } else if (ReadSmapsMetric(
105                      line, kSharedClean, sizeof(kSharedClean), &size)) {
106         last_mmap_entry->shared_clean_kb = size;
107         shared_clean_kb_ += size;
108       } else if (ReadSmapsMetric(
109                      line, kSharedDirty, sizeof(kSharedDirty), &size)) {
110         last_mmap_entry->shared_dirty_kb = size;
111         shared_dirty_kb_ += size;
112       } else if (ReadSmapsMetric(
113                      line, kPrivateClean, sizeof(kPrivateClean), &size)) {
114         last_mmap_entry->private_clean_kb = size;
115         private_clean_kb_ += size;
116       } else if (ReadSmapsMetric(
117                      line, kPrivateDirty, sizeof(kPrivateDirty), &size)) {
118         last_mmap_entry->private_dirty_kb = size;
119         private_dirty_kb_ += size;
120       }
121     }
122   }
123   full_stats_ = true;
124   return true;
125 }
126 
ReadGpuStats(int pid)127 bool ProcessMemoryStats::ReadGpuStats(int pid) {
128   MemtrackProc mt(pid);
129   gpu_graphics_kb_ = mt.graphics_total() / 1024;
130   gpu_graphics_pss_kb_ = mt.graphics_pss() / 1024;
131   gpu_gl_kb_ = mt.gl_total() / 1024;
132   gpu_gl_pss_kb_ = mt.gl_pss() / 1024;
133   gpu_other_kb_ = mt.other_total() / 1024;
134   gpu_other_pss_kb_ = mt.other_pss() / 1024;
135 
136   gpu_stats_ = !mt.has_errors() &&
137       (gpu_graphics_kb_ != 0 || gpu_gl_kb_ != 0 || gpu_other_kb_ != 0);
138   return gpu_stats_;
139 }
140