| 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 if (!mt.has_errors()) { |
| 160 gpu_stats_ = |
| 161 gpu_graphics_kb_ != 0 || gpu_gl_kb_ != 0 || gpu_other_kb_ != 0; |
| 162 return gpu_stats_; |
| 163 } |
| 164 return false; |
| 145 } | 165 } |
| OLD | NEW |