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