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 <unistd.h>
28
29#include "statistic.h"
30#include "generic.h"
31#include "preferences.h"
32#include "globalstats.h"
33#include "top.h"
34#include "sig.h"
35
36struct log_get_total_data {
37    int total;
38};
39
40static bool log_get_total_iter(struct statistic *s, void *ptr) {
41    struct log_get_total_data *data = ptr;
42    struct generic_cells *cells;
43
44    cells = s->cells;
45
46    if(NULL == cells)
47	return false;
48
49    data->total = cells->length;
50
51    return false;
52}
53
54/* Get the total number of processes this run. */
55static int log_get_total(void *tinst) {
56    struct statistics_controller *c = tinst;
57    struct log_get_total_data data;
58
59    data.total = 0;
60
61    c->iterate(c, log_get_total_iter, &data);
62
63    return data.total;
64}
65
66struct log_print_header_data {
67    int x;
68    int xoffset;
69    bool have_limit;
70    int ncols;
71    int column;
72};
73
74static bool log_print_header_iter(struct statistic *s, void *ptr) {
75    struct log_print_header_data *data = ptr;
76    int max_width, headerlen;
77    struct generic_cells *cells;
78    int i, afterx;
79
80    cells = s->cells;
81
82    if(NULL == cells)
83	return false;
84
85    max_width = cells->max_width;
86
87    headerlen = (int)strlen(s->header);
88
89    if(headerlen > max_width)
90	max_width = headerlen;
91
92    afterx = max_width + data->x;
93
94    for(i = 0; i < data->xoffset; ++i)
95	putchar(' ');
96
97    printf("%s", s->header);
98
99    for(i = headerlen; i < max_width; ++i)
100	putchar(' ');
101
102    data->x = afterx;
103    data->xoffset = /*padding*/ 1;
104
105    data->column++;
106
107    if(data->have_limit && data->column >= data->ncols)
108	return false; /*stop iterating*/
109
110    return true;
111}
112
113static void log_print_header(void *tinst) {
114    struct statistics_controller *c = tinst;
115    struct log_print_header_data data;
116
117    data.x = 0;
118    data.xoffset = 0;
119    data.have_limit = top_prefs_get_ncols(&data.ncols);
120    data.column = 0;
121
122    c->iterate(c, log_print_header_iter, &data);
123}
124
125struct log_print_cell_data {
126    int x;
127    int xoffset;
128    bool have_limit;
129    int ncols; /* The limit of the columns. */
130    int column; /* The current number of columns. */
131    int row;
132};
133
134static bool log_print_cell(struct statistic *s, void *ptr) {
135    struct log_print_cell_data *data = ptr;
136    struct generic_cells *cells;
137    size_t i;
138    int pad;
139    int afterx;
140    int max_width;
141    int headerlen;
142
143    cells = s->cells;
144
145    if(NULL == cells)
146	return false;
147
148    max_width = cells->max_width;
149
150    headerlen = (int)strlen(s->header);
151
152    if(headerlen > max_width)
153	max_width = headerlen;
154
155    afterx = data->x + max_width;
156
157    assert(data->row < cells->length);
158
159    /* Add padding to the left. */
160    for(pad = 0; pad < data->xoffset; ++pad) {
161	putchar(' ');
162    }
163
164    printf("%s", cells->array[data->row].string);
165
166    for(i = cells->array[data->row].length; i < (size_t)max_width; ++i) {
167	putchar(' ');
168    }
169
170    data->x = afterx;
171    data->xoffset = 1;
172
173    data->column++;
174
175    /* See if we have displayed all of the columns requested. */
176    if(data->have_limit && data->column >= data->ncols)
177	return false;
178
179    return true;
180}
181
182static bool log_print_globalstats(char *s, void *ptr) {
183    printf("%s\n", s);
184    return true;
185}
186
187static void log_print_all(void *tinst) {
188    struct statistics_controller *c = tinst;
189    struct log_print_cell_data data;
190    int y, ylimit;
191
192    top_globalstats_iterate(c->globalstats, log_print_globalstats, NULL);
193    putchar('\n');
194
195    log_print_header(tinst);
196    putchar('\n');
197
198    data.x = 0;
199    data.xoffset = /*padding*/ 0;
200    data.have_limit = top_prefs_get_ncols(&data.ncols);
201    data.column = 0;
202    data.row = 0;
203
204    ylimit = log_get_total(tinst);
205
206    for(y = 0; y < ylimit; ++y) {
207	data.x = 0;
208	data.xoffset = 0;
209	data.column = 0;
210	c->iterate(c, log_print_cell, &data);
211	data.row++;
212	putchar('\n');
213    }
214}
215
216void top_logging_loop_body(void *tinst) {
217    top_insert(tinst);
218    log_print_all(tinst);
219    fflush(stdout);
220    sleep(top_prefs_get_sleep());
221
222    if(top_signal_is_exit_set())
223	exit(EXIT_SUCCESS);
224}
225
226void top_logging_loop(void *tinst) {
227    int samples;
228    bool forever = false;
229
230    if(0 == top_prefs_get_samples())
231	forever = true;
232
233    if(forever) {
234	while(1)
235	    top_logging_loop_body(tinst);
236    } else {
237	for(samples = top_prefs_get_samples(); samples > 0; --samples)
238	    top_logging_loop_body(tinst);
239
240	/* We displayed the requested number of samples. */
241	exit(EXIT_SUCCESS);
242    }
243}
244