Home | History | Annotate | Download | only in memtrack
      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 #define LOG_TAG "MemTracker"
     17 
     18 #include "memtrack.h"
     19 
     20 #include <ctype.h>
     21 #include <dirent.h>
     22 #include <fcntl.h>
     23 #include <limits.h>
     24 #include <signal.h>
     25 #include <stdio.h>
     26 #include <stdlib.h>
     27 #include <sys/stat.h>
     28 #include <sys/types.h>
     29 #include <unistd.h>
     30 
     31 #include <algorithm>
     32 #include <vector>
     33 
     34 #include <log/log.h>
     35 
     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);
     40   if (fd_ < 0) {
     41     read_complete_ = true;
     42   }
     43 }
     44 
     45 FileData::~FileData() {
     46   if (fd_ >= 0) {
     47     close(fd_);
     48   }
     49 }
     50 
     51 bool FileData::isAvail(size_t bytes_needed) {
     52   if (cur_idx_ + bytes_needed < len_) {
     53     return true;
     54   }
     55 
     56   if (read_complete_) {
     57     return false;
     58   }
     59 
     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_);
     64   }
     65 
     66   ssize_t bytes;
     67   cur_idx_ = 0;
     68   while (cur_idx_ + bytes_needed >= len_) {
     69     bytes = read(fd_, data_ + len_, max_ - len_);
     70     if (bytes == 0 || bytes == -1) {
     71       read_complete_; // unused read?
     72       break;
     73     }
     74     len_ += bytes;
     75   }
     76 
     77   return cur_idx_ + bytes_needed < len_;
     78 }
     79 
     80 bool FileData::getPss(size_t *pss) {
     81   size_t value;
     82   while (true) {
     83     if (!isAvail(4)) {
     84       return false;
     85     }
     86 
     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');
     91     } else {
     92       cur_idx_ += 4;
     93       while (isAvail(1) && isspace(data_[cur_idx_])) {
     94         cur_idx_++;
     95       }
     96 
     97       value = 0;
     98       while (isAvail(1) && isdigit(data_[cur_idx_])) {
     99         value = value * 10 + data_[cur_idx_] - '0';
    100         cur_idx_++;
    101       }
    102       *pss = value;
    103 
    104       // Consume the rest of the line.
    105       while (isAvail(1) && data_[cur_idx_++] != '\n');
    106 
    107       return true;
    108     }
    109   }
    110 }
    111 
    112 const char *ProcessInfo::kProc = "/proc/";
    113 const char *ProcessInfo::kCmdline = "/cmdline";
    114 const char *ProcessInfo::kSmaps = "/smaps";
    115 
    116 ProcessInfo::ProcessInfo() {
    117   memcpy(proc_file_, kProc, kProcLen);
    118 }
    119 
    120 ProcessInfo::~ProcessInfo() {
    121 }
    122 
    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);
    126 
    127   // Read the cmdline for the process.
    128   int fd = open(proc_file_, O_RDONLY);
    129   if (fd < 0) {
    130     return false;
    131   }
    132 
    133   ssize_t bytes = read(fd, cmd_name_, sizeof(cmd_name_));
    134   close(fd);
    135   if (bytes == -1 || bytes == 0) {
    136     return false;
    137   }
    138 
    139   memcpy(proc_file_ + kProcLen + pid_str_len, kSmaps, kSmapsLen);
    140   FileData smaps(proc_file_, buffer_, sizeof(buffer_));
    141 
    142   cur_process_info_t process_info;
    143   size_t pss_kb;
    144   process_info.pss_kb = 0;
    145   while (smaps.getPss(&pss_kb)) {
    146     process_info.pss_kb += pss_kb;
    147   }
    148 
    149   if (cur_.count(cmd_name_) == 0) {
    150     cur_[cmd_name_] = process_info;
    151   } else {
    152     cur_[cmd_name_].pss_kb += process_info.pss_kb;
    153   }
    154   cur_[cmd_name_].pids.push_back(pid);
    155 
    156   return true;
    157 }
    158 
    159 void ProcessInfo::scan() {
    160   DIR *proc_dir = opendir(kProc);
    161   if (proc_dir == NULL) {
    162     perror("Cannot open directory.\n");
    163     exit(1);
    164   }
    165 
    166   // Clear any current pids.
    167   for (processes_t::iterator it = all_.begin(); it != all_.end(); ++it) {
    168     it->second.pids.clear();
    169   }
    170 
    171   struct dirent *dir_data;
    172   int len;
    173   bool is_pid;
    174   size_t pid;
    175   cur_.clear();
    176   while ((dir_data = readdir(proc_dir))) {
    177     // Check if the directory entry represents a pid.
    178     len = strlen(dir_data->d_name);
    179     is_pid = true;
    180     pid = 0;
    181     for (int i = 0; i < len; i++) {
    182       if (!isdigit(dir_data->d_name[i])) {
    183         is_pid = false;
    184         break;
    185       }
    186       pid = pid * 10 + dir_data->d_name[i] - '0';
    187     }
    188     if (is_pid) {
    189       getInformation(pid, dir_data->d_name, len);
    190     }
    191   }
    192   closedir(proc_dir);
    193 
    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) {
    197 
    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;
    205     }
    206 
    207     if (it->second.pids.size() > all_[it->first].max_num_pids) {
    208       all_[it->first].max_num_pids = it->second.pids.size();
    209     }
    210 
    211     all_[it->first].pids = it->second.pids;
    212 
    213     if (it->second.pss_kb > all_[it->first].max_pss_kb) {
    214       all_[it->first].max_pss_kb = it->second.pss_kb;
    215     }
    216 
    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;
    220     }
    221 
    222     all_[it->first].last_pss_kb = it->second.pss_kb;
    223 
    224     computeAvg(&all_[it->first].avg_pss_kb, it->second.pss_kb,
    225                all_[it->first].num_samples);
    226     all_[it->first].num_samples++;
    227   }
    228 }
    229 
    230 bool comparePss(const process_info_t *first, const process_info_t *second) {
    231   return first->max_pss_kb > second->max_pss_kb;
    232 }
    233 
    234 void ProcessInfo::dumpToLog() {
    235   list_.clear();
    236   for (processes_t::const_iterator it = all_.begin(); it != all_.end(); ++it) {
    237     list_.push_back(&it->second);
    238   }
    239 
    240   // Now sort the list.
    241   std::sort(list_.begin(), list_.end(), comparePss);
    242 
    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);
    253       }
    254     }
    255 
    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);
    260   }
    261 }
    262 
    263 void usage() {
    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",
    272          LOG_TAG);
    273 }
    274 
    275 int SignalReceived = 0;
    276 
    277 int SignalsToHandle[] = {
    278   SIGTSTP,
    279   SIGINT,
    280   SIGHUP,
    281   SIGPIPE,
    282   SIGUSR1,
    283 };
    284 
    285 void handleSignal(int signo) {
    286   if (SignalReceived == 0) {
    287     SignalReceived = signo;
    288   }
    289 }
    290 
    291 int main(int argc, char **argv) {
    292   if (geteuid() != 0) {
    293     printf("Must be run as root.\n");
    294     exit(1);
    295   }
    296 
    297   bool verbose = false;
    298   bool quiet = 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) {
    302       verbose = true;
    303     } else if (strcmp(argv[i], "--quiet") == 0) {
    304       quiet = true;
    305     } else if (strcmp(argv[i], "--scan_delay") == 0) {
    306       if (i+1 == argc) {
    307         printf("The %s options requires a single argument.\n", argv[i]);
    308         usage();
    309         exit(1);
    310       }
    311       scan_delay_sec = atoi(argv[++i]);
    312     } else {
    313       printf("Unknown option %s\n", argv[i]);
    314       usage();
    315       exit(1);
    316     }
    317   }
    318   if (quiet && verbose) {
    319     printf("Both --quiet and --verbose cannot be specified.\n");
    320     usage();
    321     exit(1);
    322   }
    323 
    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]);
    328       exit(1);
    329     }
    330   }
    331 
    332   ProcessInfo proc_info;
    333 
    334   if (!quiet) {
    335     printf("Hit Ctrl-Z or send SIGUSR1 to pid %d to print the current list of\n",
    336            getpid());
    337     printf("processes.\n");
    338     printf("Hit Ctrl-C to print the list of processes and terminate.\n");
    339   }
    340 
    341   struct timespec t;
    342   unsigned long long nsecs;
    343   while (true) {
    344     if (verbose) {
    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;
    348     }
    349     proc_info.scan();
    350     if (verbose) {
    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);
    355     }
    356 
    357     if (SignalReceived != 0) {
    358       proc_info.dumpToLog();
    359       if (SignalReceived != SIGUSR1 && SignalReceived != SIGTSTP) {
    360         if (!quiet) {
    361           printf("Terminating...\n");
    362         }
    363         exit(1);
    364       }
    365       SignalReceived = 0;
    366     }
    367     sleep(scan_delay_sec);
    368   }
    369 }
    370