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