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