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