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