1 /******************************************************************************
2  *
3  *   Copyright © International Business Machines  Corp., 2006, 2008
4  *
5  *   This program is free software;  you can redistribute it and/or modify
6  *   it under the terms of the GNU General Public License as published by
7  *   the Free Software Foundation; either version 2 of the License, or
8  *   (at your option) any later version.
9  *
10  *   This program is distributed in the hope that it will be useful,
11  *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
13  *   the GNU General Public License for more details.
14  *
15  *   You should have received a copy of the GNU General Public License
16  *   along with this program;  if not, write to the Free Software
17  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18  *
19  * NAME
20  *	   libstats.c
21  *
22  * DESCRIPTION
23  *	  Some basic statistical analysis convenience tools.
24  *
25  *
26  * USAGE:
27  *	  To be included in test cases
28  *
29  * AUTHOR
30  *		Darren Hart <dvhltc@us.ibm.com>
31  *
32  * HISTORY
33  *	  2006-Oct-17: Initial version by Darren Hart
34  *	  2009-Jul-22: Addition of stats_container_append function by Kiran Prakash
35  *
36  * TODO: the save routine for gnuplot plotting should be more modular...
37  *
38  *****************************************************************************/
39 
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <errno.h>
44 #include <unistd.h>
45 #include <math.h>
46 #include <libstats.h>
47 #include <librttest.h>
48 
49 #include "../include/realtime_config.h"
50 
51 #ifndef HAVE_EXP10
52 # define exp10(x) (exp((x) * log(10)))
53 #endif
54 
55 int save_stats = 0;
56 
57 /* static helper functions */
stats_record_compare(const void * a,const void * b)58 static int stats_record_compare(const void *a, const void *b)
59 {
60 	int ret = 0;
61 	stats_record_t *rec_a = (stats_record_t *) a;
62 	stats_record_t *rec_b = (stats_record_t *) b;
63 	if (rec_a->y < rec_b->y)
64 		ret = -1;
65 	else if (rec_a->y > rec_b->y)
66 		ret = 1;
67 	return ret;
68 }
69 
70 /* function implementations */
stats_container_init(stats_container_t * data,long size)71 int stats_container_init(stats_container_t * data, long size)
72 {
73 	data->size = size;
74 	data->index = -1;
75 	data->records = calloc(size, sizeof(stats_record_t));
76 	if (!data->records)
77 		return -1;
78 	return 0;
79 }
80 
stats_container_append(stats_container_t * data,stats_record_t rec)81 int stats_container_append(stats_container_t * data, stats_record_t rec)
82 {
83 	int myindex = ++data->index;
84 	if (myindex >= data->size) {
85 		debug(DBG_ERR, "Number of elements cannot be more than %ld\n",
86 		      data->size);
87 		data->index--;
88 		return -1;
89 	}
90 	data->records[myindex] = rec;
91 	return myindex;
92 }
93 
stats_container_resize(stats_container_t * data,long size)94 int stats_container_resize(stats_container_t * data, long size)
95 {
96 	stats_record_t *newrecords =
97 	    realloc(data->records, size * sizeof(stats_record_t));
98 	if (!newrecords)
99 		return -1;
100 	data->records = newrecords;
101 	if (data->size < size)
102 		memset(data->records + data->size, 0, size - data->size);
103 	data->size = size;
104 	return 0;
105 }
106 
stats_container_free(stats_container_t * data)107 int stats_container_free(stats_container_t * data)
108 {
109 	free(data->records);
110 	return 0;
111 }
112 
stats_sort(stats_container_t * data,enum stats_sort_method method)113 int stats_sort(stats_container_t * data, enum stats_sort_method method)
114 {
115 	/* method not implemented, always ascending on y atm */
116 	qsort(data->records, data->index + 1, sizeof(stats_record_t),
117 	      stats_record_compare);
118 	return 0;
119 }
120 
stats_stddev(stats_container_t * data)121 float stats_stddev(stats_container_t * data)
122 {
123 	long i;
124 	float sd, avg, sum, delta;
125 	long n;
126 
127 	sd = 0.0;
128 	n = data->index + 1;
129 	sum = 0.0;
130 
131 	/* calculate the mean */
132 	for (i = 0; i < n; i++) {
133 		sum += data->records[i].y;
134 	}
135 	avg = sum / (float)n;
136 
137 	/* calculate the standard deviation */
138 	sum = 0.0;
139 	for (i = 0; i < n; i++) {
140 		delta = (data->records[i].y - avg);
141 		sum += delta * delta;
142 	}
143 	sum /= n;
144 
145 	sd = sqrt(sum);
146 
147 	return sd;
148 }
149 
stats_avg(stats_container_t * data)150 float stats_avg(stats_container_t * data)
151 {
152 	long i;
153 	float avg, sum;
154 	long n;
155 
156 	n = data->index + 1;
157 	sum = 0.0;
158 
159 	/* calculate the mean */
160 	for (i = 0; i < n; i++) {
161 		sum += data->records[i].y;
162 	}
163 	avg = sum / (float)n;
164 
165 	return avg;
166 }
167 
stats_min(stats_container_t * data)168 long stats_min(stats_container_t * data)
169 {
170 	long i;
171 	long min;
172 	long n;
173 
174 	n = data->index + 1;
175 
176 	/* calculate the mean */
177 	min = data->records[0].y;
178 	for (i = 1; i < n; i++) {
179 		if (data->records[i].y < min) {
180 			min = data->records[i].y;
181 		}
182 	}
183 
184 	return min;
185 }
186 
stats_max(stats_container_t * data)187 long stats_max(stats_container_t * data)
188 {
189 	long i;
190 	long max;
191 	long n;
192 
193 	n = data->index + 1;
194 
195 	/* calculate the mean */
196 	max = data->records[0].y;
197 	for (i = 1; i < n; i++) {
198 		if (data->records[i].y > max) {
199 			max = data->records[i].y;
200 		}
201 	}
202 
203 	return max;
204 }
205 
stats_quantiles_init(stats_quantiles_t * quantiles,int nines)206 int stats_quantiles_init(stats_quantiles_t * quantiles, int nines)
207 {
208 	if (nines < 2) {
209 		return -1;
210 	}
211 	quantiles->nines = nines;
212 	/* allocate space for quantiles, starting with 0.99 (two nines) */
213 	quantiles->quantiles = calloc(sizeof(long), (nines - 1));
214 	if (!quantiles->quantiles) {
215 		return -1;
216 	}
217 	return 0;
218 }
219 
stats_quantiles_free(stats_quantiles_t * quantiles)220 int stats_quantiles_free(stats_quantiles_t * quantiles)
221 {
222 	free(quantiles->quantiles);
223 	return 0;
224 }
225 
stats_quantiles_calc(stats_container_t * data,stats_quantiles_t * quantiles)226 int stats_quantiles_calc(stats_container_t * data,
227 			 stats_quantiles_t * quantiles)
228 {
229 	int i;
230 	int size;
231 	int index;
232 
233 	// check for sufficient data size of accurate calculation
234 	if (data->index < 0 ||
235 	    (data->index + 1) < (long)exp10(quantiles->nines)) {
236 		return -1;
237 	}
238 
239 	size = data->index + 1;
240 	stats_sort(data, ASCENDING_ON_Y);
241 
242 	for (i = 2; i <= quantiles->nines; i++) {
243 		index = size - size / exp10(i);
244 		quantiles->quantiles[i - 2] = data->records[index].y;
245 	}
246 	return 0;
247 }
248 
stats_quantiles_print(stats_quantiles_t * quantiles)249 void stats_quantiles_print(stats_quantiles_t * quantiles)
250 {
251 	int i;
252 	int fraction = 0;
253 	for (i = 0; i <= quantiles->nines - 2; i++) {
254 		if (i > 0)
255 			fraction += 9 * exp10(i - 1);
256 		printf("99.%d%% < %ld\n", fraction, quantiles->quantiles[i]);
257 	}
258 }
259 
stats_hist(stats_container_t * hist,stats_container_t * data)260 int stats_hist(stats_container_t * hist, stats_container_t * data)
261 {
262 	int i;
263 	int ret;
264 	long min, max, width;
265 	long y, b;
266 
267 	ret = 0;
268 
269 	if (hist->size <= 0 || data->index < 0) {
270 		return -1;
271 	}
272 
273 	/* calculate the range of dataset */
274 	min = max = data->records[0].y;
275 	for (i = 0; i <= data->index; i++) {
276 		y = data->records[i].y;
277 		if (y > max)
278 			max = y;
279 		if (y < min)
280 			min = y;
281 	}
282 
283 	/* define the bucket ranges */
284 	width = MAX((max - min) / hist->size, 1);
285 	hist->records[0].x = min;
286 	for (i = 1; i < (hist->size); i++) {
287 		hist->records[i].x = min + i * width;
288 	}
289 
290 	/* fill in the counts */
291 	for (i = 0; i <= data->index; i++) {
292 		y = data->records[i].y;
293 		b = MIN((y - min) / width, hist->size - 1);
294 		hist->records[b].y++;
295 	}
296 
297 	return 0;
298 }
299 
stats_hist_print(stats_container_t * hist)300 void stats_hist_print(stats_container_t * hist)
301 {
302 	long i, x;
303 	for (i = 0; i < hist->size; i++) {
304 		x = hist->records[i].x;
305 		if (i < hist->size - 1)
306 			printf("[%ld,%ld) = %ld\n", x,
307 			       hist->records[i + 1].x, hist->records[i].y);
308 		else
309 			printf("[%ld,-) = %ld\n", x, hist->records[i].y);
310 	}
311 }
312 
stats_container_save(char * filename,char * title,char * xlabel,char * ylabel,stats_container_t * data,char * mode)313 int stats_container_save(char *filename, char *title, char *xlabel,
314 			 char *ylabel, stats_container_t * data, char *mode)
315 {
316 	int i;
317 	int minx = 0, maxx = 0, miny = 0, maxy = 0;
318 	FILE *dat_fd;
319 	FILE *plt_fd;
320 	char *datfile;
321 	char *pltfile;
322 	stats_record_t *rec;
323 
324 	if (!save_stats)
325 		return 0;
326 
327 	/* generate the filenames */
328 	if (asprintf(&datfile, "%s.dat", filename) == -1) {
329 		fprintf(stderr,
330 			"Failed to allocate string for data filename\n");
331 		return -1;
332 	}
333 	if (asprintf(&pltfile, "%s.plt", filename) == -1) {
334 		fprintf(stderr,
335 			"Failed to allocate string for plot filename\n");
336 		return -1;
337 	}
338 
339 	/* generate the data file */
340 	if ((dat_fd = fopen(datfile, "w")) == NULL) {
341 		perror("Failed to open dat file");
342 		return -1;
343 	} else {
344 		minx = maxx = data->records[0].x;
345 		miny = maxy = data->records[0].y;
346 		for (i = 0; i <= data->index; i++) {
347 			rec = &data->records[i];
348 			minx = MIN(minx, rec->x);
349 			maxx = MAX(maxx, rec->x);
350 			miny = MIN(miny, rec->y);
351 			maxy = MAX(maxy, rec->y);
352 			fprintf(dat_fd, "%ld %ld\n", rec->x, rec->y);
353 		}
354 		fclose(dat_fd);
355 	}
356 
357 	/* generate the plt file */
358 	if (!(plt_fd = fopen(pltfile, "w"))) {
359 		perror("Failed to open plt file");
360 		return -1;
361 	} else {
362 		fprintf(plt_fd, "set terminal png\n");
363 		fprintf(plt_fd, "set output \"%s.png\"\n", pltfile);
364 		fprintf(plt_fd, "set title \"%s\"\n", title);
365 		fprintf(plt_fd, "set xlabel \"%s\"\n", xlabel);
366 		fprintf(plt_fd, "set ylabel \"%s\"\n", ylabel);
367 		fprintf(plt_fd, "plot [0:%d] [0:%d] \"%s\" with %s\n",
368 			maxx + 1, maxy + 1, datfile, mode);
369 		fclose(plt_fd);
370 	}
371 
372 	return 0;
373 }
374