From 3e47bd250cac5fb81a5c0ad578dfbe90c6ddf6de Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 29 Feb 2012 13:45:02 +0100 Subject: [PATCH] Update GUI to attempt to graphically handle ETA output The whole layout will probably be changed, but for now it demonstrates how to properly integrate with the net client to handle the data and output it. Signed-off-by: Jens Axboe --- client.c | 52 +++++++----------- client.h | 13 ++++- eta.c | 38 ++++++------- gfio.c | 159 +++++++++++++++++++++++++++++++++++++++++++++++++------ server.c | 8 +-- server.h | 2 +- stat.h | 5 +- 7 files changed, 201 insertions(+), 76 deletions(-) diff --git a/client.c b/client.c index faa990ba..842f225c 100644 --- a/client.c +++ b/client.c @@ -21,13 +21,6 @@ #include "flist.h" #include "hash.h" -extern void (*update_thread_status)(char *status_message, double perc); - -struct client_eta { - struct jobs_eta eta; - unsigned int pending; -}; - static void fio_client_text_op(struct fio_client *client, FILE *f, __u16 pdu_len, const char *buf) { @@ -56,7 +49,6 @@ struct client_ops fio_client_ops = { .group_stats = handle_gs, .eta = handle_eta, .probe = handle_probe, - /* status display, if NULL, printf is used */ }; static struct timeval eta_tv; @@ -85,9 +77,6 @@ static int sum_stat_nr; #define FIO_CLIENT_HASH_MASK (FIO_CLIENT_HASH_SZ - 1) static struct flist_head client_hash[FIO_CLIENT_HASH_SZ]; -static int handle_client(struct fio_client *client, struct client_ops *ops); -static void dec_jobs_eta(struct client_eta *eta); - static void fio_client_add_hash(struct fio_client *client) { int bucket = hash_long(client->fd, FIO_CLIENT_HASH_BITS); @@ -135,7 +124,7 @@ static void remove_client(struct fio_client *client) if (!flist_empty(&client->eta_list)) { flist_del_init(&client->eta_list); - dec_jobs_eta(client->eta_in_flight); + fio_client_dec_jobs_eta(client->eta_in_flight, display_thread_status); } free(client->hostname); @@ -681,7 +670,7 @@ static void handle_du(struct fio_client *client, struct fio_net_cmd *cmd) print_disk_util(&du->dus, &du->agg, terse_output); } -static void convert_jobs_eta(struct jobs_eta *je) +void fio_client_convert_jobs_eta(struct jobs_eta *je) { int i; @@ -689,12 +678,12 @@ static void convert_jobs_eta(struct jobs_eta *je) je->nr_ramp = le32_to_cpu(je->nr_ramp); je->nr_pending = le32_to_cpu(je->nr_pending); je->files_open = le32_to_cpu(je->files_open); - je->m_rate = le32_to_cpu(je->m_rate); - je->t_rate = le32_to_cpu(je->t_rate); - je->m_iops = le32_to_cpu(je->m_iops); - je->t_iops = le32_to_cpu(je->t_iops); for (i = 0; i < 2; i++) { + je->m_rate[i] = le32_to_cpu(je->m_rate[i]); + je->t_rate[i] = le32_to_cpu(je->t_rate[i]); + je->m_iops[i] = le32_to_cpu(je->m_iops[i]); + je->t_iops[i] = le32_to_cpu(je->t_iops[i]); je->rate[i] = le32_to_cpu(je->rate[i]); je->iops[i] = le32_to_cpu(je->iops[i]); } @@ -703,7 +692,7 @@ static void convert_jobs_eta(struct jobs_eta *je) je->eta_sec = le64_to_cpu(je->eta_sec); } -static void sum_jobs_eta(struct jobs_eta *dst, struct jobs_eta *je) +void fio_client_sum_jobs_eta(struct jobs_eta *dst, struct jobs_eta *je) { int i; @@ -711,12 +700,12 @@ static void sum_jobs_eta(struct jobs_eta *dst, struct jobs_eta *je) dst->nr_ramp += je->nr_ramp; dst->nr_pending += je->nr_pending; dst->files_open += je->files_open; - dst->m_rate += je->m_rate; - dst->t_rate += je->t_rate; - dst->m_iops += je->m_iops; - dst->t_iops += je->t_iops; for (i = 0; i < 2; i++) { + dst->m_rate[i] += je->m_rate[i]; + dst->t_rate[i] += je->t_rate[i]; + dst->m_iops[i] += je->m_iops[i]; + dst->t_iops[i] += je->t_iops[i]; dst->rate[i] += je->rate[i]; dst->iops[i] += je->iops[i]; } @@ -727,10 +716,10 @@ static void sum_jobs_eta(struct jobs_eta *dst, struct jobs_eta *je) dst->eta_sec = je->eta_sec; } -static void dec_jobs_eta(struct client_eta *eta) +void fio_client_dec_jobs_eta(struct client_eta *eta, void (*fn)(struct jobs_eta *)) { if (!--eta->pending) { - display_thread_status(&eta->eta); + fn(&eta->eta); free(eta); } } @@ -771,9 +760,9 @@ static void handle_eta(struct fio_client *client, struct fio_net_cmd *cmd) client->eta_in_flight = NULL; flist_del_init(&client->eta_list); - convert_jobs_eta(je); - sum_jobs_eta(&eta->eta, je); - dec_jobs_eta(eta); + fio_client_convert_jobs_eta(je); + fio_client_sum_jobs_eta(&eta->eta, je); + fio_client_dec_jobs_eta(eta, display_thread_status); } static void handle_probe(struct fio_client *client, struct fio_net_cmd *cmd) @@ -819,7 +808,7 @@ static void handle_stop(struct fio_client *client, struct fio_net_cmd *cmd) log_info("client <%s>: exited with error %d\n", client->hostname, client->error); } -static int handle_client(struct fio_client *client, struct client_ops *ops) +int fio_handle_client(struct fio_client *client, struct client_ops *ops) { struct fio_net_cmd *cmd; @@ -917,7 +906,7 @@ static void request_client_etas(void) } while (skipped--) - dec_jobs_eta(eta); + fio_client_dec_jobs_eta(eta, display_thread_status); dprint(FD_NET, "client: requested eta tag %p\n", eta); } @@ -984,9 +973,6 @@ int fio_handle_clients(struct client_ops *ops) init_thread_stat(&client_ts); init_group_run_stat(&client_gs); - /* Used by eta.c:display_thread_status() */ - update_thread_status = ops->thread_status_display; - while (!exit_backend && nr_clients) { struct flist_head *entry, *tmp; struct fio_client *client; @@ -1042,7 +1028,7 @@ int fio_handle_clients(struct client_ops *ops) log_err("fio: unknown client fd %d\n", pfds[i].fd); continue; } - if (!handle_client(client, ops)) { + if (!fio_handle_client(client, ops)) { log_info("client: host=%s disconnected\n", client->hostname); remove_client(client); diff --git a/client.h b/client.h index acf31ff0..4146607e 100644 --- a/client.h +++ b/client.h @@ -6,6 +6,8 @@ #include #include +#include "stat.h" + struct fio_net_cmd; struct fio_client { @@ -64,10 +66,19 @@ struct client_ops { client_group_stats_op group_stats; client_eta_op eta; client_probe_op probe; - client_thread_status_display_op thread_status_display; }; extern struct client_ops fio_client_ops; +struct client_eta { + struct jobs_eta eta; + unsigned int pending; +}; + +extern int fio_handle_client(struct fio_client *, struct client_ops *ops); +extern void fio_client_dec_jobs_eta(struct client_eta *eta, void (*fn)(struct jobs_eta *)); +extern void fio_client_sum_jobs_eta(struct jobs_eta *dst, struct jobs_eta *je); +extern void fio_client_convert_jobs_eta(struct jobs_eta *je); + #endif diff --git a/eta.c b/eta.c index 64c02fe2..b07ae806 100644 --- a/eta.c +++ b/eta.c @@ -7,8 +7,6 @@ #include "fio.h" -void (*update_thread_status)(char *status_message, double perc) = NULL; - static char run_str[REAL_MAX_JOBS + 1]; /* @@ -85,7 +83,7 @@ static void check_str_update(struct thread_data *td) /* * Convert seconds to a printable string. */ -static void eta_to_str(char *str, unsigned long eta_sec) +void eta_to_str(char *str, unsigned long eta_sec) { unsigned int d, h, m, s; int disp_hour = 0; @@ -275,11 +273,14 @@ int calc_thread_status(struct jobs_eta *je, int force) || td->runstate == TD_FSYNCING || td->runstate == TD_PRE_READING) { je->nr_running++; - je->t_rate += td->o.rate[0] + td->o.rate[1]; - je->m_rate += td->o.ratemin[0] + td->o.ratemin[1]; - je->t_iops += td->o.rate_iops[0] + td->o.rate_iops[1]; - je->m_iops += td->o.rate_iops_min[0] + - td->o.rate_iops_min[1]; + je->t_rate[0] += td->o.rate[0]; + je->t_rate[1] += td->o.rate[1]; + je->m_rate[0] += td->o.ratemin[0]; + je->m_rate[1] += td->o.ratemin[1]; + je->t_iops[0] += td->o.rate_iops[0]; + je->t_iops[1] += td->o.rate_iops[1]; + je->m_iops[0] += td->o.rate_iops_min[0]; + je->m_iops[1] += td->o.rate_iops_min[1]; je->files_open += td->nr_open_files; } else if (td->runstate == TD_RAMP) { je->nr_running++; @@ -368,16 +369,19 @@ void display_thread_status(struct jobs_eta *je) } p += sprintf(p, "Jobs: %d (f=%d)", je->nr_running, je->files_open); - if (je->m_rate || je->t_rate) { + if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) { char *tr, *mr; - mr = num2str(je->m_rate, 4, 0, i2p); - tr = num2str(je->t_rate, 4, 0, i2p); + mr = num2str(je->m_rate[0] + je->m_rate[1], 4, 0, i2p); + tr = num2str(je->t_rate[0] + je->t_rate[1], 4, 0, i2p); p += sprintf(p, ", CR=%s/%s KB/s", tr, mr); free(tr); free(mr); - } else if (je->m_iops || je->t_iops) - p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops); + } else if (je->m_iops[0] || je->m_iops[1] || je->t_iops[0] || je->t_iops[1]) { + p += sprintf(p, ", CR=%d/%d IOPS", + je->t_iops[0] + je->t_iops[1], + je->m_iops[0] + je->t_iops[1]); + } if (je->eta_sec != INT_MAX && je->nr_running) { char perc_str[32]; char *iops_str[2]; @@ -413,12 +417,8 @@ void display_thread_status(struct jobs_eta *je) } p += sprintf(p, "\r"); - if (update_thread_status) { - update_thread_status(output, perc); - } else { - printf("%s", output); - fflush(stdout); - } + printf("%s", output); + fflush(stdout); } void print_thread_status(void) diff --git a/gfio.c b/gfio.c index 382b54b1..b14eed31 100644 --- a/gfio.c +++ b/gfio.c @@ -28,22 +28,27 @@ #include "fio.h" +static void gfio_update_thread_status(char *status_message, double perc); + #define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0]))) typedef void (*clickfunction)(GtkWidget *widget, gpointer data); -static void quit_clicked(GtkWidget *widget, gpointer data); +static void connect_clicked(GtkWidget *widget, gpointer data); static void start_job_clicked(GtkWidget *widget, gpointer data); static struct button_spec { const char *buttontext; clickfunction f; const char *tooltiptext; + const int start_insensitive; } buttonspeclist[] = { -#define START_JOB_BUTTON 0 +#define CONNECT_BUTTON 0 +#define START_JOB_BUTTON 1 + { "Connect", connect_clicked, "Connect to host", 0 }, { "Start Job", start_job_clicked, - "Send current fio job to fio server to be executed" }, + "Send current fio job to fio server to be executed", 1 }, }; struct probe_widget { @@ -53,6 +58,19 @@ struct probe_widget { GtkWidget *fio_ver; }; +struct eta_widget { + GtkWidget *jobs; + GtkWidget *files; + GtkWidget *read_bw; + GtkWidget *read_iops; + GtkWidget *cr_bw; + GtkWidget *cr_iops; + GtkWidget *write_bw; + GtkWidget *write_iops; + GtkWidget *cw_bw; + GtkWidget *cw_iops; +}; + struct gui { GtkWidget *window; GtkWidget *vbox; @@ -74,6 +92,7 @@ struct gui { GtkWidget *error_label; GtkTextBuffer *text; struct probe_widget probe; + struct eta_widget eta; pthread_t t; void *cookie; @@ -114,9 +133,97 @@ static void gfio_group_stats_op(struct fio_net_cmd *cmd) fio_client_ops.group_stats(cmd); } +static void gfio_update_eta(struct jobs_eta *je) +{ + static int eta_good; + char eta_str[128]; + char output[256]; + char tmp[32]; + double perc = 0.0; + int i2p = 0; + + eta_str[0] = '\0'; + output[0] = '\0'; + + if (je->eta_sec != INT_MAX && je->elapsed_sec) { + perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec); + eta_to_str(eta_str, je->eta_sec); + } + + sprintf(tmp, "%u", je->nr_running); + gtk_label_set_text(GTK_LABEL(ui.eta.jobs), tmp); + sprintf(tmp, "%u", je->files_open); + gtk_label_set_text(GTK_LABEL(ui.eta.files), tmp); + +#if 0 + if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) { + if (je->m_rate || je->t_rate) { + char *tr, *mr; + + mr = num2str(je->m_rate, 4, 0, i2p); + tr = num2str(je->t_rate, 4, 0, i2p); + gtk_label_set_text(GTK_LABEL(ui.eta. + p += sprintf(p, ", CR=%s/%s KB/s", tr, mr); + free(tr); + free(mr); + } else if (je->m_iops || je->t_iops) + p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops); +#else + gtk_label_set_text(GTK_LABEL(ui.eta.cr_bw), "---"); + gtk_label_set_text(GTK_LABEL(ui.eta.cr_iops), "---"); + gtk_label_set_text(GTK_LABEL(ui.eta.cw_bw), "---"); + gtk_label_set_text(GTK_LABEL(ui.eta.cw_iops), "---"); +#endif + + if (je->eta_sec != INT_MAX && je->nr_running) { + char *iops_str[2]; + char *rate_str[2]; + + if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running) + strcpy(output, "-.-% done"); + else { + eta_good = 1; + perc *= 100.0; + sprintf(output, "%3.1f%% done", perc); + } + + rate_str[0] = num2str(je->rate[0], 5, 10, i2p); + rate_str[1] = num2str(je->rate[1], 5, 10, i2p); + + iops_str[0] = num2str(je->iops[0], 4, 1, 0); + iops_str[1] = num2str(je->iops[1], 4, 1, 0); + + gtk_label_set_text(GTK_LABEL(ui.eta.read_bw), rate_str[0]); + gtk_label_set_text(GTK_LABEL(ui.eta.read_iops), iops_str[0]); + gtk_label_set_text(GTK_LABEL(ui.eta.write_bw), rate_str[1]); + gtk_label_set_text(GTK_LABEL(ui.eta.write_iops), iops_str[1]); + + free(rate_str[0]); + free(rate_str[1]); + free(iops_str[0]); + free(iops_str[1]); + } + + if (eta_str[0]) { + char *dst = output + strlen(output); + + sprintf(dst, " - %s", eta_str); + } + + gfio_update_thread_status(output, perc); +} + static void gfio_eta_op(struct fio_client *client, struct fio_net_cmd *cmd) { - fio_client_ops.eta(client, cmd); + struct jobs_eta *je = (struct jobs_eta *) cmd->payload; + struct client_eta *eta = (struct client_eta *) (uintptr_t) cmd->tag; + + client->eta_in_flight = NULL; + flist_del_init(&client->eta_list); + + fio_client_convert_jobs_eta(je); + fio_client_sum_jobs_eta(&eta->eta, je); + fio_client_dec_jobs_eta(eta, gfio_update_eta); } static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd) @@ -165,7 +272,6 @@ struct client_ops gfio_client_ops = { .group_stats = gfio_group_stats_op, .eta = gfio_eta_op, .probe = gfio_probe_op, - .thread_status_display = gfio_update_thread_status, }; static void quit_clicked(__attribute__((unused)) GtkWidget *widget, @@ -200,8 +306,6 @@ static int send_job_files(struct gui *ui) static void start_job_thread(pthread_t *t, struct gui *ui) { - fio_clients_connect(); - if (send_job_files(ui)) { printf("Yeah, I didn't really like those options too much.\n"); gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1); @@ -216,11 +320,17 @@ static void start_job_clicked(__attribute__((unused)) GtkWidget *widget, { struct gui *ui = data; - printf("Start job button was clicked.\n"); gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0); start_job_thread(&ui->t, ui); } +static void connect_clicked(__attribute__((unused)) GtkWidget *widget, + gpointer data) +{ + fio_clients_connect(); + gtk_widget_set_sensitive(ui.button[START_JOB_BUTTON], 1); +} + static void add_button(struct gui *ui, int i, GtkWidget *buttonbox, struct button_spec *buttonspec) { @@ -228,6 +338,7 @@ static void add_button(struct gui *ui, int i, GtkWidget *buttonbox, g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui); gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], TRUE, TRUE, 0); gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext); + gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive); } static void add_buttons(struct gui *ui, @@ -370,7 +481,7 @@ static GtkActionEntry menu_items[] = { { "Quit", GTK_STOCK_QUIT, NULL, "Q", NULL, G_CALLBACK(quit_clicked) }, { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) }, }; -static gint nmenu_items = sizeof (menu_items) / sizeof(menu_items[0]); +static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]); static const gchar *ui_string = " \ \ @@ -498,28 +609,44 @@ static void init_ui(int *argc, char **argv[], struct gui *ui) gtk_container_add(GTK_CONTAINER (ui->hostname_hbox), ui->hostname_combo_box); gtk_container_add(GTK_CONTAINER (ui->topvbox), ui->hostname_hbox); - probe = gtk_frame_new("Host"); + probe = gtk_frame_new("Job"); gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3); probe_frame = gtk_vbox_new(FALSE, 3); gtk_container_add(GTK_CONTAINER(probe), probe_frame); probe_box = gtk_hbox_new(FALSE, 3); gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3); - ui->probe.hostname = new_info_label_in_frame(probe_box, "Host"); ui->probe.os = new_info_label_in_frame(probe_box, "OS"); ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture"); ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version"); + probe_box = gtk_hbox_new(FALSE, 3); + gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3); + ui->eta.jobs = new_info_label_in_frame(probe_box, "Jobs"); + ui->eta.files = new_info_label_in_frame(probe_box, "Open files"); + + probe_box = gtk_hbox_new(FALSE, 3); + gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3); + ui->eta.read_bw = new_info_label_in_frame(probe_box, "Read BW"); + ui->eta.read_iops = new_info_label_in_frame(probe_box, "IOPS"); + ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW"); + ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS"); + + probe_box = gtk_hbox_new(FALSE, 3); + gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3); + ui->eta.write_bw = new_info_label_in_frame(probe_box, "Write BW"); + ui->eta.write_iops = new_info_label_in_frame(probe_box, "IOPS"); + ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW"); + ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS"); + /* * Set up thread status progress bar */ ui->thread_status_pb = gtk_progress_bar_new(); - gtk_progress_bar_set_fraction( - GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0); - gtk_progress_bar_set_text( - GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running"); - gtk_container_add(GTK_CONTAINER (ui->topvbox), ui->thread_status_pb); + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0); + gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running"); + gtk_container_add(GTK_CONTAINER(ui->topvbox), ui->thread_status_pb); /* * Add a text box for text op messages diff --git a/server.c b/server.c index f263a0a3..2d9b009f 100644 --- a/server.c +++ b/server.c @@ -443,12 +443,12 @@ static int handle_send_eta_cmd(struct fio_net_cmd *cmd) je->nr_ramp = cpu_to_le32(je->nr_ramp); je->nr_pending = cpu_to_le32(je->nr_pending); je->files_open = cpu_to_le32(je->files_open); - je->m_rate = cpu_to_le32(je->m_rate); - je->t_rate = cpu_to_le32(je->t_rate); - je->m_iops = cpu_to_le32(je->m_iops); - je->t_iops = cpu_to_le32(je->t_iops); for (i = 0; i < 2; i++) { + je->m_rate[i] = cpu_to_le32(je->m_rate[i]); + je->t_rate[i] = cpu_to_le32(je->t_rate[i]); + je->m_iops[i] = cpu_to_le32(je->m_iops[i]); + je->t_iops[i] = cpu_to_le32(je->t_iops[i]); je->rate[i] = cpu_to_le32(je->rate[i]); je->iops[i] = cpu_to_le32(je->iops[i]); } diff --git a/server.h b/server.h index 2be61733..f2dd29fa 100644 --- a/server.h +++ b/server.h @@ -38,7 +38,7 @@ struct fio_net_int_cmd { }; enum { - FIO_SERVER_VER = 6, + FIO_SERVER_VER = 7, FIO_SERVER_MAX_PDU = 1024, diff --git a/stat.h b/stat.h index 3115539f..bdb858ef 100644 --- a/stat.h +++ b/stat.h @@ -174,8 +174,8 @@ struct jobs_eta { uint32_t nr_ramp; uint32_t nr_pending; uint32_t files_open; - uint32_t m_rate, t_rate; - uint32_t m_iops, t_iops; + uint32_t m_rate[2], t_rate[2]; + uint32_t m_iops[2], t_iops[2]; uint32_t rate[2]; uint32_t iops[2]; uint64_t elapsed_sec; @@ -197,5 +197,6 @@ extern void sum_thread_stats(struct thread_stat *dst, struct thread_stat *src, i extern void sum_group_stats(struct group_run_stats *dst, struct group_run_stats *src); extern void init_thread_stat(struct thread_stat *ts); extern void init_group_run_stat(struct group_run_stats *gs); +extern void eta_to_str(char *str, unsigned long eta_sec); #endif -- 2.25.1