blob: a451d5c066b3c35c964df7ab1293b9e89a63d15e [file] [log] [blame]
Christopher Ferris91f44102013-05-20 17:24:15 -07001/*
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
Dan Albertdc847e62014-11-15 18:50:10 -080017#include "memtrack.h"
Christopher Ferris91f44102013-05-20 17:24:15 -070018
Dan Albertdc847e62014-11-15 18:50:10 -080019#include <ctype.h>
Christopher Ferris91f44102013-05-20 17:24:15 -070020#include <dirent.h>
Dan Albertdc847e62014-11-15 18:50:10 -080021#include <fcntl.h>
22#include <limits.h>
23#include <signal.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <sys/stat.h>
27#include <sys/types.h>
28#include <unistd.h>
Christopher Ferris91f44102013-05-20 17:24:15 -070029
30#include <cutils/log.h>
31
32#include <algorithm>
33#include <vector>
34
Christopher Ferris91f44102013-05-20 17:24:15 -070035#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}