1/*
2 * Copyright (c) 2008, 2009 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 <assert.h>
27#include <libutil.h>
28#include <inttypes.h>
29#include <panel.h>
30#include <sys/time.h>
31#include <time.h>
32#include <unistd.h>
33
34#include "libtop.h"
35#include "globalstats.h"
36#include "generic.h"
37#include "log.h"
38#include "preferences.h"
39#include "uinteger.h"
40#include "top.h"
41
42enum {
43    GLOBALSTAT_PROCESSES, GLOBALSTAT_TIME, GLOBALSTAT_LOAD,
44    GLOBALSTAT_CPU, GLOBALSTAT_SHAREDLIBS,
45    GLOBALSTAT_MEMREGIONS, GLOBALSTAT_PHYSMEM,
46    GLOBALSTAT_VM, GLOBALSTAT_SWAP, GLOBALSTAT_PURGEABLE,
47    GLOBALSTAT_NETWORKS, GLOBALSTAT_DISKS
48};
49
50enum { GLOBALSTAT_TOTAL = GLOBALSTAT_DISKS + 1 };
51
52enum { GLOBALSTAT_HN_FORMAT = HN_NOSPACE | HN_B };
53
54struct globalstat_size {
55    int width, height;
56};
57
58struct globalstat {
59    WINDOW *window;
60    PANEL *panel;
61    int type;
62    char data[120];
63    int length;
64    int x, y;
65    struct globalstat_size size;
66    int max_width;
67};
68
69struct globalstat_controller {
70    struct globalstat stats[GLOBALSTAT_TOTAL];
71    WINDOW *window;
72    PANEL *panel;
73};
74
75/* Return -1 if an error occurred. */
76static int humanize_globalstat(char *out, size_t s, int64_t n) {
77    return humanize_number(out, s, n, "", HN_AUTOSCALE, GLOBALSTAT_HN_FORMAT);
78}
79
80static bool init_globalstat(WINDOW *parent, struct globalstat *gs, int type) {
81
82    if(!top_prefs_get_logging_mode()) {
83	gs->window = newwin(1, 1, 0, 0);
84
85	if(NULL == gs->window)
86	    return true;
87
88	gs->panel = new_panel(gs->window);
89
90	if(NULL == gs->panel) {
91	    delwin(gs->window);
92	    return true;
93	}
94    }
95
96    gs->type = type;
97    gs->length = 0;
98    gs->x = 0;
99    gs->y = 0;
100    gs->size.width = 0;
101    gs->size.height = 0;
102    gs->max_width = 0;
103
104    return false;
105};
106
107static void update_max(struct globalstat *gs) {
108    if(gs->length > gs->max_width) {
109	gs->max_width = gs->length;
110	top_relayout_force();
111    }
112}
113
114static int get_globalstat_reqwidth(struct globalstat *gs) {
115    return gs->length + 1;
116}
117
118static void set_globalstat_geometry(struct globalstat *gs, int x, int y,
119				   int width, int height) {
120    gs->x = x;
121    gs->y = y;
122    gs->size.width = width;
123    gs->size.height = height;
124}
125
126void *top_globalstats_create(WINDOW *parent) {
127    struct globalstat_controller *c;
128    int i;
129
130    c = malloc(sizeof *c);
131
132    if(NULL == c)
133	return NULL;
134
135
136    if(!top_prefs_get_logging_mode()) {
137	c->window = newwin(1, 1, 0, 0);
138
139	if(NULL == c->window) {
140	    free(c);
141	    return NULL;
142	}
143
144	c->panel = new_panel(c->window);
145
146	if(NULL == c->panel) {
147	    delwin(c->window);
148	    free(c);
149	    return NULL;
150	}
151    } else {
152	c->window = NULL;
153	c->panel = NULL;
154    }
155
156    for(i = 0; i < GLOBALSTAT_TOTAL; ++i) {
157	if(GLOBALSTAT_SHAREDLIBS == i && !top_prefs_get_frameworks()) {
158	    /* Skip the framework update. */
159	    continue;
160	}
161
162	if(init_globalstat(c->window, c->stats + i, i)) {
163	    --i;
164	    /* Cleanup the already created windows. */
165	    for(; i >= 0; --i) {
166		if(!top_prefs_get_logging_mode())
167		    delwin(c->stats[i].window);
168	    }
169	    free(c);
170	    return NULL;
171	}
172    }
173
174    return c;
175}
176
177void top_globalstats_draw(void *ptr) {
178    struct globalstat_controller *c = ptr;
179    int i;
180
181    for(i = 0; i < GLOBALSTAT_TOTAL; ++i) {
182		// MWW: Skip the frameworks window if we've got them disabled
183		if (!top_prefs_get_frameworks() && (i == GLOBALSTAT_SHAREDLIBS))
184			continue;
185		if(strlen(c->stats[i].data))
186	    	mvwaddstr(c->stats[i].window, 0, 0, c->stats[i].data);
187    }
188}
189
190static void reset_globalstat(struct globalstat *gs) {
191    gs->data[0] = '\0';
192    gs->length = 0;
193}
194
195
196static void update_time(struct globalstat *gs,
197			const libtop_tsamp_t *tsamp) {
198    struct timeval tval;
199    struct tm tm;
200
201    gettimeofday(&tval, NULL);
202    localtime_r(&(tval.tv_sec), &tm);
203
204    if(STATMODE_ACCUM == top_prefs_get_mode()) {
205	unsigned int sec, min, hour;
206
207	timersub(&tsamp->time, &tsamp->b_time, &tval);
208
209	sec = tval.tv_sec;
210	min = sec / 60;
211	hour = min / 60;
212
213	gs->length = snprintf(gs->data, sizeof(gs->data), "%u:%02u:%02u",
214			      hour, min % 60, sec % 60);
215
216	if(gs->length < 0) {
217	    reset_globalstat(gs);
218	    return;
219	}
220    } else if(top_prefs_get_logging_mode()) {
221	gs->length = strftime(gs->data, sizeof(gs->data), "%Y/%m/%d %T", &tm);
222    } else {
223	gs->length = strftime(gs->data, sizeof(gs->data), "%T", &tm);
224    }
225
226    if(0 == gs->length) {
227	reset_globalstat(gs);
228	return;
229    }
230
231    update_max(gs);
232}
233
234
235static void update_processes(struct globalstat *gs,
236			     const libtop_tsamp_t *tsamp) {
237    int i, offset, chunksize;
238
239    gs->length = snprintf(gs->data, sizeof(gs->data),
240			  "Processes: %" PRIu32 " total, ",
241			  (uint32_t)tsamp->nprocs);
242
243    if(gs->length <= 0) {
244	reset_globalstat(gs);
245	return;
246    }
247
248    offset = gs->length;
249
250    for (i = 0; i < LIBTOP_NSTATES; ++i) {
251	if(tsamp->state_breakdown[i]) {
252	    chunksize = snprintf(gs->data + offset,
253				 sizeof(gs->data) - offset,
254				 "%d %s, ",
255				 tsamp->state_breakdown[i],
256				 libtop_state_str(i));
257	    if(chunksize < 0)
258		break;
259
260	    offset += chunksize;
261	}
262    }
263
264    gs->length = offset;
265
266    chunksize = snprintf(gs->data + gs->length,
267			 sizeof(gs->data) - gs->length,
268			 "%" PRIu32 " threads ", tsamp->threads);
269
270    if(chunksize < 0) {
271	reset_globalstat(gs);
272	return;
273    }
274
275    gs->length += chunksize;
276    assert(gs->length <= sizeof(gs->data));
277
278    update_max(gs);
279}
280
281static void update_load(struct globalstat *gs, const libtop_tsamp_t *tsamp) {
282    gs->length = snprintf(gs->data, sizeof(gs->data),
283			  "Load Avg: %.2f, %.2f, %.2f ",
284			  tsamp->loadavg[0], tsamp->loadavg[1],
285			  tsamp->loadavg[2]);
286
287    if(gs->length < 0) {
288	reset_globalstat(gs);
289	return;
290    }
291
292    update_max(gs);
293}
294
295static void cpu_percent(unsigned long long ticks, unsigned long long totalticks,
296			unsigned long long *whole, unsigned long long *part) {
297    *whole = 100ULL * ticks / totalticks;
298    *part = (((100ULL * ticks) - (*whole * totalticks)) * 100ULL)
299	/ totalticks;
300}
301
302static void update_cpu(struct globalstat *gs, const libtop_tsamp_t *tsamp) {
303    unsigned long long userticks, systicks, idleticks, totalticks;
304    int mode;
305    unsigned long long userwhole, userpart, syswhole, syspart,
306	idlewhole, idlepart;
307
308    userticks = tsamp->cpu.cpu_ticks[CPU_STATE_USER]
309	+ tsamp->cpu.cpu_ticks[CPU_STATE_NICE];
310    systicks = tsamp->cpu.cpu_ticks[CPU_STATE_SYSTEM];
311    idleticks = tsamp->cpu.cpu_ticks[CPU_STATE_IDLE];
312
313    mode = top_prefs_get_mode();
314
315    if(STATMODE_ACCUM == mode) {
316	userticks -= (tsamp->b_cpu.cpu_ticks[CPU_STATE_USER] +
317		      tsamp->b_cpu.cpu_ticks[CPU_STATE_NICE]);
318
319	systicks -= tsamp->b_cpu.cpu_ticks[CPU_STATE_SYSTEM];
320	idleticks -= tsamp->b_cpu.cpu_ticks[CPU_STATE_IDLE];
321    }
322
323    if(STATMODE_DELTA == mode || STATMODE_NON_EVENT == mode) {
324	userticks -= (tsamp->p_cpu.cpu_ticks[CPU_STATE_USER] +
325		      tsamp->p_cpu.cpu_ticks[CPU_STATE_NICE]);
326
327	systicks -= tsamp->p_cpu.cpu_ticks[CPU_STATE_SYSTEM];
328	idleticks -= tsamp->p_cpu.cpu_ticks[CPU_STATE_IDLE];
329    }
330
331    totalticks = userticks + systicks + idleticks;
332
333    if(0 == totalticks)
334	return;
335
336
337    cpu_percent(userticks, totalticks, &userwhole, &userpart);
338    cpu_percent(systicks, totalticks, &syswhole, &syspart);
339    cpu_percent(idleticks, totalticks, &idlewhole, &idlepart);
340
341    gs->length = snprintf(gs->data, sizeof(gs->data),
342			  "CPU usage: "
343			  "%" PRIu64 "." "%" PRIu64 "%% user, "
344			  "%" PRIu64 "." "%" PRIu64 "%% sys, "
345			  "%" PRIu64 "." "%" PRIu64 "%% idle ",
346			  userwhole, userpart,
347			  syswhole, syspart,
348			  idlewhole, idlepart);
349
350    if(gs->length < 0) {
351	reset_globalstat(gs);
352	return;
353    }
354
355    update_max(gs);
356}
357
358
359static void update_sharedlibs(struct globalstat *gs,
360			      const libtop_tsamp_t *tsamp) {
361    char code[6];
362    char data[6];
363    char linkedit[6];
364
365    if((-1 == humanize_number(code, sizeof(code), tsamp->fw_code, "",
366			      HN_AUTOSCALE, GLOBALSTAT_HN_FORMAT))
367       || (-1 == humanize_number(data, sizeof(data), tsamp->fw_data, "",
368				 HN_AUTOSCALE, GLOBALSTAT_HN_FORMAT))
369       || (-1 == humanize_number(linkedit, sizeof(linkedit),
370				 tsamp->fw_linkedit, "", HN_AUTOSCALE,
371				 GLOBALSTAT_HN_FORMAT))) {
372
373	reset_globalstat(gs);
374	return;
375    }
376
377    gs->length = snprintf(gs->data, sizeof(gs->data),
378			  "SharedLibs: "
379			  "%s resident, "
380			  "%s data, "
381			  "%s linkedit.",
382			  code,
383			  data,
384			  linkedit);
385
386    if(gs->length < 0) {
387	reset_globalstat(gs);
388	return;
389    }
390
391    update_max(gs);
392}
393
394static void update_memregions(struct globalstat *gs,
395			      const libtop_tsamp_t *tsamp) {
396    char resident[6];
397    char priv[6];
398    char shared[6];
399
400
401
402
403    if((-1 == humanize_globalstat(resident, sizeof(resident), tsamp->rprvt))
404       || (-1 == humanize_globalstat(priv, sizeof(priv), tsamp->fw_private))
405       || (-1 == humanize_globalstat(shared, sizeof(shared), tsamp->rshrd))) {
406	reset_globalstat(gs);
407	return;
408    }
409
410    gs->length = snprintf(gs->data, sizeof(gs->data),
411			  "MemRegions: %" PRIu32 " total, "
412			  "%s resident, "
413			  "%s private, "
414			  "%s shared.",
415			  tsamp->reg,
416			  resident,
417			  priv,
418			  shared);
419    if(gs->length < 0) {
420	reset_globalstat(gs);
421	return;
422    }
423
424    update_max(gs);
425}
426
427static void update_physmem(struct globalstat *gs,
428			   const libtop_tsamp_t *tsamp) {
429    char wired[6];
430    char used[6];
431    char physfree[6];
432    uint64_t total_free, total_used, total_used_count;
433    struct top_uinteger wiredresult, usedresult,
434	physfreeresult;
435
436    total_free = (uint64_t)tsamp->vm_stat.free_count * tsamp->pagesize;
437    total_used_count = (uint64_t)tsamp->vm_stat.wire_count + tsamp->vm_stat.inactive_count
438			+ tsamp->vm_stat.active_count + tsamp->vm_stat.compressor_page_count;
439    total_used = total_used_count * tsamp->pagesize;
440
441    wiredresult = top_init_uinteger(tsamp->vm_stat.wire_count
442				    * tsamp->pagesize, false);
443    usedresult = top_init_uinteger(total_used, false);
444    physfreeresult = top_init_uinteger(total_free, false);
445
446    if(top_humanize_uinteger(wired, sizeof(wired), wiredresult)
447       || top_humanize_uinteger(used, sizeof(used), usedresult)
448       || top_humanize_uinteger(physfree, sizeof(physfree), physfreeresult)) {
449	fprintf(stderr, "top_humanize_uinteger failure in %s\n", __func__);
450	reset_globalstat(gs);
451	return;
452    }
453
454    gs->length = snprintf(gs->data, sizeof(gs->data),
455			  "PhysMem: "
456			  "%s used (%s wired), "
457			  "%s unused.",
458			  used, wired,
459			  physfree);
460
461    if(gs->length < 0) {
462	reset_globalstat(gs);
463	return;
464    }
465
466    update_max(gs);
467}
468
469static void update_vm(struct globalstat *gs,
470		      const libtop_tsamp_t *tsamp) {
471    char vsize[6];
472    char fwvsize[6];
473    struct top_uinteger vsize_result, fw_vsize_result;
474
475    vsize_result = top_init_uinteger(tsamp->vsize, false);
476    fw_vsize_result = top_init_uinteger(tsamp->fw_vsize, false);
477
478    if(top_humanize_uinteger(vsize, sizeof(vsize), vsize_result))
479	return;
480
481    if(top_humanize_uinteger(fwvsize, sizeof(fwvsize), fw_vsize_result))
482	return;
483
484    gs->length = snprintf(gs->data, sizeof(gs->data),
485			  "VM: "
486			  "%s vsize, "
487			  "%s framework vsize, "
488			  "%" PRIu64 "(%" PRIu64 ") swapins, "
489			  "%" PRIu64 "(%" PRIu64 ") swapouts.",
490			  vsize,
491			  fwvsize,
492			  (unsigned long long)tsamp->vm_stat.swapins,
493			  (unsigned long long)(tsamp->vm_stat.swapins
494					       - tsamp->p_vm_stat.swapins),
495			  (unsigned long long)tsamp->vm_stat.swapouts,
496			  (unsigned long long)(tsamp->vm_stat.swapouts
497					       - tsamp->p_vm_stat.swapouts));
498
499    if(gs->length < 0) {
500	reset_globalstat(gs);
501	return;
502    }
503
504    update_max(gs);
505}
506
507static void update_swap(struct globalstat *gs,
508			const libtop_tsamp_t *tsamp) {
509    struct top_uinteger used_result, available_result;
510    char used[6];
511    char avail[6];
512
513    if(!tsamp->xsu_is_valid) {
514	gs->length = snprintf(gs->data, sizeof(gs->data),
515			      "Swap: N/A");
516
517	if(gs->length < 0)
518	    reset_globalstat(gs);
519
520	return;
521    }
522
523    /*FIXME a p_xsu added to libtop for delta mode would be nice.*/
524    used_result = top_init_uinteger(tsamp->xsu.xsu_used, false);
525    available_result = top_init_uinteger(tsamp->xsu.xsu_avail, false);
526
527    if(top_humanize_uinteger(used, sizeof(used), used_result)
528       || top_humanize_uinteger(avail, sizeof(avail), available_result)) {
529	reset_globalstat(gs);
530	return;
531    }
532
533    gs->length = snprintf(gs->data, sizeof(gs->data),
534			  "Swap: %s + %s free.", used, avail);
535
536    if(gs->length < 0)
537	reset_globalstat(gs);
538
539    update_max(gs);
540}
541
542static void update_purgeable(struct globalstat *gs,
543			const libtop_tsamp_t *tsamp) {
544    struct top_uinteger purgeable_result, purges_result, prev_purges,
545	purges_delta;
546    char purgeable[6];
547    char purges[6];
548    char pdelta[6];
549    uint64_t purgeable_mem;
550
551    if(!tsamp->purgeable_is_valid) {
552	gs->length = snprintf(gs->data, sizeof(gs->data),
553			      "Purgeable: N/A");
554
555	if(gs->length < 0)
556	    reset_globalstat(gs);
557
558	return;
559    }
560
561    purgeable_mem = tsamp->vm_stat.purgeable_count * tsamp->pagesize;
562
563    purgeable_result = top_init_uinteger(purgeable_mem, false);
564    purges_result = top_init_uinteger(tsamp->vm_stat.purges, false);
565    prev_purges = top_init_uinteger(tsamp->p_vm_stat.purges, false);
566    purges_delta = top_sub_uinteger(&purges_result, &prev_purges);
567
568    if(top_humanize_uinteger(purgeable, sizeof(purgeable), purgeable_result)
569       || top_sprint_uinteger(purges, sizeof(purges), purges_result)
570       || top_sprint_uinteger(pdelta, sizeof(pdelta), purges_delta)) {
571	reset_globalstat(gs);
572	return;
573    }
574
575    gs->length = snprintf(gs->data, sizeof(gs->data),
576			  "Purgeable: %s %s(%s) pages purged.",
577			  purgeable, purges,
578			  pdelta);
579
580    if(gs->length < 0)
581	reset_globalstat(gs);
582
583    update_max(gs);
584}
585
586/* Return true if an error occurred. */
587static bool update_networks(struct globalstat *gs,
588			    const libtop_tsamp_t *tsamp) {
589    struct top_uinteger inp, outp, inbytes, outbytes;
590    char inpbuf[GENERIC_INT_SIZE];
591    char outpbuf[GENERIC_INT_SIZE];
592    char inbytesbuf[6];
593    char outbytesbuf[6];
594
595    inp = top_init_uinteger(tsamp->net_ipackets, false);
596    outp = top_init_uinteger(tsamp->net_opackets, false);
597
598    inbytes = top_init_uinteger(tsamp->net_ibytes, false);
599    outbytes = top_init_uinteger(tsamp->net_obytes, false);
600
601    switch(top_prefs_get_mode()) {
602    case STATMODE_ACCUM: {
603	/* The changes that occurred since the beginning sample. */
604	struct top_uinteger binp, boutp, binbytes, boutbytes;
605
606	binp = top_init_uinteger(tsamp->b_net_ipackets, false);
607	boutp = top_init_uinteger(tsamp->b_net_opackets, false);
608	binbytes = top_init_uinteger(tsamp->b_net_ibytes, false);
609	boutbytes = top_init_uinteger(tsamp->b_net_obytes, false);
610
611	inp = top_sub_uinteger(&inp, &binp);
612	outp = top_sub_uinteger(&outp, &boutp);
613
614	inbytes = top_sub_uinteger(&inbytes, &binbytes);
615	outbytes = top_sub_uinteger(&outbytes, &boutbytes);
616    }
617	break;
618
619    case STATMODE_DELTA: {
620	/* The changes that occurred since the previous sample. */
621	struct top_uinteger pinp, poutp, pinbytes, poutbytes;
622
623	pinp = top_init_uinteger(tsamp->p_net_ipackets, false);
624	poutp = top_init_uinteger(tsamp->p_net_opackets, false);
625	pinbytes = top_init_uinteger(tsamp->p_net_ibytes, false);
626	poutbytes = top_init_uinteger(tsamp->p_net_obytes, false);
627
628	inp = top_sub_uinteger(&inp, &pinp);
629	outp = top_sub_uinteger(&outp, &poutp);
630
631	inbytes = top_sub_uinteger(&inbytes, &pinbytes);
632	outbytes = top_sub_uinteger(&outbytes, &poutbytes);
633    }
634	break;
635    }
636
637    if(top_sprint_uinteger(inpbuf, sizeof(inpbuf), inp))
638	return true;
639
640    if(top_sprint_uinteger(outpbuf, sizeof(outpbuf), outp))
641	return true;
642
643    if(top_humanize_uinteger(inbytesbuf, sizeof(inbytesbuf), inbytes))
644	return true;
645
646    if(top_humanize_uinteger(outbytesbuf, sizeof(outbytesbuf), outbytes))
647	return true;
648
649    gs->length = snprintf(gs->data, sizeof(gs->data),
650			  "Networks: "
651			  "packets: "
652			  "%s/%s in, "
653			  "%s/%s out.",
654			  inpbuf, inbytesbuf,
655			  outpbuf, outbytesbuf);
656    if(-1 == gs->length) {
657	reset_globalstat(gs);
658	return true;
659    }
660
661    update_max(gs);
662
663    return false;
664}
665
666/* Return true if an error ocurred. */
667static bool update_disks(struct globalstat *gs, const libtop_tsamp_t *tsamp) {
668    char rbuf[GENERIC_INT_SIZE];
669    char wbuf[GENERIC_INT_SIZE];
670    char rsizebuf[6];
671    char wsizebuf[6];
672    struct top_uinteger rops, wops, rbytes, wbytes;
673
674    rops = top_init_uinteger(tsamp->disk_rops, false);
675    wops = top_init_uinteger(tsamp->disk_wops, false);
676    rbytes = top_init_uinteger(tsamp->disk_rbytes, false);
677    wbytes = top_init_uinteger(tsamp->disk_wbytes, false);
678
679    switch(top_prefs_get_mode()) {
680    case STATMODE_ACCUM: {
681	struct top_uinteger brops, bwops, brbytes, bwbytes;
682
683	brops = top_init_uinteger(tsamp->b_disk_rops, false);
684	bwops = top_init_uinteger(tsamp->b_disk_wops, false);
685	brbytes = top_init_uinteger(tsamp->b_disk_rbytes, false);
686	bwbytes = top_init_uinteger(tsamp->b_disk_wbytes, false);
687
688	rops = top_sub_uinteger(&rops, &brops);
689	wops = top_sub_uinteger(&wops, &bwops);
690	rbytes = top_sub_uinteger(&rbytes, &brbytes);
691	wbytes = top_sub_uinteger(&wbytes, &bwbytes);
692    }
693	break;
694
695    case STATMODE_DELTA: {
696	struct top_uinteger props, pwops, prbytes, pwbytes;
697
698	props = top_init_uinteger(tsamp->p_disk_rops, false);
699	pwops = top_init_uinteger(tsamp->p_disk_wops, false);
700	prbytes = top_init_uinteger(tsamp->p_disk_rbytes, false);
701	pwbytes = top_init_uinteger(tsamp->p_disk_wbytes, false);
702
703	rops = top_sub_uinteger(&rops, &props);
704	wops = top_sub_uinteger(&wops, &pwops);
705	rbytes = top_sub_uinteger(&rbytes, &prbytes);
706	wbytes = top_sub_uinteger(&wbytes, &pwbytes);
707    }
708	break;
709    }
710
711    if(top_sprint_uinteger(rbuf, sizeof(rbuf), rops))
712	return true;
713
714    if(top_sprint_uinteger(wbuf, sizeof(wbuf), wops))
715	return true;
716
717    if(top_humanize_uinteger(rsizebuf, sizeof(rsizebuf), rbytes))
718	return true;
719
720    if(top_humanize_uinteger(wsizebuf, sizeof(wsizebuf), wbytes))
721	return true;
722
723    gs->length = snprintf(gs->data, sizeof(gs->data),
724			  "Disks: "
725			  "%s/%s read, "
726			  "%s/%s written.",
727			  rbuf, rsizebuf,
728			  wbuf, wsizebuf);
729
730    if(-1 == gs->length) {
731	reset_globalstat(gs);
732	return true;
733    }
734
735    update_max(gs);
736
737    return false;
738}
739
740bool top_globalstats_update(void *ptr, const void *sample) {
741    const libtop_tsamp_t *tsamp = sample;
742    struct globalstat_controller *c = ptr;
743    int i;
744
745	if (!top_prefs_get_logging_mode()) {
746		for(i = 0; i < GLOBALSTAT_TOTAL; ++i) {
747			// MWW: If we've disabled framework logging one of these windows isn't valid and needs skipping
748			if (!top_prefs_get_frameworks() && (i == GLOBALSTAT_SHAREDLIBS)) continue;
749			werase(c->stats[i].window);
750		}
751	}
752
753    update_processes(c->stats + GLOBALSTAT_PROCESSES, tsamp);
754    update_load(c->stats + GLOBALSTAT_LOAD, tsamp);
755    update_cpu(c->stats + GLOBALSTAT_CPU, tsamp);
756
757    if(top_prefs_get_frameworks())
758	update_sharedlibs(c->stats + GLOBALSTAT_SHAREDLIBS, tsamp);
759
760    update_memregions(c->stats + GLOBALSTAT_MEMREGIONS, tsamp);
761    update_physmem(c->stats + GLOBALSTAT_PHYSMEM, tsamp);
762    update_vm(c->stats + GLOBALSTAT_VM, tsamp);
763    update_time(c->stats + GLOBALSTAT_TIME, tsamp);
764
765    if(top_prefs_get_swap()) {
766	update_swap(c->stats + GLOBALSTAT_SWAP, tsamp);
767	update_purgeable(c->stats + GLOBALSTAT_PURGEABLE, tsamp);
768    }
769
770    if(update_networks(c->stats + GLOBALSTAT_NETWORKS, tsamp))
771	top_log("An error occurred while updating the network global stats.\n");
772
773    if(update_disks(c->stats + GLOBALSTAT_DISKS, tsamp))
774	top_log("An erorr occurred while updating the disk global stats.\n");
775
776    return false;
777}
778
779static bool skip_globalstat(int i) {
780    if((GLOBALSTAT_SHAREDLIBS == i && !top_prefs_get_frameworks())
781       || (GLOBALSTAT_SWAP == i && !top_prefs_get_swap())
782       || (GLOBALSTAT_PURGEABLE == i && !top_prefs_get_swap())) {
783	return true;
784    }
785
786    return false;
787}
788
789static void setup_globalstats_geometry(struct globalstat_controller *c,
790				  int availwidth, int availheight,
791				  int *outheight) {
792    struct globalstat *gs;
793    int i;
794    int remaining = availwidth;
795    int gswidth;
796    int x = 0;
797    int y = 0;
798
799    *outheight = 0;
800
801    /* First place the Processes in the upper left. */
802    gs = c->stats + GLOBALSTAT_PROCESSES;
803
804    gswidth = get_globalstat_reqwidth(gs);
805
806    if(gswidth > availwidth)
807	gswidth = availwidth;
808
809    set_globalstat_geometry(gs, x, y, gswidth, /*height*/ 1);
810
811    remaining -= gswidth;
812    x += gswidth;
813
814    /*
815     * Place the time in the upper right, or if it doesn't fit,
816     * down 1 column, on the left.
817     */
818    gs = c->stats + GLOBALSTAT_TIME;
819
820    gswidth = get_globalstat_reqwidth(gs);
821
822    if(gswidth > availwidth)
823	gswidth = availwidth;
824
825    if(remaining >= gswidth) {
826	/* There is enough room on the first row for the time. */
827	set_globalstat_geometry(gs, availwidth - gswidth, y,
828				gswidth, /*height*/ 1);
829
830	/* Go down a row. */
831	++y;
832	/* Reset the x to the left most. */
833	x = 0;
834	remaining = availwidth;
835    }  else {
836	/* Move the time down a row. */
837	++y;
838	x = 0;
839	remaining = availwidth;
840
841	set_globalstat_geometry(gs, x, y, gswidth,
842				/*height*/ 1);
843	x += gswidth;
844       	remaining -= gswidth;
845    }
846
847    for(i = GLOBALSTAT_LOAD; i < GLOBALSTAT_TOTAL; ++i) {
848	if(skip_globalstat(i)) {
849	    /* Skip the updates for these. */
850	    continue;
851	}
852
853	gs = c->stats + i;
854
855	gswidth = get_globalstat_reqwidth(gs);
856
857	if(gswidth > availwidth)
858	    gswidth = availwidth;
859
860	/* Is the space remaining less than the total globalstat width? */
861	if(remaining < gswidth) {
862	    /* Reset the remaining width. */
863	    ++y;
864	    x = 0;
865	    remaining = availwidth;
866	}
867
868	set_globalstat_geometry(gs, x, y, gswidth, /*height*/ 1);
869	remaining -= gswidth;
870	x += gswidth;
871    }
872
873    if(remaining != availwidth)
874	++y;
875
876    *outheight = y;
877}
878
879bool top_globalstats_resize(void *ptr, int width, int height,
880			    int *consumed_height) {
881    struct globalstat_controller *c = ptr;
882    int i;
883
884    top_log("%s\n", __func__);
885
886    *consumed_height = 0;
887
888    setup_globalstats_geometry(c, width, height, consumed_height);
889
890    if(ERR == wresize(c->window, *consumed_height, width)) {
891	top_log("wresize error: height %d width %d\n",
892		*consumed_height, width);
893	return true;
894    }
895
896    if(ERR == move_panel(c->panel, 0, 0)) {
897	top_log("move_panel error: 0 0\n");
898	return true;
899    }
900
901    for(i = 0; i < GLOBALSTAT_TOTAL; ++i) {
902	struct globalstat *gs = c->stats + i;
903
904	// MWW: Skip the frameworks window if we've got them disabled
905	if (!top_prefs_get_frameworks() && (i == GLOBALSTAT_SHAREDLIBS)) continue;
906
907	if(skip_globalstat(i)) {
908	    /* Skip and hide the panels for these. */
909	    hide_panel(gs->panel);
910	    continue;
911	}
912
913	gs = c->stats + i;
914
915	/*
916	 * Avoid errors below by resizing and moving the panel to a known
917	 * good size/offfset.
918	 */
919	wresize(gs->window, 1, 1);
920	move_panel(gs->panel, 0, 0);
921
922	if(ERR == move_panel(gs->panel, gs->y, gs->x))
923	    return true;
924
925	if(ERR == wresize(gs->window, gs->size.height, gs->size.width))
926	    return true;
927
928	if(ERR == werase(gs->window))
929	    return true;
930    }
931
932    return false;
933}
934
935
936void top_globalstats_iterate(void *ptr, bool (*iter)(char *, void *),
937			     void *dataptr) {
938    struct globalstat_controller *c = ptr;
939    int i;
940
941    for(i = 0; i < GLOBALSTAT_TOTAL; ++i) {
942	if(skip_globalstat(i))
943	    continue;
944
945	if(c->stats[i].length > 0) {
946	    if(!iter(c->stats[i].data, dataptr))
947		break;
948	}
949    }
950}
951
952void top_globalstats_reset(void *ptr) {
953    struct globalstat_controller *c = ptr;
954    int i;
955
956    for(i = 0; i < GLOBALSTAT_TOTAL; ++i) {
957	c->stats[i].max_width = c->stats[i].length;
958    }
959}
960