1/************************************************************************
2Copyright (c) 2015, The Linux Foundation. All rights reserved.
3
4Redistribution and use in source and binary forms, with or without
5modification, are permitted provided that the following conditions are
6met:
7    * Redistributions of source code must retain the above copyright
8      notice, this list of conditions and the following disclaimer.
9    * Redistributions in binary form must reproduce the above
10      copyright notice, this list of conditions and the following
11      disclaimer in the documentation and/or other materials provided
12      with the distribution.
13    * Neither the name of The Linux Foundation nor the names of its
14      contributors may be used to endorse or promote products derived
15      from this software without specific prior written permission.
16
17THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
18WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
20ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
21BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28************************************************************************/
29
30/**
31 * @file datatop_helpers.c
32 * @brief Contains functions which output data.
33 *
34 * Contains functions which are used for printing data to output streams.
35 * Handles all formatting for data output. Also contains functions which
36 * are responsible for data gathering and collection.
37 */
38
39#include <stdio.h>
40#include <stdlib.h>
41#include <stdint.h>
42#include <time.h>
43#include <sys/time.h>
44#include <string.h>
45#include <errno.h>
46#include "datatop_interface.h"
47#include "datatop_linked_list.h"
48#include "datatop_fileops.h"
49
50/**
51 * @brief Prints the name and prefix of a datapoint.
52 *
53 * @param dp Dp whose name and prefix is printed.
54 * @param prefix Directory where dp is contained.
55 * @param fw File to print the information to.
56 * @return FILE_ERROR - Writing to file was unsuccessful.
57 * @return FILE_SUCCESS - Writing to file was successful.
58 */
59static int dtop_format_dp_names(struct dtop_data_point *dp, const char
60						*prefix, FILE *fw)
61{
62	if (dp->prefix) {
63		if (fprintf(fw, "\"%s:%s:%s\",", prefix, dp->prefix,
64							dp->name) < 0)
65			return FILE_ERROR;
66	} else {
67		if (fprintf(fw, "\"%s::%s\",", prefix, dp->name) < 0)
68			return FILE_ERROR;
69	}
70	return FILE_SUCCESS;
71}
72
73/**
74 * @brief Prints the value of a datapoint.
75 *
76 * Checks the type of the value and will print it accordingly.
77 *
78 * @param dp Pointer to the data_point struct which holds the value that will
79 *           be printed.
80 * @param fw File to print the information to.
81 * @return FILE_ERROR - Writing to file was unsuccessful.
82 * @return FILE_SUCCESS - Writing to file was successful.
83 */
84static int dtop_format_dp_values(struct dtop_data_point *dp, FILE *fw)
85{
86	switch (dp->type) {
87	case DTOP_ULONG:
88		if (fprintf(fw, "%"PRIu64, dp->data.d_ulong) < 0)
89			return FILE_ERROR;
90	break;
91	case DTOP_LONG:
92		if (fprintf(fw, "%"PRId64, dp->data.d_long) < 0)
93			return FILE_ERROR;
94	break;
95	case DTOP_UINT:
96		if (fprintf(fw, "%d", dp->data.d_uint) < 0)
97			return FILE_ERROR;
98	break;
99	case DTOP_INT:
100		if (fprintf(fw, "%u", dp->data.d_uint) < 0)
101			return FILE_ERROR;
102	break;
103	case DTOP_UCHAR:
104		if (fprintf(fw, "%c,", dp->data.d_uchar) < 0)
105			return FILE_ERROR;
106		if (fprintf(fw, "(0x%02X)", dp->data.d_uchar) < 0)
107			return FILE_ERROR;
108	break;
109	case DTOP_CHAR:
110		if (fprintf(fw, "%c,", dp->data.d_char) < 0)
111			return FILE_ERROR;
112		if (fprintf(fw, "(%d)", dp->data.d_char) < 0)
113			return FILE_ERROR;
114	break;
115	case DTOP_STR:
116		if (fprintf(fw, "\"%s\"", dp->data.d_str) < 0)
117			return FILE_ERROR;
118	break;
119	default:
120		if (fprintf(fw, "UNKNOWN_TYPE") < 0)
121			return FILE_ERROR;
122	break;
123	}
124	return FILE_SUCCESS;
125}
126
127/**
128 * @brief Prints the name and prefix of a dp, formatted appropriately.
129 *
130 * @param dpset data_point_gatherer used to access dp directory.
131 * @param dp data_point used to get datapoint prefix if available.
132 */
133static void dtop_format_text_for_snapshot
134	(struct dtop_data_point_gatherer *dpset, struct dtop_data_point dp)
135{
136	printf("%s:", dpset->prefix);
137	if (dp.prefix)
138		printf("%s:", dp.prefix);
139
140	printf("%s::", dp.name);
141}
142
143/**
144 * @brief Prints a datapoint value to a specified csv file.
145 *
146 * @param dp Datapoint that holds the value to be printed.
147 * @param fw File to print to.
148 * @return FILE_ERROR - Writing to file was unsuccessful.
149 * @return FILE_SUCCESS - Writing to file was successful.
150 */
151static int dtop_print_dp_csv(struct dtop_data_point *dp, FILE *fw)
152{
153	if (dtop_format_dp_values(dp, fw) == FILE_ERROR)
154		return FILE_ERROR;
155	if (fprintf(fw, ",") < 0)
156		return FILE_ERROR;
157	return FILE_SUCCESS;
158}
159
160/**
161 * @brief Prints a datapoint value to the terminal.
162 *
163 * @param dp Holds the value to be printed print.
164 * @param prefix Used to print prefix of the data_point.
165 */
166static void dtop_print_dp(struct dtop_data_point *dp, const char *prefix)
167{
168	dtop_format_dp_names(dp, prefix, stdout);
169	printf(" ");
170	dtop_format_dp_values(dp, stdout);
171	printf("\n");
172}
173
174/**
175 * @brief Finds delta(value) of a datapoint.
176 *
177 * Function accounts for different types that values may be.
178 *
179 * @param dpset Pointer to a data_point used as another parameter for printing.
180 * @param dp Datapoint which contains the value to find the difference of.
181 */
182static void dtop_handle_dp_type_for_snapshot(
183	struct dtop_data_point_gatherer *dpset, struct dtop_data_point dp)
184{
185	int64_t int64;
186
187	switch (dp.type) {
188	case DTOP_ULONG:
189	default:
190		/* This is less than ideal. Replace with 128-bit ops later */
191		int64 = (int64_t)dp.data.d_ulong
192			- (int64_t)dp.initial_data.d_ulong;
193		if (int64 != 0) {
194			dtop_format_text_for_snapshot(dpset, dp);
195			printf("%"PRId64"\n", int64);
196		}
197	break;
198
199	case DTOP_LONG:
200		/* This is less than ideal. Replace with 128-bit ops later */
201		int64 = (int64_t)dp.data.d_long
202			- (int64_t)dp.initial_data.d_long;
203		if (int64 != 0) {
204			dtop_format_text_for_snapshot(dpset, dp);
205			printf("%"PRId64"\n", int64);
206		}
207	break;
208
209	case DTOP_UINT:
210		int64 = (int64_t)dp.data.d_uint
211			- (int64_t)dp.initial_data.d_uint;
212		if (int64 != 0) {
213			dtop_format_text_for_snapshot(dpset, dp);
214			printf("%"PRId64"\n", int64);
215		}
216	break;
217
218	case DTOP_INT:
219		int64 = (int64_t)dp.data.d_int
220			- (int64_t)dp.initial_data.d_int;
221		if (int64 != 0) {
222			dtop_format_text_for_snapshot(dpset, dp);
223			printf("%"PRId64"\n", int64);
224		}
225	break;
226	}
227}
228
229/**
230 * @brief Calls the dtop_print_dp_csv function for each data_point a dpg has access to.
231 *
232 * @param dpg A data_point_gatherer struct that is iterated through for each datapoint.
233 * @param fw File to print datapoint values to.
234 * @return FILE_ERROR - Writing to file was unsuccessful.
235 * @return FILE_SUCCESS - Writing to file was successful.
236 */
237static int dtop_print_dpg_csv(struct dtop_data_point_gatherer *dpg, FILE *fw)
238{
239	int i;
240
241	for (i = 0; i < dpg->data_points_len; i++)
242		if (dtop_print_dp_csv(&(dpg->data_points[i]), fw) == FILE_ERROR)
243			return FILE_ERROR;
244	return FILE_SUCCESS;
245}
246
247/**
248 * @brief Calls the dtop_format_dp_names function for each data_point a dpg has access to.
249 *
250 * @param dpg A data_point_gatherer struct that is iterated through for each datapoint.
251 * @param fw File to printg datapoint names and prefixes to.
252 * @return FILE_ERROR - Writing to file was unsuccessful.
253 * @return FILE_SUCCESS - Writing to file was successful.
254 */
255int dtop_print_dpg_names_csv(struct dtop_data_point_gatherer *dpg, FILE *fw)
256{
257	int i;
258
259	for (i = 0; i < dpg->data_points_len; i++)
260		if (dtop_format_dp_names(&(dpg->data_points[i]),
261		dpg->prefix, fw) == FILE_ERROR)
262			return FILE_ERROR;
263
264	return FILE_SUCCESS;
265}
266
267/**
268 * @brief Prints all dp values to a specified file.
269 *
270 * This function is responsible for the printing of all data_point values
271 * to a specified file. It will iterate through the linked list which contains
272 * all of the dpgs and will print each dp value, being sure to flush the buffer.
273 *
274 * @param dpg_list Pointer to first node of linked list which contains all dpgs.
275 * @param fw File that data prints to.
276 * @return FILE_ERROR - Writing to file was unsuccessful.
277 * @return FILE_SUCCESS - Writing to file was successful.
278 */
279int dtop_write_pollingdata_csv(struct dtop_linked_list *dpg_list, FILE *fw)
280{
281	struct dtop_linked_list *curr_ptr = dpg_list;
282	struct dtop_data_point_gatherer *dpset;
283
284	while (curr_ptr) {
285		dpset = (struct dtop_data_point_gatherer *) curr_ptr->data;
286		if (dtop_print_dpg_csv(dpset, fw) == FILE_ERROR)
287			return FILE_ERROR;
288		curr_ptr = curr_ptr->next_ptr;
289		fflush(fw);
290	}
291
292	if (fprintf(fw, "\n") < 0)
293		return FILE_ERROR;
294
295	return FILE_SUCCESS;
296}
297
298/**
299 * @brief Calls the dtop_print_dp function for each data_point a dpg has access to.
300 *
301 * @param dpg A data_point_gatherer struct that is iterated through for each datapoint.
302 */
303void dtop_print_dpg(struct dtop_data_point_gatherer *dpg)
304{
305	int i;
306	for (i = 0; i < dpg->data_points_len; i++)
307		dtop_print_dp(&(dpg->data_points[i]), dpg->prefix);
308}
309
310/**
311 * @brief Stores the values for the datapoints and populates the initial value.
312 *
313 * @param dp A datapoint whose value will be stored.
314 * @param str Str used for sscanf function call to find value of dp.
315 */
316void dtop_store_dp(struct dtop_data_point *dp, const char *str)
317{
318	switch (dp->type) {
319	case DTOP_ULONG:
320		sscanf(str, "%"PRIu64, &(dp->data.d_ulong));
321		if (dp->initial_data_populated == NOT_POPULATED) {
322			dp->initial_data.d_ulong = dp->data.d_ulong;
323			dp->initial_data_populated = POPULATED;
324		}
325	break;
326	case DTOP_LONG:
327		sscanf(str, "%"PRId64, &(dp->data.d_long));
328		if (dp->initial_data_populated == NOT_POPULATED) {
329			dp->initial_data.d_long = dp->data.d_long;
330			dp->initial_data_populated = POPULATED;
331		}
332	break;
333	case DTOP_UINT:
334		sscanf(str, "%u",  &(dp->data.d_uint));
335		if (dp->initial_data_populated == NOT_POPULATED) {
336			dp->initial_data.d_uint = dp->data.d_uint;
337			dp->initial_data_populated = POPULATED;
338		}
339	break;
340	case DTOP_INT:
341		sscanf(str, "%d",  &(dp->data.d_int));
342		if (dp->initial_data_populated == NOT_POPULATED) {
343			dp->initial_data.d_int = dp->data.d_int;
344			dp->initial_data_populated = POPULATED;
345		}
346	break;
347	case DTOP_UCHAR:
348		sscanf(str, "%c",  &(dp->data.d_uchar));
349		if (dp->initial_data_populated == NOT_POPULATED) {
350			dp->initial_data.d_uchar = dp->data.d_uchar;
351			dp->initial_data_populated = POPULATED;
352		}
353	break;
354	case DTOP_CHAR:
355		sscanf(str, "%c",  &(dp->data.d_char));
356		if (dp->initial_data_populated == NOT_POPULATED) {
357			dp->initial_data.d_char = dp->data.d_char;
358			dp->initial_data_populated = POPULATED;
359		}
360	break;
361	case DTOP_STR:
362		sscanf(str, "%s",  dp->data.d_str);
363		if (dp->initial_data_populated == NOT_POPULATED) {
364			memcpy(dp->initial_data.d_str, dp->data.d_str,
365			       DTOP_DP_MAX_STR_LEN);
366			dp->initial_data_populated = POPULATED;
367		}
368	break;
369	default:
370	break;
371	}
372}
373
374/**
375 * @brief Responsible for calculating and printing current time to file.
376 *
377 * Prints the time since 1970, in Seconds and Milliseconds.
378 *
379 * @param fw File that time is printed to.
380 * @return FILE_ERROR - Writing to file was unsuccessful.
381 * @return FILE_SUCCESS - Writing to file was successful.
382 */
383int dtop_print_time_at_poll(FILE *fw)
384{
385	struct timeval tv;
386	gettimeofday(&tv, NULL);
387
388	if (fprintf(fw, "%10ld", tv.tv_sec) < 0)
389		return FILE_ERROR;
390
391	if (fprintf(fw, ".%06ld,", tv.tv_usec) < 0)
392		return FILE_ERROR;
393
394	return FILE_SUCCESS;
395}
396
397/**
398 * @brief Polls all dp values and updates each value.
399 *
400 * @param dpg_list Pointer to first node of linked list which contains all dpgs.
401 */
402void dtop_poll(struct dtop_linked_list *dpg_list)
403{
404	struct dtop_linked_list *curr_ptr = dpg_list;
405	struct dtop_data_point_gatherer *dpset;
406
407	while (curr_ptr) {
408		dpset = (struct dtop_data_point_gatherer *) curr_ptr->data;
409		dpset->poll(dpset);
410		curr_ptr = curr_ptr->next_ptr;
411	}
412}
413
414/**
415 * @brief Prints the delta(value) of all data_points to terminal.
416 *
417 * @param dpg_list Pointer to first node of linked list which contains all dpgs.
418 */
419void dtop_print_snapshot_diff(struct dtop_linked_list *dpg_list)
420{
421	int i;
422
423	struct dtop_linked_list *curr_ptr = dpg_list;
424	struct dtop_data_point_gatherer *dpset;
425	printf("\n");
426	printf("Change In Datapoint Values\n");
427	printf("---------------------------\n");
428	while (curr_ptr) {
429		dpset = (struct dtop_data_point_gatherer *) curr_ptr->data;
430		for (i = 0; i < dpset->data_points_len; i++)
431			dtop_handle_dp_type_for_snapshot(dpset,
432					  dpset->data_points[i]);
433		curr_ptr = curr_ptr->next_ptr;
434	}
435	printf("\n");
436}
437
438/**
439 * @brief Resets the initial values of all data_points.
440 *
441 * @param dpg_list Pointer to first node of linked list which contains all dpgs.
442 */
443void dtop_reset_dp_initial_values(struct dtop_linked_list *dpg_list)
444{
445	int i;
446
447	struct dtop_linked_list *curr_ptr = dpg_list;
448	struct dtop_data_point_gatherer *dpset;
449
450	while (curr_ptr) {
451		dpset = (struct dtop_data_point_gatherer *) curr_ptr->data;
452		for (i = 0; i < dpset->data_points_len; i++)
453			dpset->data_points[i].initial_data_populated
454							= NOT_POPULATED;
455		curr_ptr = curr_ptr->next_ptr;
456	}
457}
458
459/**
460 * @brief Calls deconstructor method for all dpgs dynamically created.
461 *
462 * Checks to see if each dpg created has a deconstructor method. If not null,
463 * function calls the appropiate deconstructor method to deallocate memory.
464 *
465 * @param dpg_list Pointer to first node of linked list which contains all dpgs.
466 */
467void deconstruct_dpgs(struct dtop_linked_list *dpg_list)
468{
469	struct dtop_linked_list *curr_ptr = dpg_list;
470	struct dtop_data_point_gatherer *dpset;
471
472	while (curr_ptr) {
473		dpset = (struct dtop_data_point_gatherer *) curr_ptr->data;
474		if (dpset->deconstruct)
475			dpset->deconstruct(dpset);
476		curr_ptr = curr_ptr->next_ptr;
477	}
478}
479