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 
     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 
     40 FileData::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 
     49 FileData::~FileData() {
     50   if (fd_ >= 0) {
     51     close(fd_);
     52   }
     53 }
     54 
     55 bool 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 
     84 bool 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 
    116 const char *ProcessInfo::kProc = "/proc/";
    117 const char *ProcessInfo::kCmdline = "/cmdline";
    118 const char *ProcessInfo::kSmaps = "/smaps";
    119 
    120 ProcessInfo::ProcessInfo() {
    121   memcpy(proc_file_, kProc, kProcLen);
    122 }
    123 
    124 ProcessInfo::~ProcessInfo() {
    125 }
    126 
    127 bool 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 
    163 void 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 
    234 bool comparePss(const process_info_t *first, const process_info_t *second) {
    235   return first->max_pss_kb > second->max_pss_kb;
    236 }
    237 
    238 void 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 
    267 void 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 
    279 int SignalReceived = 0;
    280 
    281 int SignalsToHandle[] = {
    282   SIGTSTP,
    283   SIGINT,
    284   SIGHUP,
    285   SIGPIPE,
    286   SIGUSR1,
    287 };
    288 
    289 void handleSignal(int signo) {
    290   if (SignalReceived == 0) {
    291     SignalReceived = signo;
    292   }
    293 }
    294 
    295 int 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