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 <stdlib.h>
24#include <string.h>
25#include <mach/mach_time.h>
26
27#include "statistic.h"
28
29#include "preferences.h"
30
31#include "pid.h"
32#include "pgrp.h"
33#include "ppid.h"
34#include "command.h"
35#include "cpu.h"
36#include "timestat.h"
37#include "threads.h"
38#include "ports.h"
39#include "memstats.h"
40#include "pstate.h"
41#include "user.h"
42#include "workqueue.h"
43#include "uid.h"
44#include "faults.h"
45#include "messages.h"
46#include "syscalls.h"
47#include "csw.h"
48#include "log.h"
49#include "power.h"
50
51struct statistic_name_map statistic_name_map[] = {
52    /*The order of this must match the enum in statistic.h. */
53    {STATISTIC_PID, top_pid_create, "PID"},
54    {STATISTIC_COMMAND, top_command_create, "COMMAND"},
55    {STATISTIC_CPU, top_cpu_create, "%CPU"},
56    {STATISTIC_CPU_ME, top_cpu_me_create, "%CPU_ME"},
57    {STATISTIC_CPU_OTHERS, top_cpu_others_create, "%CPU_OTHRS"},
58    {STATISTIC_BOOSTS, top_boosts_create, "BOOSTS"},
59    {STATISTIC_TIME, top_time_create, "TIME"},
60    {STATISTIC_THREADS, top_threadcount_create, "#TH"},
61    {STATISTIC_PORTS, top_ports_create, "#PORTS"},
62    {STATISTIC_MREGION, top_mregion_create, "#MREGS"},
63#ifdef TOP_ANONYMOUS_MEMORY
64    {STATISTIC_RMEM, top_rmem_create, "MEM"},
65    {STATISTIC_RPRVT, top_rprvt_create, "RPRVT"},
66    {STATISTIC_PURG, top_purg_create, "PURG"},
67    {STATISTIC_COMPRESSED, top_compressed_create, "CMPRS"},
68#else
69    {STATISTIC_RPRVT, top_rprvt_create, "RPRVT"},
70    {STATISTIC_RSHRD, top_rshrd_create, "RSHRD"},
71    {STATISTIC_RSIZE, top_rsize_create, "RSIZE"},
72#endif
73    {STATISTIC_VSIZE, top_vsize_create, "VSIZE"},
74    {STATISTIC_VPRVT, top_vprvt_create, "VPRVT"},
75    {STATISTIC_PGRP, top_pgrp_create, "PGRP"},
76    {STATISTIC_PPID, top_ppid_create, "PPID"},
77    {STATISTIC_PSTATE, top_pstate_create, "STATE"},
78    {STATISTIC_UID, top_uid_create, "UID"},
79    {STATISTIC_WORKQUEUE, top_workqueue_create, "#WQ"},
80    {STATISTIC_FAULTS, top_faults_create, "FAULTS"},
81    {STATISTIC_COW_FAULTS, top_cow_faults_create, "COW"},
82    {STATISTIC_MESSAGES_SENT, top_messages_sent_create, "MSGSENT"},
83    {STATISTIC_MESSAGES_RECEIVED, top_messages_received_create, "MSGRECV"},
84    {STATISTIC_SYSBSD, top_sysbsd_create, "SYSBSD"},
85    {STATISTIC_SYSMACH, top_sysmach_create, "SYSMACH"},
86    {STATISTIC_CSW, top_csw_create, "CSW"},
87    {STATISTIC_PAGEINS, top_pageins_create, "PAGEINS"},
88    {STATISTIC_KPRVT, top_kprvt_create, "KPRVT"},
89    {STATISTIC_KSHRD, top_kshrd_create, "KSHRD"},
90    {STATISTIC_IDLEWAKE, top_idlewake_create, "IDLEW"},
91    {STATISTIC_POWERSCORE, top_powerscore_create, "POWER"},
92    {STATISTIC_USER, top_user_create, "USER"},
93    {0, NULL, NULL}
94};
95
96static void reset_insertion(struct statistics_controller *c) {
97    struct statistic *s;
98    int i;
99
100    for(i = 0; i < c->get_total_possible(c); ++i) {
101	s = c->state[i].instance;
102
103	if(s) {
104	    s->callbacks.reset_insertion(s);
105	}
106    }
107}
108
109static void insert_sample(struct statistics_controller *c, const void *sample) {
110    struct statistic *s;
111    int i;
112
113    for(i = 0; i < c->get_total_possible(c); ++i) {
114	s = c->state[i].instance;
115
116	if(s) {
117	    if(s->callbacks.insert_cell(s, sample))
118		fprintf(stderr, "insert cell failed!\n");
119	}
120    }
121}
122
123static void remove_tail(struct statistics_controller *c) {
124    int i;
125
126    for(i = c->get_total_possible(c) - 1; i >= 0; --i) {
127	if(c->state[i].instance && c->state[i].instance->visible) {
128	    c->state[i].instance->visible = false;
129	    hide_panel(c->state[i].instance->panel);
130	    c->total_active_statistics--;
131	    return;
132	}
133    }
134}
135
136/* This is used with the controller to insert a statistic. */
137static void insert_tail(struct statistics_controller *c) {
138    int i;
139
140    for(i = 0; i < c->get_total_possible(c); ++i) {
141	if(c->state[i].instance && !c->state[i].instance->visible) {
142	    c->state[i].instance->visible = true;
143	    show_panel(c->state[i].instance->panel);
144	    top_panel(c->state[i].instance->panel);
145	    c->total_active_statistics++;
146	    return;
147	}
148    }
149}
150
151static int get_total_possible(struct statistics_controller *c) {
152    return c->total_possible_statistics;
153}
154
155static int get_total_active(struct statistics_controller *c) {
156    return c->total_active_statistics;
157}
158
159static void iterate(struct statistics_controller *c,
160		   bool (*func)(struct statistic *, void *),
161		   void *ptr) {
162    struct statistic *s;
163    int i;
164    bool log = top_prefs_get_logging_mode();
165
166    for(i = 0; i <  c->get_total_possible(c); ++i) {
167	s = c->state[i].instance;
168
169	if(s && (s->visible || log)) {
170	    if(func(s, ptr))
171		continue;
172
173	    break;
174	}
175    }
176}
177
178struct statistics_controller *create_statistics_controller(WINDOW *parent) {
179    struct statistics_controller *c;
180    int i;
181    int total = 0;
182    int *array;
183
184    c = malloc(sizeof *c);
185    if(NULL == c)
186	return NULL;
187
188    c->parent = parent;
189
190    for(i = 0; i < STATISTIC_TOTAL; ++i) {
191	c->state[i].type = i;
192	c->state[i].instance = NULL;
193	c->state[i].create_statistic = NULL;
194    }
195
196    top_prefs_get_stats(&total, &array);
197
198    for(i = 0; i < total; ++i) {
199	c->state[i].type = array[i];
200	c->state[i].create_statistic = statistic_name_map[array[i]].creator;
201	strcpy(c->state[i].name, statistic_name_map[array[i]].name);
202    }
203
204    c->total_possible_statistics = total;
205    c->total_active_statistics = 0;
206
207    c->reset_insertion = reset_insertion;
208    c->insert_sample = insert_sample;
209    c->remove_tail = remove_tail;
210    c->insert_tail = insert_tail;
211    c->get_total_possible = get_total_possible;
212    c->get_total_active = get_total_active;
213    c->iterate = iterate;
214
215    for(i = 0; i < c->get_total_possible(c); ++i) {
216	c->state[i].instance =
217	    c->state[i].create_statistic(c->parent, c->state[i].name);
218
219	if(NULL == c->state[i].instance) {
220	    free(c);
221	    return NULL;
222	}
223
224	c->state[i].instance->controller = c;
225    }
226
227    return c;
228}
229
230
231/* Return NULL if an error occurred. */
232struct statistic *create_statistic(int type, WINDOW *parent, void *ptr,
233				   struct statistic_callbacks *callbacks,
234				   const char *name) {
235    struct statistic *s;
236    bool log = top_prefs_get_logging_mode();
237
238    s = malloc(sizeof *s);
239
240    if(NULL == s) {
241	perror("malloc in create_statistic");
242	return NULL;
243    }
244
245    if(!log) {
246	s->parent = parent;
247	s->window = newwin(1, 1, 0, 0);
248
249	if(NULL == s->window) {
250	    free(s);
251	    return NULL;
252	}
253
254	s->panel = new_panel(s->window);
255
256	if(NULL == s->panel) {
257	    delwin(s->window);
258	    free(s);
259	    return NULL;
260	}
261    } else {
262	s->parent = NULL;
263	s->window = NULL;
264	s->panel = NULL;
265    }
266
267    s->type = type;
268    s->cells = NULL;
269    s->ptr = ptr;
270    s->header = strdup(name);
271
272    if(NULL == s->header) {
273	if(!log) {
274	    del_panel(s->panel);
275	    delwin(s->window);
276	}
277
278	free(s);
279	return NULL;
280    }
281
282    s->visible = false;
283
284    if(!log)
285	hide_panel(s->panel);
286
287    s->time_consumed = 0;
288    s->runs = 0;
289
290    s->callbacks = *callbacks;
291    s->destructors = NULL;
292    s->request_size.width = 0;
293    s->request_size.height = 0;
294    s->actual_size.width = 0;
295    s->actual_size.height = 0;
296
297    s->minimum_size.width = 1;
298    s->minimum_size.height = 1;
299
300    s->previous = NULL;
301    s->next = NULL;
302
303    return s;
304}
305
306void destroy_statistic(struct statistic *s) {
307    struct statistic_destructor *dtor, *dtornext;
308
309
310    for(dtor = s->destructors; dtor; ) {
311	dtornext = dtor->next;
312	dtor->destructor(s, dtor->ptr);
313	free(dtor);
314	dtor = dtornext;
315    }
316
317    werase(s->window);
318    del_panel(s->panel);
319    delwin(s->window);
320
321    if(s->header)
322	free(s->header);
323
324    free(s);
325}
326
327/* Return true if an error occurred. */
328bool create_statistic_destructor(struct statistic *s,
329				 void (*destructor)(struct statistic *, void *),
330				 void *ptr) {
331    struct statistic_destructor *dtor;
332
333    dtor = malloc(sizeof *dtor);
334    if(NULL == dtor) {
335	perror("malloc in create_statistic_destructor");
336	return true;
337    }
338
339    dtor->destructor = destructor;
340    dtor->ptr = ptr;
341    dtor->next = s->destructors;
342
343    s->destructors = dtor;
344
345    return false;
346}
347