basekernel/user/livestat.c

383 lines
12 KiB
C

#include "library/malloc.h"
#include "library/syscalls.h"
#include "library/string.h"
#include "library/stdio.h"
#include "library/nwindow.h"
/**
* Livestat is a command line utility to display a graph of operating system
* statistics in realtime
*/
#define POINTS 60
#define ARG_MAX 16
#define ARG_SIZE 32
typedef enum STAT_LIVE {
DRIVER_LIVE,
BCACHE_LIVE,
SYSTEM_LIVE,
PROCESS_LIVE
} STAT_LIVE;
struct stat_args {
/* General */
STAT_LIVE stat_type;
char * stat_name;
void * statistics;
/* Process */
char * pid_s;
int pid;
int syscall_index;
/* Driver */
char * driver_name;
/* System */
int device_unit;
};
void help();
void stat_live_2_str(STAT_LIVE stat_l, char * str);
void create_graph(STAT_LIVE stat_type, char * stat_name, char * stat_arg, int window_width, int window_height, int plot_width, int plot_height, int thickness, int char_offset);
int extract_statistic(struct stat_args * args);
void plot_bars(int * most_recent_vals, int max, int window_width, int window_height, int plot_width, int plot_height, int thickness, int char_offset);
void run_stats(struct stat_args * args);
struct nwindow *nw = 0;
int main(int argc, const char * argv[]) {
/* Setup program parameters */
int current_arg = 1;
struct stat_args args = { 0 };
/* Parse command line options */
if (argc < 2) {
help(1); return 1;
}
while(current_arg < argc) {
if (!strcmp(argv[current_arg], "-h") || !strcmp(argv[current_arg], "--help")) {
help(); return 0;
}
else if (!strcmp(argv[current_arg], "-b")) {
args.statistics = malloc(sizeof(struct bcache_stats));
args.stat_type = BCACHE_LIVE;
}
else if (!strcmp(argv[current_arg], "-dr")) {
args.statistics = malloc(sizeof(struct device_driver_stats));
args.stat_type = DRIVER_LIVE;
args.driver_name = strdup(argv[++current_arg]);
}
else if (!strcmp(argv[current_arg], "-sys")) {
args.statistics = malloc(sizeof(struct system_stats));
args.stat_type = SYSTEM_LIVE;
}
else if (!strcmp(argv[current_arg], "-p")) {
args.statistics = malloc(sizeof(struct process_stats));
args.stat_type = PROCESS_LIVE;
args.pid_s = strdup(argv[++current_arg]);
str2int(args.pid_s, &(args.pid));
}
else if (!strcmp(argv[current_arg], "-s")) {
args.stat_name = strdup(argv[++current_arg]);
}
else if (!strcmp(argv[current_arg], "-sc")) {
str2int(argv[++current_arg], &(args.syscall_index));
}
else if (!strcmp(argv[current_arg], "-du")) {
str2int(argv[++current_arg], &(args.device_unit));
}
else {
help(); return 1;
}
++current_arg;
}
if (!args.stat_name) {
help(); return 1;
}
nw = nw_create_default();
/* Start tracking stats */
run_stats(&args);
return 0;
}
/** HELPER FUNCTIONS **/
/* Create graph and continuously update it */
void run_stats(struct stat_args * args)
{
/* Initialize variables for graph and stats collection */
int window_width = 270;
int window_height = 270;
int plot_width = 180;
int plot_height = 180;
int thickness = 2;
int char_offset = 8;
/* Generate the graph with correct labels */
if (args->stat_type == BCACHE_LIVE) {
create_graph(args->stat_type, args->stat_name, 0, window_width, window_height, plot_width, plot_height, thickness, char_offset);
} else if (args->stat_type == PROCESS_LIVE) {
create_graph(args->stat_type, args->stat_name, args->pid_s, window_width, window_height, plot_width, plot_height, thickness, char_offset);
} else if (args->stat_type == DRIVER_LIVE) {
create_graph(args->stat_type, args->stat_name, args->driver_name, window_width, window_height, plot_width, plot_height, thickness, char_offset);
} else if (args->stat_type == SYSTEM_LIVE) {
create_graph(args->stat_type, args->stat_name, 0, window_width, window_height, plot_width, plot_height, thickness, char_offset);
}
/* Start tracking stats */
int i, new_val, max;
int most_recent_vals[POINTS];
int last_value = 0;
int first = 1;
char quit = 0;
for (size_t i = 0; i < POINTS; i++)
most_recent_vals[i] = -1;
while(1) {
/* Check to exit */
quit = nw_getchar(nw,0);
if (quit == 'q') break;
/* Get the correct stats */
if (args->stat_type == BCACHE_LIVE) {
syscall_bcache_stats(args->statistics);
} else if (args->stat_type == PROCESS_LIVE) {
syscall_process_stats(args->statistics, args->pid);
} else if (args->stat_type == DRIVER_LIVE) {
syscall_device_driver_stats(args->driver_name, args->statistics);
} else if (args->stat_type == SYSTEM_LIVE) {
syscall_system_stats(args->statistics);
}
/* Grab the specified statistic of interest */
new_val = extract_statistic(args);
/* Skip first value because it is just a baseline */
if (first) {
last_value = new_val;
first = 0;
goto sleep;
}
/* Update points */
for (i = 1; i < POINTS; i++)
most_recent_vals[i-1] = most_recent_vals[i];
most_recent_vals[POINTS-1] = new_val - last_value;
last_value = new_val;
/* Set max to greatest point or 1 */
max = 1;
for (i = 0; i < POINTS; i++) {
if (most_recent_vals[i] > max) max = most_recent_vals[i];
}
/* Replot points */
plot_bars(most_recent_vals, max, window_width, window_height, plot_width, plot_height, thickness, char_offset);
/* Sleep for 6 seconds and then continue */
sleep:
syscall_process_sleep(6000);
}
nw_fgcolor(nw,255, 255, 255);
nw_flush(nw);
}
/* Return one stat from statistics structure */
int extract_statistic(struct stat_args * args) {
if (args->stat_type == BCACHE_LIVE) {
if (!strcmp(args->stat_name, "read_hits")) {
return ((struct bcache_stats *)args->statistics)->read_hits;
} else if (!strcmp(args->stat_name, "read_misses")) {
return ((struct bcache_stats *)args->statistics)->read_misses;
} else if (!strcmp(args->stat_name, "write_hits")) {
return ((struct bcache_stats *)args->statistics)->write_hits;
} else if (!strcmp(args->stat_name, "write_misses")) {
return ((struct bcache_stats *)args->statistics)->write_misses;
} else if (!strcmp(args->stat_name, "writebacks")) {
return ((struct bcache_stats *)args->statistics)->writebacks;
}
}
else if (args->stat_type == PROCESS_LIVE) {
if (!strcmp(args->stat_name, "blocks_read")) {
return ((struct process_stats *)args->statistics)->blocks_read;
} else if (!strcmp(args->stat_name, "blocks_written")) {
return ((struct process_stats *)args->statistics)->blocks_written;
} else if (!strcmp(args->stat_name, "bytes_read")) {
return ((struct process_stats *)args->statistics)->bytes_read;
} else if (!strcmp(args->stat_name, "bytes_written")) {
return ((struct process_stats *)args->statistics)->bytes_written;
} else if (!strcmp(args->stat_name, "syscall_count")) {
return ((struct process_stats *)args->statistics)->syscall_count[args->syscall_index];
}
}
else if (args->stat_type == DRIVER_LIVE) {
if (!strcmp(args->stat_name, "blocks_read")) {
return ((struct device_driver_stats *)args->statistics)->blocks_read;
} else if (!strcmp(args->stat_name, "blocks_written")) {
return ((struct device_driver_stats *)args->statistics)->blocks_written;
}
}
else if (args->stat_type == SYSTEM_LIVE) {
if (!strcmp(args->stat_name, "time")) {
return ((struct system_stats *)args->statistics)->time;
} else if (!strcmp(args->stat_name, "blocks_read")) {
return ((struct system_stats *)args->statistics)->blocks_read[args->device_unit];
} else if (!strcmp(args->stat_name, "blocks_written")) {
return ((struct system_stats *)args->statistics)->blocks_written[args->device_unit];
}
}
return -1;
}
/* Create the graph template for the display */
void create_graph( STAT_LIVE stat_type, char * stat_name, char * stat_arg, int window_width, int window_height, int plot_width, int plot_height, int thickness, int char_offset) {
/* Initialize Parameters */
int i;
int x_offset = (window_width - plot_width) / 2;
int y_offset = (window_height - plot_height) / 2;
char title[100] = "";
stat_live_2_str(stat_type, title);
if (stat_arg != 0) {
strcat(title, ": ");
strcat(title, stat_arg);
}
/* Draw border around window */
nw_clear(nw,0, 0, window_width, window_height);
nw_rect(nw,0, 0, window_width, thickness);
nw_rect(nw,0, 0, thickness, window_height);
nw_rect(nw,window_width - thickness, 0, thickness, window_height);
nw_rect(nw,0, window_height - thickness, window_width, thickness);
/* Draw X, Y axis in center of window */
nw_rect(nw,x_offset, y_offset+plot_height, plot_width, thickness);
nw_rect(nw,x_offset, y_offset, thickness, plot_height);
/* Draw title, x_axis label, y_axis label */
nw_string(nw,window_width/2 - 70, 8, title);
nw_string(nw,window_width/2 - 34, window_height - 16, "Time (6s)");
/* Setup Y axis */
char y[2] = { 0 };
for (i = 0; i < strlen(stat_name); i++) {
y[0] = stat_name[i];
nw_string(nw,10, window_height/2 - 20 + i*char_offset, y);
nw_flush(nw);
}
/* For tick marks on x axis -- always keep the most recent minute - 10 ticks */
int x_tick_width = plot_width/10;
for (i = 0; i < 10+1; i++) {
nw_rect(nw,x_offset+i*x_tick_width, y_offset+plot_height, thickness, 6);
}
/* For tick marks on y axis -- max will be dynamically set */
int y_tick_width = plot_height/10;
for (i = 0; i < 10+1; i++) {
nw_rect(nw,x_offset - 4, y_offset + plot_height - i * y_tick_width, 6, thickness);
}
nw_flush(nw);
}
/* Plot all of the points on the graph and print the max */
void plot_bars( int most_recent_vals[POINTS], int max, int window_width, int window_height, int plot_width, int plot_height, int thickness, int char_offset) {
/* Clear the graph */
int x_offset = (window_width - plot_width) / 2;
int y_offset = (window_height - plot_height) / 2;
nw_clear(nw,x_offset, y_offset, plot_width+thickness, plot_height);
/* Redraw the axis */
nw_rect(nw,x_offset, y_offset+plot_height, plot_width, thickness);
nw_rect(nw,x_offset, y_offset, thickness, plot_height);
/* Create string of the max */
char max_str[32];
uint_to_string((uint32_t) max, max_str);
nw_string(nw,x_offset - 26, y_offset - 10, max_str);
nw_flush(nw);
/* Plot the points */
int i, current_point[2] = { 0 };
/* Variables to plot value in correct location on the graph */
float x_step = (float)plot_width/POINTS;
float y_step = (float)plot_height/max;
nw_fgcolor(nw,0, 255, 0);
for (i = 0; i < POINTS; i++) {
if (most_recent_vals[i] == -1)
continue;
current_point[0] = x_offset + (int)(x_step*(i+1));
current_point[1] = y_offset + plot_height - (int)(y_step*most_recent_vals[i]);
nw_rect(nw,current_point[0], current_point[1], thickness, (int)(y_step*most_recent_vals[i]));
}
nw_flush(nw);
nw_fgcolor(nw,255, 255, 255);
}
/* Convert stat type to string */
void stat_live_2_str(STAT_LIVE stat_l, char * str) {
if (stat_l == BCACHE_LIVE) {
strcpy(str, "Bcache");
}
else if (stat_l == PROCESS_LIVE) {
strcpy(str, "Process");
}
else if (stat_l == DRIVER_LIVE) {
strcpy(str, "Driver");
}
else if (stat_l == SYSTEM_LIVE) {
strcpy(str, "System");
}
}
/* Help message */
void help() {
printf("\nusage\n\n");
printf("statslive.exe\n");
printf(" -h | --help # display help\n");
printf(" -b # buffer cache stats\n");
printf(" -dr <DRIVER_NAME> # driver stats\n");
printf(" -sys <BLOCK> # system stats\n");
printf(" -p <PID> # process stats\n");
printf(" -sc <SYSCALL> # syscall number\n");
printf(" -s <STAT_NAME> # name of statistic\n");
printf("\nBuffercache STAT_NAME options:\n");
printf(" read_hits\n");
printf(" read_misses\n");
printf(" write_hits\n");
printf(" write_misses\n");
printf(" writebacks\n\n");
printf("\nProcess STAT_NAME options:\n");
printf(" blocks_read\n");
printf(" blocks_written\n");
printf(" bytes_read\n");
printf(" bytes_written\n");
printf(" syscall_count\n\n");
printf("\nDriver STAT_NAME options:\n");
printf(" blocks_read\n");
printf(" blocks_written\n\n");
printf("\nSystem STAT_NAME options:\n");
printf(" time\n");
printf(" blocks_read\n");
printf(" blocks_written\n\n");
//TODO memory utilizations (page), kernel malloc
}