Initial commit
Signed-off-by: Robert Baldyga <robert.baldyga@intel.com>
This commit is contained in:
320
casadm/statistics_view_csv.c
Normal file
320
casadm/statistics_view_csv.c
Normal file
@@ -0,0 +1,320 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "statistics_view.h"
|
||||
#include "statistics_view_structs.h"
|
||||
#include "statistics_view_csv.h"
|
||||
|
||||
#define VALS_BUFFER_INIT_SIZE 10
|
||||
|
||||
/**
|
||||
* private data of CSV output formatter
|
||||
*/
|
||||
struct csv_out_prv {
|
||||
int data_set; /* current data set number */
|
||||
int record; /* current record number */
|
||||
int column; /* current column number */
|
||||
char **vals;
|
||||
char **titles;
|
||||
int max_vals;
|
||||
int cur_val;
|
||||
int max_titles;
|
||||
int cur_title;
|
||||
};
|
||||
|
||||
static inline int csv_is_first_record(struct view_t *this)
|
||||
{
|
||||
return 1 == this->ctx.csv_prv->record;
|
||||
}
|
||||
|
||||
static inline int csv_is_unit_string(const char *s)
|
||||
{
|
||||
return NULL != s && '[' == s[0];
|
||||
}
|
||||
|
||||
static void csv_output_column(struct view_t *this, const char *s)
|
||||
{
|
||||
struct csv_out_prv *prv = this->ctx.csv_prv;
|
||||
|
||||
if (prv->column) {
|
||||
putc(',', this->outfile);
|
||||
}
|
||||
|
||||
if (strstr(s, ",")) {
|
||||
fprintf(this->outfile, "\"%s\"", s);
|
||||
} else {
|
||||
fprintf(this->outfile, "%s", s);
|
||||
}
|
||||
prv->column++;
|
||||
}
|
||||
|
||||
static char **csv_check_container(char **container, int *max_vals,
|
||||
int cur_val)
|
||||
{
|
||||
if (!container) {
|
||||
*max_vals = VALS_BUFFER_INIT_SIZE;
|
||||
container = calloc(sizeof(char *), *max_vals);
|
||||
if (!container) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Resize val pointers array if needed */
|
||||
if (*max_vals < cur_val) {
|
||||
*max_vals = *max_vals * 2;
|
||||
if (*max_vals < cur_val) {
|
||||
*max_vals = cur_val;
|
||||
}
|
||||
container = realloc(container, sizeof(char *) * (*max_vals));
|
||||
if (!container) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
static int csv_output_data(struct view_t *this, const char *s)
|
||||
{
|
||||
struct csv_out_prv *prv = this->ctx.csv_prv;
|
||||
if (csv_is_first_record(this)) {
|
||||
prv->vals = csv_check_container(prv->vals, &prv->max_vals,
|
||||
prv->cur_val+1);
|
||||
if (!prv->vals) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Store value */
|
||||
prv->vals[prv->cur_val] = strdup(s);
|
||||
if (!prv->vals[prv->cur_val]) {
|
||||
return 1;
|
||||
}
|
||||
prv->cur_val++;
|
||||
} else {
|
||||
csv_output_column(this, s);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int csv_add_column_subtitle(struct view_t *this, const char *s)
|
||||
{
|
||||
struct csv_out_prv *prv = this->ctx.csv_prv;
|
||||
|
||||
prv->titles = csv_check_container(prv->titles, &prv->max_titles,
|
||||
prv->cur_title+1);
|
||||
if (!prv->titles) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Store value */
|
||||
prv->titles[prv->cur_title] = strdup(s);
|
||||
if (!prv->titles[prv->cur_title]) {
|
||||
return 1;
|
||||
}
|
||||
prv->cur_title++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void csv_output_header(struct view_t *this, const char *title,
|
||||
const char *unit)
|
||||
{
|
||||
static char buff[64];
|
||||
if (unit) {
|
||||
if (csv_is_unit_string(unit)) {
|
||||
snprintf(buff, sizeof(buff), "%s %s", title, unit);
|
||||
} else {
|
||||
snprintf(buff, sizeof(buff), "%s [%s]", title, unit);
|
||||
}
|
||||
csv_output_column(this, buff);
|
||||
} else {
|
||||
csv_output_column(this, title);
|
||||
}
|
||||
}
|
||||
|
||||
static void csv_finish_record(struct view_t *this)
|
||||
{
|
||||
struct csv_out_prv *prv = this->ctx.csv_prv;
|
||||
int i;
|
||||
|
||||
if (prv->column) {
|
||||
putc('\n', this->outfile);
|
||||
}
|
||||
|
||||
/*
|
||||
* For first record we need to output stored data values
|
||||
*/
|
||||
if (csv_is_first_record(this)) {
|
||||
prv->column = 0;
|
||||
for (i = 0; i < prv->cur_val; ++i) {
|
||||
csv_output_column(this, prv->vals[i]);
|
||||
}
|
||||
if (prv->column) {
|
||||
putc('\n', this->outfile);
|
||||
}
|
||||
}
|
||||
fflush(this->outfile);
|
||||
}
|
||||
|
||||
static void csv_free_vals(struct view_t *this)
|
||||
{
|
||||
struct csv_out_prv *prv = this->ctx.csv_prv;
|
||||
int i;
|
||||
|
||||
if (prv->vals) {
|
||||
for (i = 0; i < prv->cur_val; ++i) {
|
||||
free(prv->vals[i]);
|
||||
}
|
||||
free(prv->vals);
|
||||
prv->vals = NULL;
|
||||
prv->cur_val = 0;
|
||||
prv->max_vals = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void csv_free_titles(struct view_t *this)
|
||||
{
|
||||
struct csv_out_prv *prv = this->ctx.csv_prv;
|
||||
int i;
|
||||
|
||||
if (prv->titles) {
|
||||
for (i = 0; i < prv->cur_title; ++i) {
|
||||
free(prv->titles[i]);
|
||||
}
|
||||
free(prv->titles);
|
||||
prv->titles = NULL;
|
||||
prv->cur_title = 0;
|
||||
prv->max_titles = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int csv_process_row(struct view_t *this, int type, int num_fields, char *fields[])
|
||||
{
|
||||
int i;
|
||||
struct csv_out_prv *prv = this->ctx.csv_prv;
|
||||
const char *unit = NULL;
|
||||
|
||||
switch (type) {
|
||||
case DATA_SET:
|
||||
if (prv->record) {
|
||||
csv_finish_record(this);
|
||||
}
|
||||
csv_free_titles(this);
|
||||
csv_free_vals(this);
|
||||
if (prv->data_set) {
|
||||
putc('\n', this->outfile);
|
||||
}
|
||||
if (num_fields > 0) {
|
||||
fprintf(this->outfile, "%s\n", fields[0]);
|
||||
}
|
||||
prv->record = 0;
|
||||
prv->data_set++;
|
||||
break;
|
||||
case RECORD:
|
||||
if (prv->record) {
|
||||
csv_finish_record(this);
|
||||
}
|
||||
prv->column = 0;
|
||||
prv->record++;
|
||||
break;
|
||||
|
||||
/*
|
||||
* For KV pair assume that values are interleaved
|
||||
* with units, so output every second value,
|
||||
* and use units to construct column headers.
|
||||
* For example:
|
||||
* KV_PAIR,Cache Size,10347970,[4KiB blocks],39.47,[GiB]
|
||||
* will result in:
|
||||
* data row: 10347970,39.47
|
||||
* header row: Cache Size [4KiB blocks],Cache Size [GiB]
|
||||
*/
|
||||
case KV_PAIR:
|
||||
for (i = 1; i < num_fields; i += 2) {
|
||||
if (csv_is_first_record(this)) {
|
||||
if (i + 1 < num_fields) {
|
||||
csv_output_header(this, fields[0],
|
||||
fields[i+1]);
|
||||
} else {
|
||||
csv_output_header(this, fields[0], NULL);
|
||||
}
|
||||
}
|
||||
if (csv_output_data(this, fields[i])) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
/*
|
||||
* For table rows assume the following format:
|
||||
* TABLE_{ROW,SECTION},Title,value1,value2,value3,...,unit
|
||||
* This will result in:
|
||||
* data row: value1,value2,value3,...
|
||||
* header row: Title [unit],Title [col1_title],Title [col2_title],...
|
||||
*/
|
||||
case TABLE_HEADER:
|
||||
csv_free_titles(this);
|
||||
csv_add_column_subtitle(this, "");
|
||||
for (i = 2; i < num_fields; i++) {
|
||||
if (csv_add_column_subtitle(this, fields[i])) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TABLE_SECTION:
|
||||
case TABLE_ROW:
|
||||
if (csv_is_first_record(this)) {
|
||||
unit = NULL;
|
||||
if (csv_is_unit_string(fields[num_fields-1])) {
|
||||
unit = fields[num_fields-1];
|
||||
}
|
||||
csv_output_header(this, fields[0], unit);
|
||||
for (i = 2; i < num_fields; i++) {
|
||||
if (!csv_is_unit_string(prv->titles[i-1])) {
|
||||
csv_output_header(this, fields[0],
|
||||
prv->titles[i-1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i = 1; i < num_fields; i++) {
|
||||
if (!csv_is_unit_string(prv->titles[i-1])) {
|
||||
if (csv_output_data(this, fields[i])) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int csv_end_input(struct view_t *this)
|
||||
{
|
||||
csv_finish_record(this);
|
||||
return 0;
|
||||
}
|
||||
int csv_construct(struct view_t *this)
|
||||
{
|
||||
struct csv_out_prv *prv = calloc(sizeof(struct csv_out_prv), 1);
|
||||
|
||||
if (!prv) {
|
||||
return 1;
|
||||
}
|
||||
this->ctx.csv_prv = prv;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int csv_destruct(struct view_t *this)
|
||||
{
|
||||
csv_free_vals(this);
|
||||
csv_free_titles(this);
|
||||
free(this->ctx.csv_prv);
|
||||
return 0;
|
||||
}
|
||||
|
Reference in New Issue
Block a user