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