Home | History | Annotate | Download | only in jni
      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 "logging.h"
     14 
     15 namespace {
     16 const int kKbPerPage = 4;
     17 
     18 // Takes a C string buffer and chunks it into lines without creating any
     19 // copies. It modifies the original buffer, by replacing \n with \0.
     20 class LineReader {
     21  public:
     22   LineReader(char* buf, size_t size) : ptr_(buf), end_(buf + size) {}
     23 
     24   const char* NextLine() {
     25     if (ptr_ >= end_)
     26       return nullptr;
     27     const char* cur = ptr_;
     28     char* next = strchr(ptr_, '\n');
     29     if (next) {
     30       *next = '\0';
     31       ptr_ = next + 1;
     32     } else {
     33       ptr_ = end_;
     34     }
     35     return cur;
     36   }
     37 
     38  private:
     39   char* ptr_;
     40   char* end_;
     41 };
     42 
     43 bool ReadSmapsMetric(const char* line, const char* metric, uint64_t* res) {
     44   if (strncmp(line, metric, strlen(metric)))
     45     return false;
     46   line = strchr(line, ':');
     47   if (!line)
     48     return false;
     49   *res = strtoull(line + 1, nullptr, 10);
     50   return true;
     51 }
     52 
     53 }  // namespace
     54 
     55 ProcessMemoryStats::ProcessMemoryStats(int pid) : pid_(pid) {}
     56 
     57 bool ProcessMemoryStats::ReadLightStats() {
     58   char buf[64];
     59   if (file_utils::ReadProcFile(pid_, "statm", buf, sizeof(buf)) <= 0)
     60     return false;
     61   uint32_t vm_size_pages;
     62   uint32_t rss_pages;
     63   int res = sscanf(buf, "%u %u", &vm_size_pages, &rss_pages);
     64   CHECK(res == 2);
     65   rss_kb_ = rss_pages * kKbPerPage;
     66   virt_kb_ = vm_size_pages * kKbPerPage;
     67   return true;
     68 }
     69 
     70 bool ProcessMemoryStats::ReadFullStats() {
     71   const size_t kBufSize = 32u * 1024 * 1024;
     72   std::unique_ptr<char[]> buf(new char[kBufSize]);
     73   ssize_t rsize = file_utils::ReadProcFile(pid_, "smaps", &buf[0], kBufSize);
     74   if (rsize <= 0)
     75     return false;
     76   MmapInfo* last_mmap_entry = nullptr;
     77   std::unique_ptr<MmapInfo> new_mmap(new MmapInfo());
     78   CHECK(mmaps_.empty());
     79   CHECK(rss_kb_ == 0);
     80 
     81   // Iterate over all lines in /proc/PID/smaps.
     82   LineReader rd(&buf[0], rsize);
     83   for (const char* line = rd.NextLine(); line; line = rd.NextLine()) {
     84     // Check if the current line is the beginning of a new mmaps entry, e.g.:
     85     // be7f7000-be818000 rw-p 00000000 00:00 0          [stack]
     86     // Note that the mapped file name ([stack]) is optional and won't be
     87     // present
     88     // on anonymous memory maps (hence res >= 3 below).
     89     int res = sscanf(
     90         line, "%llx-%llx %4s %*llx %*[:0-9a-f] %*[0-9a-f]%*[ \t]%128[^\n]",
     91         &new_mmap->start_addr, &new_mmap->end_addr, new_mmap->prot_flags,
     92         new_mmap->mapped_file);
     93     if (res >= 3) {
     94       last_mmap_entry = new_mmap.get();
     95       CHECK(new_mmap->end_addr >= new_mmap->start_addr);
     96       new_mmap->virt_kb = (new_mmap->end_addr - new_mmap->start_addr) / 1024;
     97       if (res == 3)
     98         new_mmap->mapped_file[0] = '\0';
     99       virt_kb_ += new_mmap->virt_kb;
    100       mmaps_[new_mmap->start_addr] = std::move(new_mmap);
    101       new_mmap.reset(new MmapInfo());
    102     } else {
    103       // The current line is a metrics line within a mmap entry, e.g.:
    104       // Size:   4 kB
    105       uint64_t size = 0;
    106       CHECK(last_mmap_entry);
    107       if (ReadSmapsMetric(line, "Rss:", &size)) {
    108         last_mmap_entry->rss_kb = size;
    109         rss_kb_ += size;
    110       } else if (ReadSmapsMetric(line, "Pss:", &size)) {
    111         last_mmap_entry->pss_kb = size;
    112         pss_kb_ += size;
    113       } else if (ReadSmapsMetric(line, "Swap:", &size)) {
    114         last_mmap_entry->swapped_kb = size;
    115         swapped_kb_ += size;
    116       } else if (ReadSmapsMetric(line, "Shared_Clean:", &size)) {
    117         last_mmap_entry->shared_clean_kb = size;
    118         shared_clean_kb_ += size;
    119       } else if (ReadSmapsMetric(line, "Shared_Dirty:", &size)) {
    120         last_mmap_entry->shared_dirty_kb = size;
    121         shared_dirty_kb_ += size;
    122       } else if (ReadSmapsMetric(line, "Private_Clean:", &size)) {
    123         last_mmap_entry->private_clean_kb = size;
    124         private_clean_kb_ += size;
    125       } else if (ReadSmapsMetric(line, "Private_Dirty:", &size)) {
    126         last_mmap_entry->private_dirty_kb = size;
    127         private_dirty_kb_ += size;
    128       }
    129     }
    130   }
    131   return true;
    132 }
    133