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