#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)
{
.group_stats = handle_gs,
.eta = handle_eta,
.probe = handle_probe,
- /* status display, if NULL, printf is used */
};
static struct timeval eta_tv;
#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);
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);
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;
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]);
}
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;
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];
}
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);
}
}
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)
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;
}
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);
}
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;
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);
#include "fio.h"
-void (*update_thread_status)(char *status_message, double perc) = NULL;
-
static char run_str[REAL_MAX_JOBS + 1];
/*
/*
* 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;
|| 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++;
}
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];
}
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)
#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 {
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;
GtkWidget *error_label;
GtkTextBuffer *text;
struct probe_widget probe;
+ struct eta_widget eta;
pthread_t t;
void *cookie;
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)
.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,
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);
{
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)
{
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,
{ "Quit", GTK_STOCK_QUIT, NULL, "<Control>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 = " \
<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