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 <stdbool.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <curses.h>
28#include "top.h"
29#include "libtop.h"
30#include "globalstats.h"
31#include "layout.h"
32#include "log.h"
33#include "userinput.h"
34#include "preferences.h"
35
36const libtop_tsamp_t *tsamp;
37
38static int sort_subcomp(int a_key, const libtop_psamp_t *a_a,
39			const libtop_psamp_t *a_b);
40
41static bool top_do_relayout = false;
42
43/* This may not be called in common update patterns. */
44void top_relayout_force(void) {
45    top_do_relayout = true;
46}
47
48void top_relayout(struct statistics_controller *c, int type, int maxwidth) {
49    top_do_relayout = true;
50}
51
52bool top_need_relayout(void) {
53    return top_do_relayout;
54}
55
56void *top_create(WINDOW *wmain) {
57    void *gstats;
58    struct statistics_controller *controller;
59
60    gstats = top_globalstats_create(wmain);
61
62    if(NULL == gstats) {
63	endwin();
64	fprintf(stderr, "unable to create global stats!\n");
65	_exit(EXIT_FAILURE);
66    }
67
68    controller = create_statistics_controller(wmain);
69
70    if(NULL == controller) {
71	endwin();
72	fprintf(stderr, "unable to create controller for main window!\n");
73	_exit(EXIT_FAILURE);
74    }
75
76    controller->globalstats = gstats;
77
78    return controller;
79}
80
81
82static int top_sort(void *a_data, const libtop_psamp_t *a, const libtop_psamp_t *b) {
83    int retval;
84
85    retval = sort_subcomp(top_prefs_get_sort(), a, b) *
86	(top_prefs_get_ascending() ? 1 : -1);
87    if (retval == 0) {
88	retval = sort_subcomp(top_prefs_get_secondary_sort(), a, b) *
89	    (top_prefs_get_secondary_ascending() ? 1 : -1);
90    }
91
92    return retval;
93}
94
95#define COMP(a,b) (((a)==(b)?0:	\
96		    ((a)<(b)?-1:1)))
97
98static int sort_subcomp(int a_key, const libtop_psamp_t *a_a,
99		     const libtop_psamp_t *a_b) {
100    struct timeval  tv_a, tv_b;
101    const char      *user_a, *user_b;
102
103    switch (a_key) {
104    case STATISTIC_PID: return COMP(a_a->pid, a_b->pid);
105
106    case STATISTIC_COMMAND: return COMP(strcmp(a_a->command, a_b->command),0);
107
108    case STATISTIC_CPU:
109	timersub(&a_a->total_time, &a_a->p_total_time, &tv_a);
110	timersub(&a_b->total_time, &a_b->p_total_time, &tv_b);
111
112	if(tv_a.tv_sec == tv_b.tv_sec) {
113	    return COMP(tv_a.tv_usec, tv_b.tv_usec);
114	} else {
115	    return COMP(tv_a.tv_sec, tv_b.tv_sec);
116	}
117
118    case STATISTIC_TIME:
119	 tv_a = a_a->total_time;
120	 tv_b = a_b->total_time;
121
122	 if(tv_a.tv_sec == tv_b.tv_sec) {
123	     return COMP(tv_a.tv_usec, tv_b.tv_usec);
124	 } else {
125	     return COMP(tv_a.tv_sec, tv_b.tv_sec);
126	 }
127
128    case STATISTIC_THREADS: return COMP(a_a->th, a_b->th);
129
130    case STATISTIC_WORKQUEUE: return COMP(a_a->wq_nthreads, a_b->wq_nthreads);
131
132    case STATISTIC_PORTS: return COMP(a_a->prt, a_b->prt);
133
134    case STATISTIC_MREGION: return COMP(a_a->reg, a_b->reg);
135
136#ifdef TOP_ANONYMOUS_MEMORY
137    case STATISTIC_RMEM: return COMP(a_a->anonymous, a_b->anonymous);
138
139    case STATISTIC_RPRVT: return COMP(a_a->rprvt, a_b->rprvt);
140
141    case STATISTIC_PURG: return COMP(a_a->purgeable, a_b->purgeable);
142
143    case STATISTIC_COMPRESSED: return COMP(a_a->compressed, a_b->compressed);
144#else
145    case STATISTIC_RPRVT: return COMP(a_a->rprvt, a_b->rprvt);
146
147    case STATISTIC_RSHRD: return COMP(a_a->rshrd, a_b->rshrd);
148
149    case STATISTIC_RSIZE: return COMP(a_a->rsize, a_b->rsize);
150#endif
151
152    case STATISTIC_VSIZE: return COMP(a_a->vsize, a_b->vsize);
153
154    case STATISTIC_VPRVT: return COMP(a_a->vprvt, a_b->vprvt);
155
156    case STATISTIC_PGRP: return COMP(a_a->pgrp, a_b->pgrp);
157
158    case STATISTIC_PPID: return COMP(a_a->ppid, a_b->ppid);
159
160    case STATISTIC_KPRVT: return COMP(a_a->palloc - a_a->pfree,
161				     a_b->palloc - a_b->pfree);
162
163    case STATISTIC_KSHRD: return COMP(a_a->salloc - a_a->sfree,
164				     a_b->salloc - a_b->sfree);
165
166    case STATISTIC_PSTATE: {
167	const char *a = libtop_state_str(a_a->state);
168	const char *b = libtop_state_str(a_b->state);
169
170	return COMP(strcmp(a, b), 0);
171    }
172    case STATISTIC_UID: return COMP(a_a->uid, a_b->uid);
173
174    case STATISTIC_FAULTS:
175	return COMP(a_a->faults.now, a_b->faults.now);
176
177    case STATISTIC_COW_FAULTS:
178	return COMP(a_a->cow_faults.now, a_b->cow_faults.now);
179
180    case STATISTIC_MESSAGES_SENT:
181	return COMP(a_a->messages_sent.now, a_b->messages_sent.now);
182
183    case STATISTIC_MESSAGES_RECEIVED:
184	return COMP(a_a->messages_recv.now, a_b->messages_recv.now);
185
186    case STATISTIC_SYSBSD:
187	return COMP(a_a->syscalls_bsd.now, a_b->syscalls_bsd.now);
188
189    case STATISTIC_SYSMACH:
190	return COMP(a_a->syscalls_mach.now, a_b->syscalls_mach.now);
191
192    case STATISTIC_CSW:
193	return COMP(a_a->csw.now, a_b->csw.now);
194
195    case STATISTIC_PAGEINS:
196	return COMP(a_a->pageins.now, a_b->pageins.now);
197
198    case STATISTIC_IDLEWAKE:
199	return COMP(a_a->power.task_platform_idle_wakeups, a_b->power.task_platform_idle_wakeups);
200
201    case STATISTIC_POWERSCORE: {
202	uint64_t a_idlew = a_a->power.task_platform_idle_wakeups - a_a->p_power.task_platform_idle_wakeups;
203	uint64_t b_idlew = a_b->power.task_platform_idle_wakeups - a_b->p_power.task_platform_idle_wakeups;
204	timersub(&a_a->total_time, &a_a->p_total_time, &tv_a);
205	timersub(&a_b->total_time, &a_b->p_total_time, &tv_b);
206	uint64_t a_usec = tv_a.tv_usec + (UINT64_C(1000000) * tv_a.tv_sec) + (UINT64_C(500) * a_idlew);
207	uint64_t b_usec = tv_b.tv_usec + (UINT64_C(1000000) * tv_b.tv_sec) + (UINT64_C(500) * b_idlew);
208
209	// kernel gets a free ride
210	if (a_a->pid == 0) {
211		a_usec = UINT64_C(0);
212	} else if (a_b->pid == 0) {
213		b_usec = UINT64_C(0);
214	}
215	return COMP(a_usec, b_usec);
216    }
217
218    case STATISTIC_USER:
219	/* Handle the == case first, since it's common. */
220	if (a_a->uid == a_b->uid) return 0;
221
222	user_a = libtop_username(a_a->uid);
223	user_b = libtop_username(a_b->uid);
224
225	return COMP(strcmp(user_a, user_b),0);
226    }
227
228    return 0;
229}
230
231#undef COMP
232
233void top_sample(void) {
234    if(libtop_sample(/*calculate mreg, vprvt and more see libtop.h*/
235		     top_prefs_get_mmr(),
236		     /*framework stats*/
237		     top_prefs_get_frameworks())) {
238	endwin();
239	fprintf(stderr, "error: while gathering a libtop sample.\n"
240		"The permissions and/or ownership are incorrect "
241		"for this executable, or you are testing without sudo.\n");
242	_exit(EXIT_FAILURE);
243    }
244}
245
246void top_insert(void *ptr) {
247    struct statistics_controller *c = ptr;
248    const libtop_psamp_t *psample;
249    char *user;
250    unsigned long uid = 0;
251    int nprocs;
252    pid_t pid;
253    bool have_pid = false;
254
255    c->reset_insertion(c);
256
257    top_sample();
258
259    /*
260     * The ordering is important here, because the libtop_psort actually
261     * updates the tsamp->nprocs.
262     */
263    libtop_psort(top_sort, NULL);
264
265    tsamp = libtop_tsamp();
266
267    if(top_globalstats_update(c->globalstats, tsamp))
268	top_log("An error occurred while updating global stats.\n");
269
270    /* The user has requested only displaying processes owned by user. */
271    user = top_prefs_get_user();
272
273    if(user)
274	uid = top_prefs_get_user_uid();
275
276    nprocs = top_prefs_get_nprocs();
277
278    if(0 == nprocs)
279	return;
280
281    have_pid = top_prefs_get_pid(&pid);
282
283    for(psample = libtop_piterate(); psample; psample = libtop_piterate()) {
284	if(user && psample->uid != uid)
285	    continue;
286
287	if(have_pid) {
288	    if(pid != psample->pid)
289		continue;
290	}
291
292	c->insert_sample(c, psample);
293
294	/*
295	 * If nprocs is -1 (the default), or otherwise negative,
296	 * then display all.
297	 */
298	if(nprocs > 0)
299	    --nprocs;
300
301	if(0 == nprocs) {
302	    break;
303	}
304    }
305}
306
307
308/* Return true if a non-fatal error occurred. */
309bool top_layout(void *ptr) {
310    struct statistics_controller *c = ptr;
311    int lines = LINES, cols = COLS;
312    int consumed_height = 0;
313    int pstatheight;
314
315    //top_log("%s\n", __func__);
316
317    top_do_relayout = false;
318
319    //fprintf(stderr, "laying out: lines %d cols %d\n", lines, cols);
320    //werase(c->parent);
321
322    if(ERR == wresize(c->parent, lines, cols)) {
323	top_log("error: wresizing parent!\n");
324	return true;
325    }
326
327    top_globalstats_reset(c->globalstats);
328
329    if(top_globalstats_resize(c->globalstats, cols, lines,
330			      &consumed_height)) {
331	top_log("error: performing global stats resize!\n");
332	return true;
333    }
334
335    user_input_set_position(consumed_height, 0);
336
337    pstatheight = lines - consumed_height - 1;
338    if(pstatheight <= 0)
339	return true;
340
341    //fprintf(stderr, "consumed_height %d\n", consumed_height);
342
343    if(layout_statistics(c, cols, pstatheight,
344			 /*y*/ consumed_height + 1)) {
345	top_log("error: performing statistic layout!\n");
346	return true;
347    }
348
349    return false;
350}
351
352struct draw_state {
353    int xoffset;
354};
355
356static bool top_draw_iterator(struct statistic *s, void *ptr) {
357    struct draw_state *state = ptr;
358
359    s->callbacks.draw(s, /*x*/ state->xoffset);
360
361    state->xoffset = 1;
362
363    /* more iterations */
364    return true;
365}
366
367void top_draw(void *ptr) {
368    struct statistics_controller *c = ptr;
369    struct draw_state state;
370
371    werase(c->parent);
372
373    top_globalstats_draw(c->globalstats);
374
375    user_input_draw(ptr, stdscr);
376
377    state.xoffset = 0;
378
379    c->iterate(c, top_draw_iterator, &state);
380
381    /* This moves the insertion cursor to the lower right. */
382    wmove(stdscr, LINES - 1, COLS - 1);
383
384    update_panels();
385    doupdate();
386}
387