#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 stats\n"); printf(" -sys # system stats\n"); printf(" -p # process stats\n"); printf(" -sc # syscall number\n"); printf(" -s # 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 }