1/*
2 * Copyright 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <stdio.h>
18#include <limits.h>
19#include <ctype.h>
20#include <unistd.h>
21
22#include <signal.h>
23#include <sys/types.h>
24#include <sys/stat.h>
25#include <fcntl.h>
26#include <dirent.h>
27
28#include <cutils/log.h>
29
30#include <algorithm>
31#include <vector>
32
33#include "memtrack.h"
34
35#ifdef LOG_TAG
36#undef LOG_TAG
37#endif
38#define LOG_TAG "MemTracker"
39
40FileData::FileData(char *filename, char *buffer, size_t buffer_len)
41    : data_(buffer), max_(buffer_len), cur_idx_(0), len_(0),
42      read_complete_(false) {
43  fd_ = open(filename, O_RDONLY);
44  if (fd_ < 0) {
45    read_complete_ = true;
46  }
47}
48
49FileData::~FileData() {
50  if (fd_ >= 0) {
51    close(fd_);
52  }
53}
54
55bool FileData::isAvail(size_t bytes_needed) {
56  if (cur_idx_ + bytes_needed < len_) {
57    return true;
58  }
59
60  if (read_complete_) {
61    return false;
62  }
63
64  if (cur_idx_ != len_) {
65    // Copy the leftover to the front of the buffer.
66    len_ = len_ - cur_idx_;
67    memcpy(data_, data_ + cur_idx_, len_);
68  }
69
70  ssize_t bytes;
71  cur_idx_ = 0;
72  while (cur_idx_ + bytes_needed >= len_) {
73    bytes = read(fd_, data_ + len_, max_ - len_);
74    if (bytes == 0 || bytes == -1) {
75      read_complete_;
76      break;
77    }
78    len_ += bytes;
79  }
80
81  return cur_idx_ + bytes_needed < len_;
82}
83
84bool FileData::getPss(size_t *pss) {
85  size_t value;
86  while (true) {
87    if (!isAvail(4)) {
88      return false;
89    }
90
91    if (data_[cur_idx_] != 'P' || data_[cur_idx_+1] != 's' ||
92        data_[cur_idx_+2] != 's' || data_[cur_idx_+3] != ':') {
93      // Consume the rest of the line.
94      while (isAvail(1) && data_[cur_idx_++] != '\n');
95    } else {
96      cur_idx_ += 4;
97      while (isAvail(1) && isspace(data_[cur_idx_])) {
98        cur_idx_++;
99      }
100
101      value = 0;
102      while (isAvail(1) && isdigit(data_[cur_idx_])) {
103        value = value * 10 + data_[cur_idx_] - '0';
104        cur_idx_++;
105      }
106      *pss = value;
107
108      // Consume the rest of the line.
109      while (isAvail(1) && data_[cur_idx_++] != '\n');
110
111      return true;
112    }
113  }
114}
115
116const char *ProcessInfo::kProc = "/proc/";
117const char *ProcessInfo::kCmdline = "/cmdline";
118const char *ProcessInfo::kSmaps = "/smaps";
119
120ProcessInfo::ProcessInfo() {
121  memcpy(proc_file_, kProc, kProcLen);
122}
123
124ProcessInfo::~ProcessInfo() {
125}
126
127bool ProcessInfo::getInformation(int pid, char *pid_str, size_t pid_str_len) {
128  memcpy(proc_file_ + kProcLen, pid_str, pid_str_len);
129  memcpy(proc_file_ + kProcLen + pid_str_len, kCmdline, kCmdlineLen);
130
131  // Read the cmdline for the process.
132  int fd = open(proc_file_, O_RDONLY);
133  if (fd < 0) {
134    return false;
135  }
136
137  ssize_t bytes = read(fd, cmd_name_, sizeof(cmd_name_));
138  close(fd);
139  if (bytes == -1 || bytes == 0) {
140    return false;
141  }
142
143  memcpy(proc_file_ + kProcLen + pid_str_len, kSmaps, kSmapsLen);
144  FileData smaps(proc_file_, buffer_, sizeof(buffer_));
145
146  cur_process_info_t process_info;
147  size_t pss_kb;
148  process_info.pss_kb = 0;
149  while (smaps.getPss(&pss_kb)) {
150    process_info.pss_kb += pss_kb;
151  }
152
153  if (cur_.count(cmd_name_) == 0) {
154    cur_[cmd_name_] = process_info;
155  } else {
156    cur_[cmd_name_].pss_kb += process_info.pss_kb;
157  }
158  cur_[cmd_name_].pids.push_back(pid);
159
160  return true;
161}
162
163void ProcessInfo::scan() {
164  DIR *proc_dir = opendir(kProc);
165  if (proc_dir == NULL) {
166    perror("Cannot open directory.\n");
167    exit(1);
168  }
169
170  // Clear any current pids.
171  for (processes_t::iterator it = all_.begin(); it != all_.end(); ++it) {
172    it->second.pids.clear();
173  }
174
175  struct dirent *dir_data;
176  int len;
177  bool is_pid;
178  size_t pid;
179  cur_.clear();
180  while ((dir_data = readdir(proc_dir))) {
181    // Check if the directory entry represents a pid.
182    len = strlen(dir_data->d_name);
183    is_pid = true;
184    pid = 0;
185    for (int i = 0; i < len; i++) {
186      if (!isdigit(dir_data->d_name[i])) {
187        is_pid = false;
188        break;
189      }
190      pid = pid * 10 + dir_data->d_name[i] - '0';
191    }
192    if (is_pid) {
193      getInformation(pid, dir_data->d_name, len);
194    }
195  }
196  closedir(proc_dir);
197
198  // Loop through the current processes and add them into our real list.
199  for (cur_processes_t::const_iterator it = cur_.begin();
200       it != cur_.end(); ++it) {
201
202    if (all_.count(it->first) == 0) {
203      // Initialize all of the variables.
204      all_[it->first].num_samples = 0;
205      all_[it->first].name = it->first;
206      all_[it->first].avg_pss_kb = 0;
207      all_[it->first].min_pss_kb = 0;
208      all_[it->first].max_pss_kb = 0;
209    }
210
211    if (it->second.pids.size() > all_[it->first].max_num_pids) {
212      all_[it->first].max_num_pids = it->second.pids.size();
213    }
214
215    all_[it->first].pids = it->second.pids;
216
217    if (it->second.pss_kb > all_[it->first].max_pss_kb) {
218      all_[it->first].max_pss_kb = it->second.pss_kb;
219    }
220
221    if (all_[it->first].min_pss_kb == 0 ||
222        it->second.pss_kb < all_[it->first].min_pss_kb) {
223      all_[it->first].min_pss_kb = it->second.pss_kb;
224    }
225
226    all_[it->first].last_pss_kb = it->second.pss_kb;
227
228    computeAvg(&all_[it->first].avg_pss_kb, it->second.pss_kb,
229               all_[it->first].num_samples);
230    all_[it->first].num_samples++;
231  }
232}
233
234bool comparePss(const process_info_t *first, const process_info_t *second) {
235  return first->max_pss_kb > second->max_pss_kb;
236}
237
238void ProcessInfo::dumpToLog() {
239  list_.clear();
240  for (processes_t::const_iterator it = all_.begin(); it != all_.end(); ++it) {
241    list_.push_back(&it->second);
242  }
243
244  // Now sort the list.
245  std::sort(list_.begin(), list_.end(), comparePss);
246
247  ALOGI("Dumping process list");
248  for (std::vector<const process_info_t *>::const_iterator it = list_.begin();
249       it != list_.end(); ++it) {
250    ALOGI("  Name: %s", (*it)->name.c_str());
251    ALOGI("    Max running processes: %d", (*it)->max_num_pids);
252    if ((*it)->pids.size() > 0) {
253      ALOGI("    Currently running pids:");
254      for (std::vector<int>::const_iterator pid_it = (*it)->pids.begin();
255           pid_it != (*it)->pids.end(); ++pid_it) {
256        ALOGI("      %d", *pid_it);
257      }
258    }
259
260    ALOGI("    Min  PSS %0.4fM", (*it)->min_pss_kb/1024.0);
261    ALOGI("    Avg  PSS %0.4fM", (*it)->avg_pss_kb/1024.0);
262    ALOGI("    Max  PSS %0.4fM", (*it)->max_pss_kb/1024.0);
263    ALOGI("    Last PSS %0.4fM", (*it)->last_pss_kb/1024.0);
264  }
265}
266
267void usage() {
268  printf("Usage: memtrack [--verbose | --quiet] [--scan_delay TIME_SECS]\n");
269  printf("  --scan_delay TIME_SECS\n");
270  printf("    The amount of delay in seconds between scans.\n");
271  printf("  --verbose\n");
272  printf("    Print information about the scans to stdout only.\n");
273  printf("  --quiet\n");
274  printf("    Nothing will be printed to stdout.\n");
275  printf("  All scan data is dumped to the android log using the tag %s\n",
276         LOG_TAG);
277}
278
279int SignalReceived = 0;
280
281int SignalsToHandle[] = {
282  SIGTSTP,
283  SIGINT,
284  SIGHUP,
285  SIGPIPE,
286  SIGUSR1,
287};
288
289void handleSignal(int signo) {
290  if (SignalReceived == 0) {
291    SignalReceived = signo;
292  }
293}
294
295int main(int argc, char **argv) {
296  if (geteuid() != 0) {
297    printf("Must be run as root.\n");
298    exit(1);
299  }
300
301  bool verbose = false;
302  bool quiet = false;
303  unsigned int scan_delay_sec = DEFAULT_SLEEP_DELAY_SECONDS;
304  for (int i = 1; i < argc; i++) {
305    if (strcmp(argv[i], "--verbose") == 0) {
306      verbose = true;
307    } else if (strcmp(argv[i], "--quiet") == 0) {
308      quiet = true;
309    } else if (strcmp(argv[i], "--scan_delay") == 0) {
310      if (i+1 == argc) {
311        printf("The %s options requires a single argument.\n", argv[i]);
312        usage();
313        exit(1);
314      }
315      scan_delay_sec = atoi(argv[++i]);
316    } else {
317      printf("Unknown option %s\n", argv[i]);
318      usage();
319      exit(1);
320    }
321  }
322  if (quiet && verbose) {
323    printf("Both --quiet and --verbose cannot be specified.\n");
324    usage();
325    exit(1);
326  }
327
328  // Set up the signal handlers.
329  for (size_t i = 0; i < sizeof(SignalsToHandle)/sizeof(int); i++) {
330    if (signal(SignalsToHandle[i], handleSignal) == SIG_ERR) {
331      printf("Unable to handle signal %d\n", SignalsToHandle[i]);
332      exit(1);
333    }
334  }
335
336  ProcessInfo proc_info;
337
338  if (!quiet) {
339    printf("Hit Ctrl-Z or send SIGUSR1 to pid %d to print the current list of\n",
340           getpid());
341    printf("processes.\n");
342    printf("Hit Ctrl-C to print the list of processes and terminate.\n");
343  }
344
345  struct timespec t;
346  unsigned long long nsecs;
347  while (true) {
348    if (verbose) {
349      memset(&t, 0, sizeof(t));
350      clock_gettime(CLOCK_MONOTONIC, &t);
351      nsecs = (unsigned long long)t.tv_sec*NS_PER_SEC + t.tv_nsec;
352    }
353    proc_info.scan();
354    if (verbose) {
355      memset(&t, 0, sizeof(t));
356      clock_gettime(CLOCK_MONOTONIC, &t);
357      nsecs = ((unsigned long long)t.tv_sec*NS_PER_SEC + t.tv_nsec) - nsecs;
358      printf("Scan Time %0.4f\n", ((double)nsecs)/NS_PER_SEC);
359    }
360
361    if (SignalReceived != 0) {
362      proc_info.dumpToLog();
363      if (SignalReceived != SIGUSR1 && SignalReceived != SIGTSTP) {
364        if (!quiet) {
365          printf("Terminating...\n");
366        }
367        exit(1);
368      }
369      SignalReceived = 0;
370    }
371    sleep(scan_delay_sec);
372  }
373}
374