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