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