#include "bonnie.h"
#include <stdio.h>
#include <vector>
#include <string.h>
#include <math.h>
// Maximum number of items expected on a csv line
#define MAX_ITEMS 48
#ifndef OS2
using namespace std;
#endif
typedef vector<PCCHAR> STR_VEC;
#ifndef HAVE_MIN_MAX
#if defined(HAVE_ALGO_H) || defined(HAVE_ALGO)
#ifdef HAVE_ALGO
#include <algo>
#else
#include <algo.h>
#endif
#else
#define min(XX,YY) ((XX) < (YY) ? (XX) : (YY))
#define max(XX,YY) ((XX) > (YY) ? (XX) : (YY))
#endif
#endif
vector<STR_VEC> data;
typedef PCCHAR * PPCCHAR;
PPCCHAR * props;
// Print the start of the HTML file
// return the number of columns space in the middle
int header();
// Splits a line of text (CSV format) by commas and adds it to the list to
// process later. Doesn't keep any pointers to the buf...
void read_in(CPCCHAR buf);
// print line in the specified line from columns start..end as a line of a
// HTML table
void print_a_line(int num, int start, int end);
// print a single item of data
void print_item(int num, int item);
// Print the end of the HTML file
void footer();
// Calculate the colors for backgrounds
void calc_vals();
// Returns a string representation of a color that maps to the value. The
// range of values is 0..range_col and val is the value. If reverse is set
// then low values are green and high values are red.
PCCHAR get_col(double range_col, double val, bool reverse, CPCCHAR extra);
typedef enum { eNoCols, eSpeed, eCPU, eLatency } VALS_TYPE;
const VALS_TYPE vals[MAX_ITEMS] =
{ eNoCols,eNoCols,eNoCols,eNoCols,eNoCols,eNoCols,eSpeed,eCPU,eSpeed,eCPU,eSpeed,eCPU,eSpeed,eCPU,eSpeed,eCPU,eSpeed,eCPU,
eNoCols,eNoCols,eNoCols,eNoCols,eNoCols,eSpeed,eCPU,eSpeed,eCPU,eSpeed,eCPU,eSpeed,eCPU,eSpeed,eCPU,eSpeed,eCPU,
eLatency,eLatency,eLatency,eLatency,eLatency,eLatency,eLatency,eLatency,eLatency,eLatency,eLatency,eLatency };
bool col_used[MAX_ITEMS];
#define COL_NAME 2
#define COL_CONCURRENCY 3
#define COL_FILE_SIZE 5
#define COL_DATA_CHUNK_SIZE 6
#define COL_PUTC 7
#define COL_NUM_FILES 19
#define COL_MAX_SIZE 20
#define COL_MIN_SIZE 21
#define COL_NUM_DIRS 22
#define COL_FILE_CHUNK_SIZE 23
#define COL_RAN_DEL_CPU 35
#define COL_PUTC_LATENCY 36
#define COL_SEEKS_LATENCY 41
#define COL_SEQ_CREATE_LATENCY 42
#define COL_RAN_DEL_LATENCY 47
void usage()
{
exit(1);
}
int main(int argc, char **argv)
{
unsigned int i;
for(i = 0; i < MAX_ITEMS; i++)
col_used[i] = false;
char buf[1024];
FILE *fp = NULL;
if(argc > 1)
{
fp = fopen(argv[1], "r");
if(!fp)
usage();
}
while(fgets(buf, sizeof(buf), fp ? fp : stdin))
{
buf[sizeof(buf) - 1] = '\0';
strtok(buf, "\r\n");
read_in(buf);
}
props = new PPCCHAR[data.size()];
for(i = 0; i < data.size(); i++)
{
props[i] = new PCCHAR[MAX_ITEMS];
props[i][0] = NULL;
props[i][1] = NULL;
props[i][COL_NAME] = "bgcolor=\"#FFFFFF\" class=\"rowheader\"><FONT SIZE=+1";
int j;
for(j = COL_CONCURRENCY; j < MAX_ITEMS; j++)
{
if( (j >= COL_NUM_FILES && j <= COL_FILE_CHUNK_SIZE) || j <= COL_DATA_CHUNK_SIZE )
{
props[i][j] = "class=\"size\" bgcolor=\"#FFFFFF\"";
}
else
{
props[i][j] = NULL;
}
}
}
calc_vals();
int mid_width = header();
for(i = 0; i < data.size(); i++)
{
// First print the average speed line
printf("<TR>");
print_item(i, COL_NAME);
if(col_used[COL_CONCURRENCY] == true)
print_item(i, COL_CONCURRENCY);
print_item(i, COL_FILE_SIZE); // file_size
if(col_used[COL_DATA_CHUNK_SIZE] == true)
print_item(i, COL_DATA_CHUNK_SIZE);
print_a_line(i, COL_PUTC, COL_NUM_FILES);
if(col_used[COL_MAX_SIZE])
print_item(i, COL_MAX_SIZE);
if(col_used[COL_MIN_SIZE])
print_item(i, COL_MIN_SIZE);
if(col_used[COL_NUM_DIRS])
print_item(i, COL_NUM_DIRS);
if(col_used[COL_FILE_CHUNK_SIZE])
print_item(i, COL_FILE_CHUNK_SIZE);
print_a_line(i, COL_FILE_CHUNK_SIZE + 1, COL_RAN_DEL_CPU);
printf("</TR>\n");
// Now print the latency line
printf("<TR>");
print_item(i, COL_NAME);
int lat_width = 1;
if(col_used[COL_DATA_CHUNK_SIZE] == true)
lat_width++;
if(col_used[COL_CONCURRENCY] == true)
lat_width++;
printf("<TD class=\"size\" bgcolor=\"#FFFFFF\" COLSPAN=%d>Latency</TD>"
, lat_width);
print_a_line(i, COL_PUTC_LATENCY, COL_SEEKS_LATENCY);
int bef_lat_width;
lat_width = 1;
if(mid_width > 1)
lat_width = 2;
bef_lat_width = mid_width - lat_width;
if(bef_lat_width)
printf("<TD COLSPAN=%d></TD>", bef_lat_width);
printf("<TD class=\"size\" bgcolor=\"#FFFFFF\" COLSPAN=%d>Latency</TD>", lat_width);
print_a_line(i, COL_SEQ_CREATE_LATENCY, COL_RAN_DEL_LATENCY);
printf("</TR>\n");
}
footer();
return 0;
}
typedef struct { double val; int pos; int col_ind; } ITEM;
typedef ITEM * PITEM;
int compar(const void *a, const void *b)
{
double a1 = PITEM(a)->val;
double b1 = PITEM(b)->val;
if(a1 < b1)
return -1;
if(a1 > b1)
return 1;
return 0;
}
void calc_vals()
{
ITEM *arr = new ITEM[data.size()];
for(unsigned int column_ind = 0; column_ind < MAX_ITEMS; column_ind++)
{
switch(vals[column_ind])
{
case eNoCols:
{
for(unsigned int row_ind = 0; row_ind < data.size(); row_ind++)
{
if(data[row_ind][column_ind] && strlen(data[row_ind][column_ind]))
col_used[column_ind] = true;
}
}
break;
case eCPU:
// gotta add support for CPU vals. This means sorting on previous-col
// divided by this column. If a test run takes twice the CPU but does
// three times the work it should be green! If it does half the work but
// uses the same CPU it should be red!
break;
case eSpeed:
case eLatency:
{
for(unsigned int row_ind = 0; row_ind < data.size(); row_ind++)
{
arr[row_ind].val = 0.0;
if(data[row_ind].size() <= column_ind
|| sscanf(data[row_ind][column_ind], "%lf", &arr[row_ind].val) == 0)
arr[row_ind].val = 0.0;
if(vals[column_ind] == eLatency && arr[row_ind].val != 0.0)
{
if(strstr(data[row_ind][column_ind], "ms"))
arr[row_ind].val *= 1000.0;
else if(!strstr(data[row_ind][column_ind], "us"))
arr[row_ind].val *= 1000000.0; // is !us && !ms then secs!
}
arr[row_ind].pos = row_ind;
}
qsort(arr, data.size(), sizeof(ITEM), compar);
int col_count = -1;
double min_col = -1.0, max_col = -1.0;
for(unsigned int sort_ind = 0; sort_ind < data.size(); sort_ind++)
{
// if item is different from previous or if the first row
// (sort_ind == 0) then increment col count
if(sort_ind == 0 || arr[sort_ind].val != arr[sort_ind - 1].val)
{
if(arr[sort_ind].val != 0.0)
{
col_count++;
if(min_col == -1.0)
min_col = arr[sort_ind].val;
else
min_col = min(arr[sort_ind].val, min_col);
max_col = max(max_col, arr[sort_ind].val);
}
}
arr[sort_ind].col_ind = col_count;
}
// if more than 1 line has data then calculate colors
if(col_count > 0)
{
double divisor = max_col / min_col;
if(divisor < 2.0)
{
double mult = sqrt(2.0 / divisor);
max_col *= mult;
min_col /= mult;
}
double range_col = max_col - min_col;
for(unsigned int sort_ind = 0; sort_ind < data.size(); sort_ind++)
{
if(arr[sort_ind].col_ind > -1)
{
bool reverse = false;
PCCHAR extra = "";
if(vals[column_ind] != eSpeed)
{
reverse = true;
extra = " COLSPAN=2";
}
props[arr[sort_ind].pos][column_ind]
= get_col(range_col, arr[sort_ind].val - min_col, reverse, extra);
}
else if(vals[column_ind] != eSpeed)
{
props[arr[sort_ind].pos][column_ind] = "COLSPAN=2";
}
}
}
else
{
for(unsigned int sort_ind = 0; sort_ind < data.size(); sort_ind++)
{
if(vals[column_ind] == eLatency)
{
props[sort_ind][column_ind] = "COLSPAN=2";
}
}
}
}
break;
} // end switch
}
}
PCCHAR get_col(double range_col, double val, bool reverse, CPCCHAR extra)
{
if(reverse)
val = range_col - val;
const int buf_len = 256;
PCHAR buf = new char[buf_len];
int green = int(255.0 * val / range_col);
green = min(green, 255);
int red = 255 - green;
_snprintf(buf
#ifndef NO_SNPRINTF
, buf_len
#endif
, "bgcolor=\"#%02X%02X00\"%s", red, green, extra);
buf[buf_len - 1] = '\0';
return buf;
}
void heading(const char * const head);
int header()
{
int vers_width = 2;
if(col_used[COL_DATA_CHUNK_SIZE] == true)
vers_width++;
if(col_used[COL_CONCURRENCY] == true)
vers_width++;
int mid_width = 1;
if(col_used[COL_MAX_SIZE])
mid_width++;
if(col_used[COL_MIN_SIZE])
mid_width++;
if(col_used[COL_NUM_DIRS])
mid_width++;
if(col_used[COL_FILE_CHUNK_SIZE])
mid_width++;
printf("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">"
"<HTML>"
"<HEAD><TITLE>Bonnie++ Benchmark results</TITLE>"
"<STYLE type=\"text/css\">"
"TD.header {text-align: center; backgroundcolor: \"#CCFFFF\" }"
"TD.rowheader {text-align: center; backgroundcolor: \"#CCCFFF\" }"
"TD.size {text-align: center; backgroundcolor: \"#CCCFFF\" }"
"TD.ksec {text-align: center; fontstyle: italic }"
"</STYLE>"
"<BODY>"
"<TABLE ALIGN=center BORDER=3 CELLPADDING=2 CELLSPACING=1>"
"<TR><TD COLSPAN=%d class=\"header\"><FONT SIZE=+1><B>"
"Version " BON_VERSION
"</B></FONT></TD>"
"<TD COLSPAN=6 class=\"header\"><FONT SIZE=+2><B>Sequential Output</B></FONT></TD>"
"<TD COLSPAN=4 class=\"header\"><FONT SIZE=+2><B>Sequential Input</B></FONT></TD>"
"<TD COLSPAN=2 ROWSPAN=2 class=\"header\"><FONT SIZE=+2><B>Random<BR>Seeks</B></FONT></TD>"
"<TD COLSPAN=%d class=\"header\"></TD>"
"<TD COLSPAN=6 class=\"header\"><FONT SIZE=+2><B>Sequential Create</B></FONT></TD>"
"<TD COLSPAN=6 class=\"header\"><FONT SIZE=+2><B>Random Create</B></FONT></TD>"
"</TR>\n"
"<TR>", vers_width, mid_width);
if(col_used[COL_CONCURRENCY] == true)
printf("<TD COLSPAN=2>Concurrency</TD>");
else
printf("<TD></TD>");
printf("<TD>Size</TD>");
if(col_used[COL_DATA_CHUNK_SIZE] == true)
printf("<TD>Chunk Size</TD>");
heading("Per Char"); heading("Block"); heading("Rewrite");
heading("Per Char"); heading("Block");
printf("<TD>Num Files</TD>");
if(col_used[COL_MAX_SIZE])
printf("<TD>Max Size</TD>");
if(col_used[COL_MIN_SIZE])
printf("<TD>Min Size</TD>");
if(col_used[COL_NUM_DIRS])
printf("<TD>Num Dirs</TD>");
if(col_used[COL_FILE_CHUNK_SIZE])
printf("<TD>Chunk Size</TD>");
heading("Create"); heading("Read"); heading("Delete");
heading("Create"); heading("Read"); heading("Delete");
printf("</TR>");
printf("<TR><TD COLSPAN=%d></TD>", vers_width);
int i;
CPCCHAR ksec_form = "<TD class=\"ksec\"><FONT SIZE=-2>%s/sec</FONT></TD>"
"<TD class=\"ksec\"><FONT SIZE=-2>%% CPU</FONT></TD>";
for(i = 0; i < 5; i++)
{
printf(ksec_form, "K");
}
printf(ksec_form, "");
printf("<TD COLSPAN=%d></TD>", mid_width);
for(i = 0; i < 6; i++)
{
printf(ksec_form, "");
}
printf("</TR>\n");
return mid_width;
}
void heading(const char * const head)
{
printf("<TD COLSPAN=2>%s</TD>", head);
}
void footer()
{
printf("</TABLE>\n</BODY></HTML>\n");
}
STR_VEC split(CPCCHAR delim, CPCCHAR buf)
{
STR_VEC arr;
char *tmp = strdup(buf);
while(1)
{
arr.push_back(tmp);
tmp = strstr(tmp, delim);
if(!tmp)
break;
*tmp = '\0';
tmp += strlen(delim);
}
return arr;
}
void read_in(CPCCHAR buf)
{
STR_VEC arr = split(",", buf);
if(strcmp(arr[0], CSV_VERSION) )
{
fprintf(stderr, "Can't process: %s\n", buf);
free((void *)arr[0]);
return;
}
data.push_back(arr);
}
void print_item(int num, int item)
{
PCCHAR line_data;
if(int(data[num].size()) > item)
line_data = data[num][item];
else
line_data = "";
if(props[num][item])
printf("<TD %s>%s</TD>", props[num][item], line_data);
else
printf("<TD>%s</TD>", line_data);
}
void print_a_line(int num, int start, int end)
{
int i;
for(i = start; i <= end; i++)
{
print_item(num, i);
}
}
syntax highlighted by Code2HTML, v. 0.9.1