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