diff options
author | Joseph Hunkeler <jhunkeler@gmail.com> | 2023-02-15 17:54:16 -0500 |
---|---|---|
committer | Joseph Hunkeler <jhunkeler@gmail.com> | 2023-02-15 17:54:16 -0500 |
commit | e4f00d115636d66514c7fa6407e7aa86dd542012 (patch) | |
tree | e3a6fa2faa8f90796f558f3658cf3ac605559ab4 | |
parent | ac5ccfa8c3312f8156b6e6ddedee6355b0590321 (diff) | |
download | mstat-e4f00d115636d66514c7fa6407e7aa86dd542012.tar.gz |
Move plotting functions to gnuplot.[c,h]
* Set Options and parse_options to static
* Check -f argument receives a valid string
* Change data_total from int to size_t
* Print option.filename instead of argv[1] before reading the mstat file
* Fix allocation size of gp array
* Add better ticmarks
* Increase line width to 1.
* Bugfix: wait for user to close gnuplot window before exiting
* Bugfix: free pointers to axis_y and gp
-rw-r--r-- | mstat_plot.c | 168 |
1 files changed, 16 insertions, 152 deletions
diff --git a/mstat_plot.c b/mstat_plot.c index 3ccf888..fd32582 100644 --- a/mstat_plot.c +++ b/mstat_plot.c @@ -1,147 +1,14 @@ -// -// Created by jhunk on 8/24/22. -// - -#include <string.h> -#include <stdarg.h> #include "common.h" +#include "gnuplot.h" extern char *mstat_field_names[]; -struct GNUPLOT_PLOT { - char *title; - char *xlabel; - char *ylabel; - char *line_type; - double line_width; - unsigned int line_color; - unsigned char grid_toggle; - unsigned char autoscale_toggle; - unsigned char legend_toggle; - unsigned char legend_enhanced; - char *legend_title; -}; - -struct Option { +static struct Option { unsigned char verbose; char *fields[0xffff]; char filename[PATH_MAX]; } option; - -/** - * Open a new gnuplot handle - * @return stream on success, or NULL on error - */ -FILE *gnuplot_open() { - // -p = persistent window after exit - return popen("gnuplot -p", "w"); -} - -/** - * Close a gnuplot handle - * @param fp pointer to gnuplot stream - * @return 0 on success, or <0 on error - */ -int gnuplot_close(FILE *fp) { - return pclose(fp); -} - -/** - * Send shell command to gnuplot instance - * @param fp pointer to gnuplot stream - * @param fmt command to execute (requires caller to end string with a LF "\n") - * @param ... formatter arguments - * @return value of `vfprintf()`. <0 on error - */ -int gnuplot_sh(FILE *fp, char *fmt, ...) { - int status; - - va_list args; - va_start(args, fmt); - status = vfprintf(fp, fmt, args); - va_end(args); - - return status; -} - -/** - * Generate a plot - * Each GNUPLOT_PLOT pointer in the `gp` array corresponds to a line. - * @param fp pointer to gnuplot stream - * @param gp pointer to an array of GNUPLOT_PLOT structures - * @param x an array representing the x axis - * @param y an array of double-precision arrays representing the y axes - * @param x_count total length of array x - * @param y_count total number of arrays in y - */ -void gnuplot_plot(FILE *fp, struct GNUPLOT_PLOT **gp, double x[], double *y[], size_t x_count, size_t y_count) { - // Configure plot - gnuplot_sh(fp, "set title '%s'\n", gp[0]->title); - gnuplot_sh(fp, "set xlabel '%s'\n", gp[0]->xlabel); - gnuplot_sh(fp, "set ylabel '%s'\n", gp[0]->ylabel); - if (gp[0]->grid_toggle) - gnuplot_sh(fp, "set grid\n"); - if (gp[0]->autoscale_toggle) - gnuplot_sh(fp, "set autoscale\n"); - if (gp[0]->legend_toggle) { - gnuplot_sh(fp, "set key nobox\n"); - //gnuplot_sh(fp, "set key bmargin\n"); - gnuplot_sh(fp, "set key font ',5'\n"); - gnuplot_sh(fp, "set key outside\n"); - } - if (gp[0]->legend_enhanced) { - gnuplot_sh(fp, "set key enhanced\n"); - } else { - gnuplot_sh(fp, "set key noenhanced\n"); - } - - // Begin plotting - gnuplot_sh(fp, "plot "); - for (size_t i = 0; i < y_count; i++) { - char pltbuf[1024] = {0}; - sprintf(pltbuf, "'-' "); - if (gp[0]->legend_toggle) { - sprintf(pltbuf + strlen(pltbuf), "title '%s' ", gp[i]->legend_title); - sprintf(pltbuf + strlen(pltbuf), "with lines "); - if (gp[i]->line_width) { - sprintf(pltbuf + strlen(pltbuf), "lw %0.1f ", gp[i]->line_width); - } - if (gp[i]->line_type) { - sprintf(pltbuf + strlen(pltbuf), "lt %s ", gp[i]->line_type); - } - if (gp[i]->line_color) { - sprintf(pltbuf + strlen(pltbuf), "lc rgb '#%06x' ", gp[i]->line_color); - } - gnuplot_sh(fp, "%s ", pltbuf); - } else { - gnuplot_sh(fp, "with lines "); - } - if (i < y_count - 1) { - gnuplot_sh(fp, ", "); - } - } - gnuplot_sh(fp, "\n"); - - // Emit MSTAT data - for (size_t arr = 0; arr < y_count; arr++) { - for (size_t i = 0; i < x_count; i++) { - gnuplot_sh(fp, "%lf %lf\n", x[i], y[arr][i]); - } - // Commit plot and execute - gnuplot_sh(fp, "e\n"); - } - fflush(fp); -} - -unsigned int rgb(unsigned char r, unsigned char g, unsigned char b) { - unsigned int result = r; - result = result << 8 | g; - result = result << 8 | b; - return result; -} - - static void show_fields(char **fields) { size_t total; for (total = 0; fields[total] != NULL; total++); @@ -174,7 +41,7 @@ static void usage(char *prog) { "", name); } -void parse_options(int argc, char *argv[]) { +static void parse_options(int argc, char *argv[]) { if (argc < 2) { usage(argv[0]); exit(1); @@ -187,7 +54,7 @@ void parse_options(int argc, char *argv[]) { for (int x = 0, i = 1; i < argc; i++) { char *arg = argv[i]; - if (strlen(argv[i]) > 1 && !strncmp(argv[i], "-", 1)) { + if (strlen(arg) > 1 && !strncmp(arg, "-", 1)) { arg = argv[i] + 1; if (!strcmp(arg, "h")) { usage(argv[0]); @@ -201,6 +68,7 @@ void parse_options(int argc, char *argv[]) { option.verbose = 1; } if (!strcmp(arg, "f")) { + mstat_check_argument_str(argv, arg, i); char *val = argv[i+1]; char *token = NULL; if (!val) { @@ -230,7 +98,7 @@ int main(int argc, char *argv[]) { struct mstat_record_t p; char **stored_fields; char **field; - int data_total; + size_t data_total; double **axis_y; double *axis_x; double mem_min, mem_max; @@ -242,7 +110,6 @@ int main(int argc, char *argv[]) { parse_options(argc, argv); rec = 0; - data_total = 0; mem_min = 0.0; mem_max = 0.0; field = option.fields; @@ -298,13 +165,13 @@ int main(int argc, char *argv[]) { } mstat_rewind(fp); - printf("Reading: %s\n", argv[1]); + printf("Reading: %s\n", option.filename); // Assign requested MSTAT data to y-axis. x-axis will always be time elapsed. rec = 0; while (!mstat_iter(fp, &p)) { axis_x[rec] = mstat_get_field_by_name(&p, "timestamp").d64 / 3600; - for (int i = 0; i < data_total; i++) { + for (size_t i = 0; i < data_total; i++) { axis_y[i][rec] = (double) mstat_get_field_by_name(&p, field[i]).u64 / 1024; } rec++; @@ -328,7 +195,7 @@ int main(int argc, char *argv[]) { exit(1); } - struct GNUPLOT_PLOT **gp = calloc(data_total, sizeof(**gp)); + struct GNUPLOT_PLOT **gp = calloc(data_total + 1, sizeof(*gp[0])); if (!gp) { perror("Unable to allocate memory for gnuplot configuration array"); exit(1); @@ -342,27 +209,22 @@ int main(int argc, char *argv[]) { } } - unsigned char r, g, b; char title[255] = {0}; - r = 0x10; - g = 0x20; - b = 0x30; snprintf(title, sizeof(title) - 1, "Memory Usage (PID %d)", p.pid); gp[0]->xlabel = strdup("Time (HR)"); gp[0]->ylabel = strdup("MB"); gp[0]->title = strdup(title); gp[0]->grid_toggle = 1; + gp[0]->grid_mytics = 5; + gp[0]->grid_mxtics = 5; gp[0]->autoscale_toggle = 1; gp[0]->legend_toggle = 1; for (size_t i = 0; i < data_total; i++) { gp[i]->legend_title = strdup(field[i]); - gp[i]->line_color = rgb(r, g, b); - gp[i]->line_width = 0.50; - r -= 0x30; - b += 0x20; - g *= 3; + gp[i]->line_width = 1.0; + gp[i]->line_color = 0; } printf("Generating plot... "); @@ -375,6 +237,7 @@ int main(int argc, char *argv[]) { exit(1); } gnuplot_plot(plt, gp, axis_x, axis_y, rec, data_total); + gnuplot_wait(plt); gnuplot_close(plt); printf("done!\n"); @@ -383,6 +246,7 @@ int main(int argc, char *argv[]) { free(axis_y[i]); free(gp[i]); } - + free(axis_y); + free(gp); return 0; } |