2 * Copyright 2013 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 #define LOG_TAG "MemTracker"
28 #include <sys/types.h>
34 #include <android/log.h>
36 FileData::FileData(char *filename, char *buffer, size_t buffer_len)
37 : data_(buffer), max_(buffer_len), cur_idx_(0), len_(0),
38 read_complete_(false) {
39 fd_ = open(filename, O_RDONLY);
41 read_complete_ = true;
45 FileData::~FileData() {
51 bool FileData::isAvail(size_t bytes_needed) {
52 if (cur_idx_ + bytes_needed < len_) {
60 if (cur_idx_ != len_) {
61 // Copy the leftover to the front of the buffer.
62 len_ = len_ - cur_idx_;
63 memcpy(data_, data_ + cur_idx_, len_);
68 while (cur_idx_ + bytes_needed >= len_) {
69 bytes = read(fd_, data_ + len_, max_ - len_);
70 if (bytes == 0 || bytes == -1) {
77 return cur_idx_ + bytes_needed < len_;
80 bool FileData::getPss(size_t *pss) {
87 if (data_[cur_idx_] != 'P' || data_[cur_idx_+1] != 's' ||
88 data_[cur_idx_+2] != 's' || data_[cur_idx_+3] != ':') {
89 // Consume the rest of the line.
90 while (isAvail(1) && data_[cur_idx_++] != '\n');
93 while (isAvail(1) && isspace(data_[cur_idx_])) {
98 while (isAvail(1) && isdigit(data_[cur_idx_])) {
99 value = value * 10 + data_[cur_idx_] - '0';
104 // Consume the rest of the line.
105 while (isAvail(1) && data_[cur_idx_++] != '\n');
112 const char *ProcessInfo::kProc = "/proc/";
113 const char *ProcessInfo::kCmdline = "/cmdline";
114 const char *ProcessInfo::kSmaps = "/smaps";
116 ProcessInfo::ProcessInfo() {
117 memcpy(proc_file_, kProc, kProcLen);
120 ProcessInfo::~ProcessInfo() {
123 bool ProcessInfo::getInformation(int pid, char *pid_str, size_t pid_str_len) {
124 memcpy(proc_file_ + kProcLen, pid_str, pid_str_len);
125 memcpy(proc_file_ + kProcLen + pid_str_len, kCmdline, kCmdlineLen);
127 // Read the cmdline for the process.
128 int fd = open(proc_file_, O_RDONLY);
133 ssize_t bytes = read(fd, cmd_name_, sizeof(cmd_name_));
135 if (bytes == -1 || bytes == 0) {
139 memcpy(proc_file_ + kProcLen + pid_str_len, kSmaps, kSmapsLen);
140 FileData smaps(proc_file_, buffer_, sizeof(buffer_));
142 cur_process_info_t process_info;
144 process_info.pss_kb = 0;
145 while (smaps.getPss(&pss_kb)) {
146 process_info.pss_kb += pss_kb;
149 if (cur_.count(cmd_name_) == 0) {
150 cur_[cmd_name_] = process_info;
152 cur_[cmd_name_].pss_kb += process_info.pss_kb;
154 cur_[cmd_name_].pids.push_back(pid);
159 void ProcessInfo::scan() {
160 DIR *proc_dir = opendir(kProc);
161 if (proc_dir == NULL) {
162 perror("Cannot open directory.\n");
166 // Clear any current pids.
167 for (processes_t::iterator it = all_.begin(); it != all_.end(); ++it) {
168 it->second.pids.clear();
171 struct dirent *dir_data;
176 while ((dir_data = readdir(proc_dir))) {
177 // Check if the directory entry represents a pid.
178 len = strlen(dir_data->d_name);
181 for (int i = 0; i < len; i++) {
182 if (!isdigit(dir_data->d_name[i])) {
186 pid = pid * 10 + dir_data->d_name[i] - '0';
189 getInformation(pid, dir_data->d_name, len);
194 // Loop through the current processes and add them into our real list.
195 for (cur_processes_t::const_iterator it = cur_.begin();
196 it != cur_.end(); ++it) {
198 if (all_.count(it->first) == 0) {
199 // Initialize all of the variables.
200 all_[it->first].num_samples = 0;
201 all_[it->first].name = it->first;
202 all_[it->first].avg_pss_kb = 0;
203 all_[it->first].min_pss_kb = 0;
204 all_[it->first].max_pss_kb = 0;
207 if (it->second.pids.size() > all_[it->first].max_num_pids) {
208 all_[it->first].max_num_pids = it->second.pids.size();
211 all_[it->first].pids = it->second.pids;
213 if (it->second.pss_kb > all_[it->first].max_pss_kb) {
214 all_[it->first].max_pss_kb = it->second.pss_kb;
217 if (all_[it->first].min_pss_kb == 0 ||
218 it->second.pss_kb < all_[it->first].min_pss_kb) {
219 all_[it->first].min_pss_kb = it->second.pss_kb;
222 all_[it->first].last_pss_kb = it->second.pss_kb;
224 computeAvg(&all_[it->first].avg_pss_kb, it->second.pss_kb,
225 all_[it->first].num_samples);
226 all_[it->first].num_samples++;
230 bool comparePss(const process_info_t *first, const process_info_t *second) {
231 return first->max_pss_kb > second->max_pss_kb;
234 void ProcessInfo::dumpToLog() {
236 for (processes_t::const_iterator it = all_.begin(); it != all_.end(); ++it) {
237 list_.push_back(&it->second);
240 // Now sort the list.
241 std::sort(list_.begin(), list_.end(), comparePss);
243 ALOGI("Dumping process list");
244 for (std::vector<const process_info_t *>::const_iterator it = list_.begin();
245 it != list_.end(); ++it) {
246 ALOGI(" Name: %s", (*it)->name.c_str());
247 ALOGI(" Max running processes: %zu", (*it)->max_num_pids);
248 if ((*it)->pids.size() > 0) {
249 ALOGI(" Currently running pids:");
250 for (std::vector<int>::const_iterator pid_it = (*it)->pids.begin();
251 pid_it != (*it)->pids.end(); ++pid_it) {
252 ALOGI(" %d", *pid_it);
256 ALOGI(" Min PSS %0.4fM", (*it)->min_pss_kb/1024.0);
257 ALOGI(" Avg PSS %0.4fM", (*it)->avg_pss_kb/1024.0);
258 ALOGI(" Max PSS %0.4fM", (*it)->max_pss_kb/1024.0);
259 ALOGI(" Last PSS %0.4fM", (*it)->last_pss_kb/1024.0);
264 printf("Usage: memtrack [--verbose | --quiet] [--scan_delay TIME_SECS]\n");
265 printf(" --scan_delay TIME_SECS\n");
266 printf(" The amount of delay in seconds between scans.\n");
267 printf(" --verbose\n");
268 printf(" Print information about the scans to stdout only.\n");
269 printf(" --quiet\n");
270 printf(" Nothing will be printed to stdout.\n");
271 printf(" All scan data is dumped to the android log using the tag %s\n",
275 int SignalReceived = 0;
277 int SignalsToHandle[] = {
285 void handleSignal(int signo) {
286 if (SignalReceived == 0) {
287 SignalReceived = signo;
291 int main(int argc, char **argv) {
292 if (geteuid() != 0) {
293 printf("Must be run as root.\n");
297 bool verbose = false;
299 unsigned int scan_delay_sec = DEFAULT_SLEEP_DELAY_SECONDS;
300 for (int i = 1; i < argc; i++) {
301 if (strcmp(argv[i], "--verbose") == 0) {
303 } else if (strcmp(argv[i], "--quiet") == 0) {
305 } else if (strcmp(argv[i], "--scan_delay") == 0) {
307 printf("The %s options requires a single argument.\n", argv[i]);
311 scan_delay_sec = atoi(argv[++i]);
313 printf("Unknown option %s\n", argv[i]);
318 if (quiet && verbose) {
319 printf("Both --quiet and --verbose cannot be specified.\n");
324 // Set up the signal handlers.
325 for (size_t i = 0; i < sizeof(SignalsToHandle)/sizeof(int); i++) {
326 if (signal(SignalsToHandle[i], handleSignal) == SIG_ERR) {
327 printf("Unable to handle signal %d\n", SignalsToHandle[i]);
332 ProcessInfo proc_info;
335 printf("Hit Ctrl-Z or send SIGUSR1 to pid %d to print the current list of\n",
337 printf("processes.\n");
338 printf("Hit Ctrl-C to print the list of processes and terminate.\n");
342 unsigned long long nsecs;
345 memset(&t, 0, sizeof(t));
346 clock_gettime(CLOCK_MONOTONIC, &t);
347 nsecs = (unsigned long long)t.tv_sec*NS_PER_SEC + t.tv_nsec;
351 memset(&t, 0, sizeof(t));
352 clock_gettime(CLOCK_MONOTONIC, &t);
353 nsecs = ((unsigned long long)t.tv_sec*NS_PER_SEC + t.tv_nsec) - nsecs;
354 printf("Scan Time %0.4f\n", ((double)nsecs)/NS_PER_SEC);
357 if (SignalReceived != 0) {
358 proc_info.dumpToLog();
359 if (SignalReceived != SIGUSR1 && SignalReceived != SIGTSTP) {
361 printf("Terminating...\n");
367 sleep(scan_delay_sec);