1/* 2 * Copyright (c) 2008 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * The contents of this file constitute Original Code as defined in and 7 * are subject to the Apple Public Source License Version 1.1 (the 8 * "License"). You may not use this file except in compliance with the 9 * License. Please obtain a copy of the License at 10 * http://www.apple.com/publicsource and read it before using this file. 11 * 12 * This Original Code and all software distributed under the License are 13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER 14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the 17 * License for the specific language governing rights and limitations 18 * under the License. 19 * 20 * @APPLE_LICENSE_HEADER_END@ 21 */ 22 23#include <stdio.h> 24#include <stdlib.h> 25#include <string.h> 26#include <signal.h> 27#include <assert.h> 28#include <ctype.h> 29#include <unistd.h> 30#include <libproc.h> 31#include "preferences.h" 32#include "statistic.h" 33#include "top.h" 34 35static struct { 36 int mode; 37 int sort_by; 38 int secondary_sort; 39 bool sort_ascending; 40 bool secondary_sort_ascending; 41 int sleep_seconds; 42 bool frameworks; 43 int frameworks_interval; 44 char *user; 45 uid_t uid; 46 int samples; 47 int nprocs; 48 bool have_pid; 49 pid_t pid; 50 bool logging_mode; 51 bool have_ncols; 52 int ncols; 53 bool show_swap; 54 bool mmr; /* memory map reporting */ 55 bool delta_forced_mmr; /* delta mode forced mmr */ 56 57 /* These are initialized in top_prefs_init() below. */ 58 struct { 59 int total; 60 int array[STATISTIC_TOTAL]; 61 } display_stats; 62 63 const char *signal_string; 64 int signal_number; 65} prefs = { 66 .mode = STATMODE_NON_EVENT, 67 .sort_by = STATISTIC_PID, 68 .secondary_sort = STATISTIC_PID, 69 .sort_ascending = false, 70 .secondary_sort_ascending = false, 71 .sleep_seconds = 1, 72 .frameworks = true, 73 .frameworks_interval = 10, 74 .user = NULL, 75 .uid = 0, 76 .samples = -1, 77 .nprocs = -1, 78 .have_pid = false, 79 .pid = 0, 80 .logging_mode = false, 81 .have_ncols = false, 82 .ncols = -1, 83 .show_swap = false, 84 .mmr = true, 85 .delta_forced_mmr = false 86}; 87 88static struct { 89 const char *string; 90 int e; 91} stat_map[] = { 92 {"pid", STATISTIC_PID}, 93 {"command", STATISTIC_COMMAND}, 94 {"cpu", STATISTIC_CPU}, 95 {"csw", STATISTIC_CSW}, 96 {"time", STATISTIC_TIME}, 97 /*alias*/ 98 {"threads", STATISTIC_THREADS}, 99 {"th", STATISTIC_THREADS}, 100 /*alias*/ 101 {"ports", STATISTIC_PORTS}, 102 {"prt", STATISTIC_PORTS}, 103 /*alias*/ 104 {"mregion", STATISTIC_MREGION}, 105 /*alias*/ 106 {"mreg", STATISTIC_MREGION}, 107 /*alias*/ 108 {"mregs", STATISTIC_MREGION}, 109 {"reg", STATISTIC_MREGION}, 110#ifdef TOP_ANONYMOUS_MEMORY 111 {"mem", STATISTIC_RMEM}, 112 {"rsize", STATISTIC_RMEM}, /* alias */ 113 {"rprvt", STATISTIC_RPRVT}, 114 {"purg", STATISTIC_PURG}, 115 {"compress", STATISTIC_COMPRESSED}, 116 {"cmprs", STATISTIC_COMPRESSED}, /* alias */ 117 {"compressed", STATISTIC_COMPRESSED}, /* alias */ 118#else 119 {"rprvt", STATISTIC_RPRVT}, 120 {"rshrd", STATISTIC_RSHRD}, 121 {"rsize", STATISTIC_RSIZE}, 122#endif 123 {"vsize", STATISTIC_VSIZE}, 124 {"vprvt", STATISTIC_VPRVT}, 125 {"pgrp", STATISTIC_PGRP}, 126 {"ppid", STATISTIC_PPID}, 127 {"state", STATISTIC_PSTATE}, 128 {"pstate", STATISTIC_PSTATE}, 129 {"uid", STATISTIC_UID}, 130 {"wq", STATISTIC_WORKQUEUE}, 131 /*alias*/ 132 {"#wq", STATISTIC_WORKQUEUE}, 133 /*alias*/ 134 {"workqueue", STATISTIC_WORKQUEUE}, 135 {"faults", STATISTIC_FAULTS}, 136 /*alias*/ 137 {"fault", STATISTIC_FAULTS}, 138 {"cow", STATISTIC_COW_FAULTS}, 139 /*alias*/ 140 {"cow_faults", STATISTIC_COW_FAULTS}, 141 {"user", STATISTIC_USER}, 142 {"username", STATISTIC_USER}, 143 {"msgsent", STATISTIC_MESSAGES_SENT}, 144 {"msgrecv", STATISTIC_MESSAGES_RECEIVED}, 145 {"sysbsd", STATISTIC_SYSBSD}, 146 {"sysmach", STATISTIC_SYSMACH}, 147 {"pageins", STATISTIC_PAGEINS}, 148 {"kprvt", STATISTIC_KPRVT}, 149 {"kshrd", STATISTIC_KSHRD}, 150 {"idlew", STATISTIC_IDLEWAKE}, 151 {"power", STATISTIC_POWERSCORE}, 152 {NULL, 0} 153}; 154 155static struct { 156 const char *string; 157 int value; 158} signal_map[] = { 159 {"HUP", SIGHUP}, 160 {"INT", SIGINT}, 161 {"QUIT", SIGQUIT}, 162 {"ILL", SIGILL}, 163 {"TRAP", SIGTRAP}, 164 {"ABRT", SIGABRT}, 165 {"IOT", SIGIOT}, 166 {"EMT", SIGEMT}, 167 {"FPE", SIGFPE}, 168 {"KILL", SIGKILL}, 169 {"BUS", SIGBUS}, 170 {"SEGV", SIGSEGV}, 171 {"SYS", SIGSYS}, 172 {"PIPE", SIGPIPE}, 173 {"ALRM", SIGALRM}, 174 {"TERM", SIGTERM}, 175 {"URG", SIGURG}, 176 {"STOP", SIGSTOP}, 177 {"TSTP", SIGTSTP}, 178 {"CONT", SIGCONT}, 179 {"CHLD", SIGCHLD}, 180 {"TTIN", SIGTTIN}, 181 {"TTOU", SIGTTOU}, 182 {"IO", SIGIO}, 183 {"XCPU", SIGXCPU}, 184 {"XFSZ", SIGXFSZ}, 185 {"VTALRM", SIGVTALRM}, 186 {"PROF", SIGPROF}, 187 {"WINCH", SIGWINCH}, 188 {"INFO", SIGINFO}, 189 {"USR1", SIGUSR1}, 190 {"USR2", SIGUSR2}, 191 {NULL, -1} 192}; 193 194void top_prefs_init(void) { 195 int i; 196 197 prefs.display_stats.total = 0; 198 199#define SPREF(e) do { \ 200 assert(prefs.display_stats.total < STATISTIC_TOTAL); \ 201 prefs.display_stats.array[prefs.display_stats.total] = e; \ 202 prefs.display_stats.total++; \ 203 } while(0) 204 205 SPREF(STATISTIC_PID); 206 SPREF(STATISTIC_COMMAND); 207 SPREF(STATISTIC_CPU); 208 SPREF(STATISTIC_TIME); 209 SPREF(STATISTIC_THREADS); 210#ifdef PROC_PIDWORKQUEUEINFO 211 SPREF(STATISTIC_WORKQUEUE); 212#endif 213 SPREF(STATISTIC_PORTS); 214 SPREF(STATISTIC_MREGION); 215#ifdef TOP_ANONYMOUS_MEMORY 216 SPREF(STATISTIC_RMEM); 217 SPREF(STATISTIC_RPRVT); 218 SPREF(STATISTIC_PURG); 219 SPREF(STATISTIC_COMPRESSED); 220#else 221 SPREF(STATISTIC_RPRVT); 222 SPREF(STATISTIC_RSHRD); 223 SPREF(STATISTIC_RSIZE); 224#endif 225 SPREF(STATISTIC_VPRVT); 226 SPREF(STATISTIC_VSIZE); 227 SPREF(STATISTIC_PGRP); 228 SPREF(STATISTIC_PPID); 229 SPREF(STATISTIC_PSTATE); 230 SPREF(STATISTIC_UID); 231 SPREF(STATISTIC_FAULTS); 232 SPREF(STATISTIC_COW_FAULTS); 233 SPREF(STATISTIC_MESSAGES_SENT); 234 SPREF(STATISTIC_MESSAGES_RECEIVED); 235 SPREF(STATISTIC_SYSBSD); 236 SPREF(STATISTIC_SYSMACH); 237 SPREF(STATISTIC_CSW); 238 SPREF(STATISTIC_PAGEINS); 239 SPREF(STATISTIC_KPRVT); 240 SPREF(STATISTIC_KSHRD); 241 SPREF(STATISTIC_IDLEWAKE); 242 SPREF(STATISTIC_POWERSCORE); 243 SPREF(STATISTIC_USER); 244 245#undef SPREF 246 247 for(i = 0; signal_map[i].string; ++i) { 248 if(SIGTERM == signal_map[i].value) { 249 prefs.signal_string = signal_map[i].string; 250 prefs.signal_number = SIGTERM; 251 break; 252 } 253 } 254} 255 256/* MODE */ 257static struct { 258 const char *string; 259 int e; /*enum*/ 260} mode_map[] = { 261 {"a", STATMODE_ACCUM}, 262 {"d", STATMODE_DELTA}, 263 {"e", STATMODE_EVENT}, 264 {"n", STATMODE_NON_EVENT}, 265 {NULL, 0} 266}; 267 268/* Return true if an error occurred. */ 269bool top_prefs_set_mode(const char *mode) { 270 int i; 271 272 for(i = 0; mode_map[i].string; ++i) { 273 if(!strcmp(mode, mode_map[i].string)) { 274 prefs.mode = mode_map[i].e; 275 276 if(STATMODE_DELTA == prefs.mode) { 277 if(top_prefs_get_mmr()) { 278 /* 279 * By default we turn off memory map reporting (mmr) 280 * in delta mode. It uses too much CPU time in 281 * some cases. Users can re-enable the mmr option 282 * interactively or after specifying the mode at the 283 * command line with -r after -c d. 284 * 285 * The effected stats should display N/A. 286 */ 287 top_prefs_set_mmr(false); 288 prefs.delta_forced_mmr = true; 289 } 290 } else { 291 if(prefs.delta_forced_mmr) { 292 /* Delta mode forced the mmr off. */ 293 top_prefs_set_mmr(true); 294 prefs.delta_forced_mmr = false; 295 } 296 } 297 298 return false; 299 } 300 } 301 302 return true; /*error*/ 303} 304 305int top_prefs_get_mode(void) { 306 return prefs.mode; 307} 308 309const char *top_prefs_get_mode_string(void) { 310 int i; 311 312 for(i = 0; mode_map[i].string; ++i) { 313 if(prefs.mode == mode_map[i].e) 314 return mode_map[i].string; 315 } 316 317 return NULL; 318} 319 320 321/* SLEEP */ 322void top_prefs_set_sleep(int seconds) { 323 prefs.sleep_seconds = seconds; 324} 325 326int top_prefs_get_sleep(void) { 327 return prefs.sleep_seconds; 328} 329 330/* Specify NULL for ascending if the sort shouldn't allow a + or - prefix. */ 331/* This returns true when it has found the matching enum value. */ 332/* NOTE: The caller should initialize ascending to avoid an invalid value. */ 333static bool find_sort(const char *sortkey, int *e, bool *ascending) { 334 int i; 335 336 if(ascending) { 337 if('+' == sortkey[0]) { 338 *ascending = true; 339 ++sortkey; 340 } else if('-' == sortkey[0]) { 341 *ascending = false; 342 ++sortkey; 343 } 344 } 345 346 for(i = 0; stat_map[i].string; ++i) { 347 if(!strcasecmp(sortkey, stat_map[i].string)) { 348 *e = stat_map[i].e; 349 return true; 350 } 351 } 352 353 return false; 354} 355 356/*Return true if an error occurred.*/ 357bool top_prefs_set_sort(const char *sortkey) { 358 bool ascending; 359 int e; 360 361 ascending = top_prefs_get_ascending(); 362 363 if(find_sort(sortkey, &e, &ascending)) { 364 top_prefs_set_ascending(ascending); 365 prefs.sort_by = e; 366 return false; 367 } 368 369 /* Not found -- error */ 370 return true; 371} 372 373int top_prefs_get_sort(void) { 374 return prefs.sort_by; 375} 376 377/* Return true if an error occurred. */ 378bool top_prefs_set_secondary_sort(const char *sortkey) { 379 bool ascending; 380 int e; 381 382 ascending = top_prefs_get_secondary_ascending(); 383 384 if(find_sort(sortkey, &e, &ascending)) { 385 top_prefs_set_secondary_ascending(ascending); 386 prefs.secondary_sort = e; 387 return false; 388 } 389 390 return true; 391} 392 393 394int top_prefs_get_secondary_sort(void) { 395 return prefs.secondary_sort; 396} 397 398static const char *enum_value_to_sort_string(int e) { 399 int i; 400 401 for(i = 0; stat_map[i].string; ++i) { 402 if(e == stat_map[i].e) { 403 return stat_map[i].string; 404 } 405 } 406 407 fprintf(stderr, "Invalid enum value: %d in %s!\n", e, __func__); 408 abort(); 409} 410 411const char *top_prefs_get_sort_string(void) { 412 return enum_value_to_sort_string(prefs.sort_by); 413} 414 415const char *top_prefs_get_secondary_sort_string(void) { 416 return enum_value_to_sort_string(prefs.secondary_sort); 417} 418 419/* ASCENDING/DESCENDING */ 420 421void top_prefs_set_ascending(bool flag) { 422 prefs.sort_ascending = flag; 423} 424 425bool top_prefs_get_ascending(void) { 426 return prefs.sort_ascending; 427} 428 429void top_prefs_set_secondary_ascending(bool flag) { 430 prefs.secondary_sort_ascending = flag; 431} 432 433bool top_prefs_get_secondary_ascending(void) { 434 return prefs.secondary_sort_ascending; 435} 436 437void top_prefs_set_frameworks(bool flag) { 438 prefs.frameworks = flag; 439} 440 441bool top_prefs_get_frameworks(void) { 442 return prefs.frameworks; 443} 444 445void top_prefs_set_frameworks_interval(int interval) { 446 prefs.frameworks_interval = interval; 447} 448 449int top_prefs_get_frameworks_interval(void) { 450 return prefs.frameworks_interval; 451} 452 453void top_prefs_set_user(const char *user) { 454 free(prefs.user); 455 prefs.user = NULL; 456 457 if(strlen(user)) { 458 prefs.user = strdup(user); 459 } 460} 461 462char *top_prefs_get_user(void) { 463 return prefs.user; 464} 465 466void top_prefs_set_user_uid(uid_t uid) { 467 prefs.uid = uid; 468} 469 470uid_t top_prefs_get_user_uid(void) { 471 return prefs.uid; 472} 473 474/* Return true if found. */ 475static bool find_stat_enum(const char *key, int *e) { 476 int i; 477 478 for(i = 0; stat_map[i].string; ++i) { 479 if(!strcasecmp(key, stat_map[i].string)) { 480 *e = stat_map[i].e; 481 return true; 482 } 483 } 484 485 return false; 486} 487 488/* Take a comma separated list of names. */ 489/* Return true if an error occurred. */ 490bool top_prefs_set_stats(const char *names) { 491 char key[20]; 492 int key_offset = 0; 493 const char *np; 494 int stat_enum_array[STATISTIC_TOTAL]; 495 int stat_enum_array_offset = 0; 496 int e, i; 497 498 for(np = names; *np; ++np) { 499 if(isspace(*np)) 500 continue; 501 502 /* Check for a comma separating the keys. */ 503 if(',' == *np) { 504 key[key_offset++] = '\0'; 505 506 if(!find_stat_enum(key, &e)) { 507 fprintf(stderr, "invalid stat: %s\n", key); 508 return true; 509 } 510 511 if(stat_enum_array_offset >= STATISTIC_TOTAL) { 512 fprintf(stderr, "too many stats specified.\n"); 513 return true; 514 } 515 516 stat_enum_array[stat_enum_array_offset++] = e; 517 518 key_offset = 0; 519 } else { 520 key[key_offset++] = *np; 521 522 /* Check if we would exceed the length of the buffer. */ 523 if(key_offset >= (sizeof(key) - 1)) { 524 fprintf(stderr, "invalid input: longer than any valid stat.\n"); 525 return true; 526 } 527 } 528 } 529 530 /* See if we had a trailing key without a comma. */ 531 if(key_offset > 0) { 532 key[key_offset++] = '\0'; 533 534 if(!find_stat_enum(key, &e)) { 535 fprintf(stderr, "invalid stat: %s\n", key); 536 return true; 537 } 538 539 if(stat_enum_array_offset >= STATISTIC_TOTAL) { 540 fprintf(stderr, "too many stats specified.\n"); 541 return true; 542 } 543 544 stat_enum_array[stat_enum_array_offset++] = e; 545 546 key_offset = 0; 547 } 548 549 /* See if we had no keys at all. */ 550 if(stat_enum_array_offset <= 0) { 551 fprintf(stderr, "invalid input: %s\n", names); 552 return true; 553 } 554 555 /* Now set the stats. */ 556 for(i = 0; i < stat_enum_array_offset; ++i) { 557 prefs.display_stats.array[i] = stat_enum_array[i]; 558 } 559 560 prefs.display_stats.total = stat_enum_array_offset; 561 562 return false; 563} 564 565/* Return true if able to get the stats. */ 566bool top_prefs_get_stats(int *total, int **array) { 567 *total = prefs.display_stats.total; 568 *array = prefs.display_stats.array; 569 570 return true; 571} 572 573int top_prefs_get_samples(void) { 574 return prefs.samples; 575} 576 577void top_prefs_set_samples(int s) { 578 prefs.samples = s; 579} 580 581int top_prefs_get_nprocs(void) { 582 return prefs.nprocs; 583} 584 585void top_prefs_set_nprocs(int n) { 586 prefs.nprocs = n; 587} 588 589void top_prefs_set_pid(pid_t pid) { 590 prefs.have_pid = true; 591 prefs.pid = pid; 592} 593 594bool top_prefs_get_pid(pid_t *pidptr) { 595 if(prefs.have_pid) { 596 *pidptr = prefs.pid; 597 return true; 598 } 599 600 return false; 601} 602 603/* Return true if the signal string is invalid. */ 604bool top_prefs_set_signal_string(char *s) { 605 int i; 606 607 for(i = 0; signal_map[i].string; ++i) { 608 if(!strcasecmp(signal_map[i].string, s)) { 609 prefs.signal_string = signal_map[i].string; 610 prefs.signal_number = signal_map[i].value; 611 return false; 612 } 613 } 614 615 return true; 616} 617 618int top_prefs_get_signal(const char **sptr) { 619 *sptr = prefs.signal_string; 620 621 return prefs.signal_number; 622} 623 624void top_prefs_set_logging_mode(bool mode) { 625 prefs.logging_mode = mode; 626} 627 628bool top_prefs_get_logging_mode(void) { 629 return prefs.logging_mode; 630} 631 632void top_prefs_set_ncols(int limit) { 633 prefs.have_ncols = true; 634 prefs.ncols = limit; 635} 636 637bool top_prefs_get_ncols(int *limit) { 638 if(prefs.have_ncols) { 639 *limit = prefs.ncols; 640 return true; 641 } 642 643 return false; 644} 645 646void top_prefs_set_swap(bool show) { 647 prefs.show_swap = show; 648} 649 650bool top_prefs_get_swap(void) { 651 return prefs.show_swap; 652} 653 654void top_prefs_set_mmr(bool mmr) { 655 prefs.mmr = mmr; 656} 657 658bool top_prefs_get_mmr(void) { 659 return prefs.mmr; 660} 661