1/* 2 * Copyright (c) 2008, The Android Open Source Project 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in 12 * the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google, Inc. nor the names of its contributors 15 * may be used to endorse or promote products derived from this 16 * software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 21 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 22 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 25 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 28 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32#include <ctype.h> 33#include <dirent.h> 34#include <errno.h> 35#include <grp.h> 36#include <inttypes.h> 37#include <pwd.h> 38#include <stdio.h> 39#include <stdlib.h> 40#include <string.h> 41#include <sys/types.h> 42#include <time.h> 43#include <unistd.h> 44 45#include <cutils/sched_policy.h> 46 47struct cpu_info { 48 long unsigned utime, ntime, stime, itime; 49 long unsigned iowtime, irqtime, sirqtime; 50}; 51 52#define PROC_NAME_LEN 64 53#define THREAD_NAME_LEN 32 54#define POLICY_NAME_LEN 4 55 56struct proc_info { 57 struct proc_info *next; 58 pid_t pid; 59 pid_t tid; 60 uid_t uid; 61 gid_t gid; 62 char name[PROC_NAME_LEN]; 63 char tname[THREAD_NAME_LEN]; 64 char state; 65 uint64_t utime; 66 uint64_t stime; 67 char pr[3]; 68 long ni; 69 uint64_t delta_utime; 70 uint64_t delta_stime; 71 uint64_t delta_time; 72 uint64_t vss; 73 uint64_t rss; 74 int num_threads; 75 char policy[POLICY_NAME_LEN]; 76}; 77 78struct proc_list { 79 struct proc_info **array; 80 int size; 81}; 82 83#define die(...) { fprintf(stderr, __VA_ARGS__); exit(EXIT_FAILURE); } 84 85#define INIT_PROCS 50 86#define THREAD_MULT 8 87static struct proc_info **old_procs, **new_procs; 88static int num_old_procs, num_new_procs; 89static struct proc_info *free_procs; 90static int num_used_procs, num_free_procs; 91 92static int max_procs, delay, iterations, threads; 93 94static struct cpu_info old_cpu, new_cpu; 95 96static struct proc_info *alloc_proc(void); 97static void free_proc(struct proc_info *proc); 98static void read_procs(void); 99static int read_stat(char *filename, struct proc_info *proc); 100static void read_policy(int pid, struct proc_info *proc); 101static void add_proc(int proc_num, struct proc_info *proc); 102static int read_cmdline(char *filename, struct proc_info *proc); 103static int read_status(char *filename, struct proc_info *proc); 104static void print_procs(void); 105static struct proc_info *find_old_proc(pid_t pid, pid_t tid); 106static void free_old_procs(void); 107static int (*proc_cmp)(const void *a, const void *b); 108static int proc_cpu_cmp(const void *a, const void *b); 109static int proc_vss_cmp(const void *a, const void *b); 110static int proc_rss_cmp(const void *a, const void *b); 111static int proc_thr_cmp(const void *a, const void *b); 112static int numcmp(long long a, long long b); 113static void usage(char *cmd); 114 115int top_main(int argc, char *argv[]) { 116 num_used_procs = num_free_procs = 0; 117 118 max_procs = 0; 119 delay = 3; 120 iterations = -1; 121 proc_cmp = &proc_cpu_cmp; 122 for (int i = 1; i < argc; i++) { 123 if (!strcmp(argv[i], "-m")) { 124 if (i + 1 >= argc) { 125 fprintf(stderr, "Option -m expects an argument.\n"); 126 usage(argv[0]); 127 exit(EXIT_FAILURE); 128 } 129 max_procs = atoi(argv[++i]); 130 continue; 131 } 132 if (!strcmp(argv[i], "-n")) { 133 if (i + 1 >= argc) { 134 fprintf(stderr, "Option -n expects an argument.\n"); 135 usage(argv[0]); 136 exit(EXIT_FAILURE); 137 } 138 iterations = atoi(argv[++i]); 139 continue; 140 } 141 if (!strcmp(argv[i], "-d")) { 142 if (i + 1 >= argc) { 143 fprintf(stderr, "Option -d expects an argument.\n"); 144 usage(argv[0]); 145 exit(EXIT_FAILURE); 146 } 147 delay = atoi(argv[++i]); 148 continue; 149 } 150 if (!strcmp(argv[i], "-s")) { 151 if (i + 1 >= argc) { 152 fprintf(stderr, "Option -s expects an argument.\n"); 153 usage(argv[0]); 154 exit(EXIT_FAILURE); 155 } 156 ++i; 157 if (!strcmp(argv[i], "cpu")) { proc_cmp = &proc_cpu_cmp; continue; } 158 if (!strcmp(argv[i], "vss")) { proc_cmp = &proc_vss_cmp; continue; } 159 if (!strcmp(argv[i], "rss")) { proc_cmp = &proc_rss_cmp; continue; } 160 if (!strcmp(argv[i], "thr")) { proc_cmp = &proc_thr_cmp; continue; } 161 fprintf(stderr, "Invalid argument \"%s\" for option -s.\n", argv[i]); 162 exit(EXIT_FAILURE); 163 } 164 if (!strcmp(argv[i], "-H") || !strcmp(argv[i], "-t")) { threads = 1; continue; } 165 if (!strcmp(argv[i], "-h")) { 166 usage(argv[0]); 167 exit(EXIT_SUCCESS); 168 } 169 fprintf(stderr, "Invalid argument \"%s\".\n", argv[i]); 170 usage(argv[0]); 171 exit(EXIT_FAILURE); 172 } 173 174 if (threads && proc_cmp == &proc_thr_cmp) { 175 fprintf(stderr, "Sorting by threads per thread makes no sense!\n"); 176 exit(EXIT_FAILURE); 177 } 178 179 free_procs = NULL; 180 181 num_new_procs = num_old_procs = 0; 182 new_procs = old_procs = NULL; 183 184 read_procs(); 185 186 // Pause 250ms to get better data and avoid divide by zero later (http://b/32478213). 187 struct timespec ts = { .tv_sec = 0, .tv_nsec = 250000000 }; 188 TEMP_FAILURE_RETRY(nanosleep(&ts, &ts)); 189 190 while ((iterations == -1) || (iterations-- > 0)) { 191 old_procs = new_procs; 192 num_old_procs = num_new_procs; 193 memcpy(&old_cpu, &new_cpu, sizeof(old_cpu)); 194 read_procs(); 195 print_procs(); 196 free_old_procs(); 197 fflush(stdout); 198 if (iterations != 0) sleep(delay); 199 } 200 201 return 0; 202} 203 204static struct proc_info *alloc_proc(void) { 205 struct proc_info *proc; 206 207 if (free_procs) { 208 proc = free_procs; 209 free_procs = free_procs->next; 210 num_free_procs--; 211 } else { 212 proc = malloc(sizeof(*proc)); 213 if (!proc) die("Could not allocate struct process_info.\n"); 214 } 215 216 num_used_procs++; 217 218 return proc; 219} 220 221static void free_proc(struct proc_info *proc) { 222 proc->next = free_procs; 223 free_procs = proc; 224 225 num_used_procs--; 226 num_free_procs++; 227} 228 229#define MAX_LINE 256 230 231static void read_procs(void) { 232 DIR *proc_dir, *task_dir; 233 struct dirent *pid_dir, *tid_dir; 234 char filename[64]; 235 FILE *file; 236 int proc_num; 237 struct proc_info *proc; 238 pid_t pid, tid; 239 240 int i; 241 242 proc_dir = opendir("/proc"); 243 if (!proc_dir) die("Could not open /proc.\n"); 244 245 new_procs = calloc(INIT_PROCS * (threads ? THREAD_MULT : 1), sizeof(struct proc_info *)); 246 num_new_procs = INIT_PROCS * (threads ? THREAD_MULT : 1); 247 248 file = fopen("/proc/stat", "r"); 249 if (!file) die("Could not open /proc/stat.\n"); 250 fscanf(file, "cpu %lu %lu %lu %lu %lu %lu %lu", &new_cpu.utime, &new_cpu.ntime, &new_cpu.stime, 251 &new_cpu.itime, &new_cpu.iowtime, &new_cpu.irqtime, &new_cpu.sirqtime); 252 fclose(file); 253 254 proc_num = 0; 255 while ((pid_dir = readdir(proc_dir))) { 256 if (!isdigit(pid_dir->d_name[0])) 257 continue; 258 259 pid = atoi(pid_dir->d_name); 260 261 struct proc_info cur_proc; 262 263 if (!threads) { 264 proc = alloc_proc(); 265 266 proc->pid = proc->tid = pid; 267 268 sprintf(filename, "/proc/%d/stat", pid); 269 read_stat(filename, proc); 270 271 sprintf(filename, "/proc/%d/cmdline", pid); 272 read_cmdline(filename, proc); 273 274 sprintf(filename, "/proc/%d/status", pid); 275 read_status(filename, proc); 276 277 read_policy(pid, proc); 278 279 proc->num_threads = 0; 280 } else { 281 sprintf(filename, "/proc/%d/cmdline", pid); 282 read_cmdline(filename, &cur_proc); 283 284 sprintf(filename, "/proc/%d/status", pid); 285 read_status(filename, &cur_proc); 286 287 proc = NULL; 288 } 289 290 sprintf(filename, "/proc/%d/task", pid); 291 task_dir = opendir(filename); 292 if (!task_dir) continue; 293 294 while ((tid_dir = readdir(task_dir))) { 295 if (!isdigit(tid_dir->d_name[0])) 296 continue; 297 298 if (threads) { 299 tid = atoi(tid_dir->d_name); 300 301 proc = alloc_proc(); 302 303 proc->pid = pid; proc->tid = tid; 304 305 sprintf(filename, "/proc/%d/task/%d/stat", pid, tid); 306 read_stat(filename, proc); 307 308 read_policy(tid, proc); 309 310 strcpy(proc->name, cur_proc.name); 311 proc->uid = cur_proc.uid; 312 proc->gid = cur_proc.gid; 313 314 add_proc(proc_num++, proc); 315 } else { 316 proc->num_threads++; 317 } 318 } 319 320 closedir(task_dir); 321 322 if (!threads) 323 add_proc(proc_num++, proc); 324 } 325 326 for (i = proc_num; i < num_new_procs; i++) 327 new_procs[i] = NULL; 328 329 closedir(proc_dir); 330} 331 332static int read_stat(char *filename, struct proc_info *proc) { 333 FILE *file; 334 char buf[MAX_LINE], *open_paren, *close_paren; 335 336 file = fopen(filename, "r"); 337 if (!file) return 1; 338 fgets(buf, MAX_LINE, file); 339 fclose(file); 340 341 /* Split at first '(' and last ')' to get process name. */ 342 open_paren = strchr(buf, '('); 343 close_paren = strrchr(buf, ')'); 344 if (!open_paren || !close_paren) return 1; 345 346 *open_paren = *close_paren = '\0'; 347 strncpy(proc->tname, open_paren + 1, THREAD_NAME_LEN); 348 proc->tname[THREAD_NAME_LEN-1] = 0; 349 350 // Scan rest of string. 351 long pr; 352 sscanf(close_paren + 1, 353 " %c " 354 "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d " 355 "%" SCNu64 // utime %lu (14) 356 "%" SCNu64 // stime %lu (15) 357 "%*d %*d " 358 "%ld " // priority %ld (18) 359 "%ld " // nice %ld (19) 360 "%*d %*d %*d " 361 "%" SCNu64 // vsize %lu (23) 362 "%" SCNu64, // rss %ld (24) 363 &proc->state, 364 &proc->utime, 365 &proc->stime, 366 &pr, 367 &proc->ni, 368 &proc->vss, 369 &proc->rss); 370 371 // Translate the PR field. 372 if (pr < -9) strcpy(proc->pr, "RT"); 373 else snprintf(proc->pr, sizeof(proc->pr), "%ld", pr); 374 375 return 0; 376} 377 378static void add_proc(int proc_num, struct proc_info *proc) { 379 int i; 380 381 if (proc_num >= num_new_procs) { 382 new_procs = realloc(new_procs, 2 * num_new_procs * sizeof(struct proc_info *)); 383 if (!new_procs) die("Could not expand procs array.\n"); 384 for (i = num_new_procs; i < 2 * num_new_procs; i++) 385 new_procs[i] = NULL; 386 num_new_procs = 2 * num_new_procs; 387 } 388 new_procs[proc_num] = proc; 389} 390 391static int read_cmdline(char *filename, struct proc_info *proc) { 392 FILE *file; 393 char line[MAX_LINE]; 394 395 line[0] = '\0'; 396 file = fopen(filename, "r"); 397 if (!file) return 1; 398 fgets(line, MAX_LINE, file); 399 fclose(file); 400 if (strlen(line) > 0) { 401 strncpy(proc->name, line, PROC_NAME_LEN); 402 proc->name[PROC_NAME_LEN-1] = 0; 403 } else 404 proc->name[0] = 0; 405 return 0; 406} 407 408static void read_policy(int pid, struct proc_info *proc) { 409 SchedPolicy p; 410 if (get_sched_policy(pid, &p) < 0) 411 strlcpy(proc->policy, "unk", POLICY_NAME_LEN); 412 else { 413 strlcpy(proc->policy, get_sched_policy_name(p), POLICY_NAME_LEN); 414 proc->policy[2] = '\0'; 415 } 416} 417 418static int read_status(char *filename, struct proc_info *proc) { 419 FILE *file; 420 char line[MAX_LINE]; 421 unsigned int uid, gid; 422 423 file = fopen(filename, "r"); 424 if (!file) return 1; 425 while (fgets(line, MAX_LINE, file)) { 426 sscanf(line, "Uid: %u", &uid); 427 sscanf(line, "Gid: %u", &gid); 428 } 429 fclose(file); 430 proc->uid = uid; proc->gid = gid; 431 return 0; 432} 433 434static void print_procs(void) { 435 static int call = 0; 436 int i; 437 struct proc_info *old_proc, *proc; 438 long unsigned total_delta_time; 439 440 for (i = 0; i < num_new_procs; i++) { 441 if (new_procs[i]) { 442 old_proc = find_old_proc(new_procs[i]->pid, new_procs[i]->tid); 443 if (old_proc) { 444 new_procs[i]->delta_utime = new_procs[i]->utime - old_proc->utime; 445 new_procs[i]->delta_stime = new_procs[i]->stime - old_proc->stime; 446 } else { 447 new_procs[i]->delta_utime = 0; 448 new_procs[i]->delta_stime = 0; 449 } 450 new_procs[i]->delta_time = new_procs[i]->delta_utime + new_procs[i]->delta_stime; 451 } 452 } 453 454 total_delta_time = (new_cpu.utime + new_cpu.ntime + new_cpu.stime + new_cpu.itime 455 + new_cpu.iowtime + new_cpu.irqtime + new_cpu.sirqtime) 456 - (old_cpu.utime + old_cpu.ntime + old_cpu.stime + old_cpu.itime 457 + old_cpu.iowtime + old_cpu.irqtime + old_cpu.sirqtime); 458 459 qsort(new_procs, num_new_procs, sizeof(struct proc_info *), proc_cmp); 460 461 if (call++ > 0) printf("\n\n\n"); 462 printf("User %ld%%, System %ld%%, IOW %ld%%, IRQ %ld%%\n", 463 ((new_cpu.utime + new_cpu.ntime) - (old_cpu.utime + old_cpu.ntime)) * 100 / total_delta_time, 464 ((new_cpu.stime ) - (old_cpu.stime)) * 100 / total_delta_time, 465 ((new_cpu.iowtime) - (old_cpu.iowtime)) * 100 / total_delta_time, 466 ((new_cpu.irqtime + new_cpu.sirqtime) 467 - (old_cpu.irqtime + old_cpu.sirqtime)) * 100 / total_delta_time); 468 printf("User %ld + Nice %ld + Sys %ld + Idle %ld + IOW %ld + IRQ %ld + SIRQ %ld = %ld\n", 469 new_cpu.utime - old_cpu.utime, 470 new_cpu.ntime - old_cpu.ntime, 471 new_cpu.stime - old_cpu.stime, 472 new_cpu.itime - old_cpu.itime, 473 new_cpu.iowtime - old_cpu.iowtime, 474 new_cpu.irqtime - old_cpu.irqtime, 475 new_cpu.sirqtime - old_cpu.sirqtime, 476 total_delta_time); 477 printf("\n"); 478 if (!threads) 479 printf("%5s %-8s %2s %3s %4s %1s %5s %7s %7s %3s %s\n", "PID", "USER", "PR", "NI", "CPU%", "S", "#THR", "VSS", "RSS", "PCY", "Name"); 480 else 481 printf("%5s %5s %-8s %2s %3s %4s %1s %7s %7s %3s %-15s %s\n", "PID", "TID", "USER", "PR", "NI", "CPU%", "S", "VSS", "RSS", "PCY", "Thread", "Proc"); 482 483 for (i = 0; i < num_new_procs; i++) { 484 proc = new_procs[i]; 485 486 if (!proc || (max_procs && (i >= max_procs))) 487 break; 488 struct passwd* user = getpwuid(proc->uid); 489 char user_buf[20]; 490 char* user_str; 491 if (user && user->pw_name) { 492 user_str = user->pw_name; 493 } else { 494 snprintf(user_buf, 20, "%d", proc->uid); 495 user_str = user_buf; 496 } 497 if (!threads) { 498 printf("%5d %-8.8s %2s %3ld %3" PRIu64 "%% %c %5d %6" PRIu64 "K %6" PRIu64 "K %3s %s\n", 499 proc->pid, user_str, proc->pr, proc->ni, 500 proc->delta_time * 100 / total_delta_time, proc->state, proc->num_threads, 501 proc->vss / 1024, proc->rss * getpagesize() / 1024, proc->policy, 502 proc->name[0] != 0 ? proc->name : proc->tname); 503 } else { 504 printf("%5d %5d %-8.8s %2s %3ld %3" PRIu64 "%% %c %6" PRIu64 "K %6" PRIu64 "K %3s %-15s %s\n", 505 proc->pid, proc->tid, user_str, proc->pr, proc->ni, 506 proc->delta_time * 100 / total_delta_time, proc->state, 507 proc->vss / 1024, proc->rss * getpagesize() / 1024, proc->policy, 508 proc->tname, proc->name); 509 } 510 } 511} 512 513static struct proc_info *find_old_proc(pid_t pid, pid_t tid) { 514 int i; 515 516 for (i = 0; i < num_old_procs; i++) 517 if (old_procs[i] && (old_procs[i]->pid == pid) && (old_procs[i]->tid == tid)) 518 return old_procs[i]; 519 520 return NULL; 521} 522 523static void free_old_procs(void) { 524 int i; 525 526 for (i = 0; i < num_old_procs; i++) 527 if (old_procs[i]) 528 free_proc(old_procs[i]); 529 530 free(old_procs); 531} 532 533static int proc_cpu_cmp(const void *a, const void *b) { 534 struct proc_info *pa, *pb; 535 536 pa = *((struct proc_info **)a); pb = *((struct proc_info **)b); 537 538 if (!pa && !pb) return 0; 539 if (!pa) return 1; 540 if (!pb) return -1; 541 542 return -numcmp(pa->delta_time, pb->delta_time); 543} 544 545static int proc_vss_cmp(const void *a, const void *b) { 546 struct proc_info *pa, *pb; 547 548 pa = *((struct proc_info **)a); pb = *((struct proc_info **)b); 549 550 if (!pa && !pb) return 0; 551 if (!pa) return 1; 552 if (!pb) return -1; 553 554 return -numcmp(pa->vss, pb->vss); 555} 556 557static int proc_rss_cmp(const void *a, const void *b) { 558 struct proc_info *pa, *pb; 559 560 pa = *((struct proc_info **)a); pb = *((struct proc_info **)b); 561 562 if (!pa && !pb) return 0; 563 if (!pa) return 1; 564 if (!pb) return -1; 565 566 return -numcmp(pa->rss, pb->rss); 567} 568 569static int proc_thr_cmp(const void *a, const void *b) { 570 struct proc_info *pa, *pb; 571 572 pa = *((struct proc_info **)a); pb = *((struct proc_info **)b); 573 574 if (!pa && !pb) return 0; 575 if (!pa) return 1; 576 if (!pb) return -1; 577 578 return -numcmp(pa->num_threads, pb->num_threads); 579} 580 581static int numcmp(long long a, long long b) { 582 if (a < b) return -1; 583 if (a > b) return 1; 584 return 0; 585} 586 587static void usage(char *cmd) { 588 fprintf(stderr, "Usage: %s [ -m max_procs ] [ -n iterations ] [ -d delay ] [ -s sort_column ] [ -t ] [ -h ]\n" 589 " -m num Maximum number of processes to display.\n" 590 " -n num Updates to show before exiting.\n" 591 " -d num Seconds to wait between updates.\n" 592 " -s col Column to sort by (cpu,vss,rss,thr).\n" 593 " -H Show threads instead of processes.\n" 594 " -h Display this help screen.\n", 595 cmd); 596} 597