gfio.c revision 3863d1ad1020200f26958da42ed208f6c5c8f372
1/* 2 * gfio - gui front end for fio - the flexible io tester 3 * 4 * Copyright (C) 2012 Stephen M. Cameron <stephenmcameron@gmail.com> 5 * Copyright (C) 2012 Jens Axboe <axboe@kernel.dk> 6 * 7 * The license below covers all files distributed with fio unless otherwise 8 * noted in the file itself. 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License version 2 as 12 * published by the Free Software Foundation. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22 * 23 */ 24#include <locale.h> 25#include <malloc.h> 26#include <string.h> 27 28#include <glib.h> 29#include <cairo.h> 30#include <gtk/gtk.h> 31 32#include "fio.h" 33#include "graph.h" 34 35static int gfio_server_running; 36static const char *gfio_graph_font; 37 38static void view_log(GtkWidget *w, gpointer data); 39 40#define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0]))) 41 42typedef void (*clickfunction)(GtkWidget *widget, gpointer data); 43 44static void connect_clicked(GtkWidget *widget, gpointer data); 45static void start_job_clicked(GtkWidget *widget, gpointer data); 46static void send_clicked(GtkWidget *widget, gpointer data); 47 48static struct button_spec { 49 const char *buttontext; 50 clickfunction f; 51 const char *tooltiptext; 52 const int start_insensitive; 53} buttonspeclist[] = { 54#define CONNECT_BUTTON 0 55#define SEND_BUTTON 1 56#define START_JOB_BUTTON 2 57 { "Connect", connect_clicked, "Connect to host", 0 }, 58 { "Send", send_clicked, "Send job description to host", 1 }, 59 { "Start Job", start_job_clicked, 60 "Start the current job on the server", 1 }, 61}; 62 63struct probe_widget { 64 GtkWidget *hostname; 65 GtkWidget *os; 66 GtkWidget *arch; 67 GtkWidget *fio_ver; 68}; 69 70struct eta_widget { 71 GtkWidget *names; 72 GtkWidget *iotype; 73 GtkWidget *ioengine; 74 GtkWidget *iodepth; 75 GtkWidget *jobs; 76 GtkWidget *files; 77 GtkWidget *read_bw; 78 GtkWidget *read_iops; 79 GtkWidget *cr_bw; 80 GtkWidget *cr_iops; 81 GtkWidget *write_bw; 82 GtkWidget *write_iops; 83 GtkWidget *cw_bw; 84 GtkWidget *cw_iops; 85}; 86 87struct gfio_graphs { 88#define DRAWING_AREA_XDIM 1000 89#define DRAWING_AREA_YDIM 400 90 GtkWidget *drawing_area; 91 int drawing_area_xdim; 92 int drawing_area_ydim; 93 94 struct graph *iops_graph; 95 struct graph *bandwidth_graph; 96}; 97 98/* 99 * Main window widgets and data 100 */ 101struct gui { 102 GtkWidget *window; 103 GtkWidget *vbox; 104 GtkWidget *topvbox; 105 GtkWidget *topalign; 106 GtkWidget *bottomalign; 107 GtkWidget *thread_status_pb; 108 GtkWidget *buttonbox; 109 GtkWidget *scrolled_window; 110 GtkWidget *notebook; 111 GtkWidget *error_info_bar; 112 GtkWidget *error_label; 113 GtkListStore *log_model; 114 GtkWidget *log_tree; 115 GtkWidget *log_view; 116 struct gfio_graphs graphs; 117 struct probe_widget probe; 118 struct eta_widget eta; 119 pthread_t server_t; 120 121 pthread_t t; 122 int handler_running; 123 124 struct flist_head list; 125} main_ui; 126 127/* 128 * Notebook entry 129 */ 130struct gui_entry { 131 struct flist_head list; 132 struct gui *ui; 133 134 GtkWidget *vbox; 135 GtkWidget *topvbox; 136 GtkWidget *topalign; 137 GtkWidget *bottomalign; 138 GtkWidget *thread_status_pb; 139 GtkWidget *buttonbox; 140 GtkWidget *button[ARRAYSIZE(buttonspeclist)]; 141 GtkWidget *scrolled_window; 142 GtkWidget *notebook; 143 GtkWidget *error_info_bar; 144 GtkWidget *error_label; 145 GtkWidget *results_notebook; 146 GtkWidget *results_window; 147 GtkListStore *log_model; 148 GtkWidget *log_tree; 149 GtkWidget *log_view; 150 struct gfio_graphs graphs; 151 struct probe_widget probe; 152 struct eta_widget eta; 153 GtkWidget *page_label; 154 gint page_num; 155 int connected; 156 157 struct gfio_client *client; 158 int nr_job_files; 159 char **job_files; 160}; 161 162struct gfio_client { 163 struct gui_entry *ge; 164 struct fio_client *client; 165 GtkWidget *results_widget; 166 GtkWidget *disk_util_frame; 167 GtkWidget *err_entry; 168 unsigned int job_added; 169 struct thread_options o; 170}; 171 172static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc); 173static void gfio_update_thread_status_all(char *status_message, double perc); 174void report_error(GError *error); 175 176static struct graph *setup_iops_graph(void) 177{ 178 struct graph *g; 179 180 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font); 181 graph_title(g, "IOPS"); 182 graph_x_title(g, "Time (secs)"); 183 graph_y_title(g, "IOs / sec"); 184 graph_add_label(g, "Read IOPS"); 185 graph_add_label(g, "Write IOPS"); 186 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13); 187 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0); 188 line_graph_set_data_count_limit(g, 100); 189 return g; 190} 191 192static struct graph *setup_bandwidth_graph(void) 193{ 194 struct graph *g; 195 196 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font); 197 graph_title(g, "Bandwidth"); 198 graph_x_title(g, "Time (secs)"); 199 graph_y_title(g, "Kbytes / sec"); 200 graph_add_label(g, "Read Bandwidth"); 201 graph_add_label(g, "Write Bandwidth"); 202 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13); 203 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0); 204 line_graph_set_data_count_limit(g, 100); 205 return g; 206} 207 208static void setup_graphs(struct gfio_graphs *g) 209{ 210 g->iops_graph = setup_iops_graph(); 211 g->bandwidth_graph = setup_bandwidth_graph(); 212} 213 214static void clear_ge_ui_info(struct gui_entry *ge) 215{ 216 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), ""); 217 gtk_label_set_text(GTK_LABEL(ge->probe.os), ""); 218 gtk_label_set_text(GTK_LABEL(ge->probe.arch), ""); 219 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), ""); 220#if 0 221 /* should we empty it... */ 222 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), ""); 223#endif 224 gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), ""); 225 gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), ""); 226 gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), ""); 227 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), ""); 228 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), ""); 229 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), ""); 230 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), ""); 231 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), ""); 232 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), ""); 233} 234 235static GtkWidget *new_combo_entry_in_frame(GtkWidget *box, const char *label) 236{ 237 GtkWidget *entry, *frame; 238 239 frame = gtk_frame_new(label); 240 entry = gtk_combo_box_new_text(); 241 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3); 242 gtk_container_add(GTK_CONTAINER(frame), entry); 243 244 return entry; 245} 246 247static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label) 248{ 249 GtkWidget *entry, *frame; 250 251 frame = gtk_frame_new(label); 252 entry = gtk_entry_new(); 253 gtk_entry_set_editable(GTK_ENTRY(entry), 0); 254 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3); 255 gtk_container_add(GTK_CONTAINER(frame), entry); 256 257 return entry; 258} 259 260static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label) 261{ 262 GtkWidget *label_widget; 263 GtkWidget *frame; 264 265 frame = gtk_frame_new(label); 266 label_widget = gtk_label_new(NULL); 267 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3); 268 gtk_container_add(GTK_CONTAINER(frame), label_widget); 269 270 return label_widget; 271} 272 273static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval) 274{ 275 GtkWidget *button, *box; 276 277 box = gtk_hbox_new(FALSE, 3); 278 gtk_container_add(GTK_CONTAINER(hbox), box); 279 280 button = gtk_spin_button_new_with_range(min, max, 1.0); 281 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0); 282 283 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID); 284 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval); 285 286 return button; 287} 288 289static void gfio_set_connected(struct gui_entry *ge, int connected) 290{ 291 if (connected) { 292 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1); 293 ge->connected = 1; 294 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Disconnect"); 295 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1); 296 } else { 297 ge->connected = 0; 298 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Connect"); 299 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0); 300 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0); 301 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1); 302 } 303} 304 305static void label_set_int_value(GtkWidget *entry, unsigned int val) 306{ 307 char tmp[80]; 308 309 sprintf(tmp, "%u", val); 310 gtk_label_set_text(GTK_LABEL(entry), tmp); 311} 312 313static void entry_set_int_value(GtkWidget *entry, unsigned int val) 314{ 315 char tmp[80]; 316 317 sprintf(tmp, "%u", val); 318 gtk_entry_set_text(GTK_ENTRY(entry), tmp); 319} 320 321#define ALIGN_LEFT 1 322#define ALIGN_RIGHT 2 323#define INVISIBLE 4 324#define UNSORTABLE 8 325 326GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags) 327{ 328 GtkCellRenderer *renderer; 329 GtkTreeViewColumn *col; 330 double xalign = 0.0; /* left as default */ 331 PangoAlignment align; 332 gboolean visible; 333 334 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT : 335 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT : 336 PANGO_ALIGN_CENTER; 337 visible = !(flags & INVISIBLE); 338 339 renderer = gtk_cell_renderer_text_new(); 340 col = gtk_tree_view_column_new(); 341 342 gtk_tree_view_column_set_title(col, title); 343 if (!(flags & UNSORTABLE)) 344 gtk_tree_view_column_set_sort_column_id(col, index); 345 gtk_tree_view_column_set_resizable(col, TRUE); 346 gtk_tree_view_column_pack_start(col, renderer, TRUE); 347 gtk_tree_view_column_add_attribute(col, renderer, "text", index); 348 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL); 349 switch (align) { 350 case PANGO_ALIGN_LEFT: 351 xalign = 0.0; 352 break; 353 case PANGO_ALIGN_CENTER: 354 xalign = 0.5; 355 break; 356 case PANGO_ALIGN_RIGHT: 357 xalign = 1.0; 358 break; 359 } 360 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5); 361 gtk_tree_view_column_set_visible(col, visible); 362 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col); 363 return col; 364} 365 366static void gfio_ui_setup_log(struct gui *ui) 367{ 368 GtkTreeSelection *selection; 369 GtkListStore *model; 370 GtkWidget *tree_view; 371 372 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING); 373 374 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); 375 gtk_widget_set_can_focus(tree_view, FALSE); 376 377 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); 378 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE); 379 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE, 380 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL); 381 382 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE); 383 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE); 384 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE); 385 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE); 386 387 ui->log_model = model; 388 ui->log_tree = tree_view; 389} 390 391static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals, 392 fio_fp64_t *plist, 393 unsigned int len, 394 const char *base, 395 unsigned int scale) 396{ 397 GType types[FIO_IO_U_LIST_MAX_LEN]; 398 GtkWidget *tree_view; 399 GtkTreeSelection *selection; 400 GtkListStore *model; 401 GtkTreeIter iter; 402 int i; 403 404 for (i = 0; i < len; i++) 405 types[i] = G_TYPE_INT; 406 407 model = gtk_list_store_newv(len, types); 408 409 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); 410 gtk_widget_set_can_focus(tree_view, FALSE); 411 412 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE, 413 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL); 414 415 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); 416 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE); 417 418 for (i = 0; i < len; i++) { 419 char fbuf[8]; 420 421 sprintf(fbuf, "%2.2f%%", plist[i].u.f); 422 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE); 423 } 424 425 gtk_list_store_append(model, &iter); 426 427 for (i = 0; i < len; i++) { 428 if (scale) 429 ovals[i] = (ovals[i] + 999) / 1000; 430 gtk_list_store_set(model, &iter, i, ovals[i], -1); 431 } 432 433 return tree_view; 434} 435 436static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts, 437 int ddir) 438{ 439 unsigned int *io_u_plat = ts->io_u_plat[ddir]; 440 unsigned long nr = ts->clat_stat[ddir].samples; 441 fio_fp64_t *plist = ts->percentile_list; 442 unsigned int *ovals, len, minv, maxv, scale_down; 443 const char *base; 444 GtkWidget *tree_view, *frame, *hbox; 445 char tmp[64]; 446 447 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv); 448 if (!len) 449 goto out; 450 451 /* 452 * We default to usecs, but if the value range is such that we 453 * should scale down to msecs, do that. 454 */ 455 if (minv > 2000 && maxv > 99999) { 456 scale_down = 1; 457 base = "msec"; 458 } else { 459 scale_down = 0; 460 base = "usec"; 461 } 462 463 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down); 464 465 sprintf(tmp, "Completion percentiles (%s)", base); 466 frame = gtk_frame_new(tmp); 467 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); 468 469 hbox = gtk_hbox_new(FALSE, 3); 470 gtk_container_add(GTK_CONTAINER(frame), hbox); 471 472 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3); 473out: 474 if (ovals) 475 free(ovals); 476} 477 478static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min, 479 unsigned long max, double mean, double dev) 480{ 481 const char *base = "(usec)"; 482 GtkWidget *hbox, *label, *frame; 483 char *minp, *maxp; 484 char tmp[64]; 485 486 if (!usec_to_msec(&min, &max, &mean, &dev)) 487 base = "(msec)"; 488 489 minp = num2str(min, 6, 1, 0); 490 maxp = num2str(max, 6, 1, 0); 491 492 sprintf(tmp, "%s %s", name, base); 493 frame = gtk_frame_new(tmp); 494 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); 495 496 hbox = gtk_hbox_new(FALSE, 3); 497 gtk_container_add(GTK_CONTAINER(frame), hbox); 498 499 label = new_info_label_in_frame(hbox, "Minimum"); 500 gtk_label_set_text(GTK_LABEL(label), minp); 501 label = new_info_label_in_frame(hbox, "Maximum"); 502 gtk_label_set_text(GTK_LABEL(label), maxp); 503 label = new_info_label_in_frame(hbox, "Average"); 504 sprintf(tmp, "%5.02f", mean); 505 gtk_label_set_text(GTK_LABEL(label), tmp); 506 label = new_info_label_in_frame(hbox, "Standard deviation"); 507 sprintf(tmp, "%5.02f", dev); 508 gtk_label_set_text(GTK_LABEL(label), tmp); 509 510 free(minp); 511 free(maxp); 512 513} 514 515#define GFIO_CLAT 1 516#define GFIO_SLAT 2 517#define GFIO_LAT 4 518 519static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs, 520 struct thread_stat *ts, int ddir) 521{ 522 const char *ddir_label[2] = { "Read", "Write" }; 523 GtkWidget *frame, *label, *box, *vbox, *main_vbox; 524 unsigned long min[3], max[3], runt; 525 unsigned long long bw, iops; 526 unsigned int flags = 0; 527 double mean[3], dev[3]; 528 char *io_p, *bw_p, *iops_p; 529 int i2p; 530 531 if (!ts->runtime[ddir]) 532 return; 533 534 i2p = is_power_of_2(rs->kb_base); 535 runt = ts->runtime[ddir]; 536 537 bw = (1000 * ts->io_bytes[ddir]) / runt; 538 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p); 539 bw_p = num2str(bw, 6, 1, i2p); 540 541 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt; 542 iops_p = num2str(iops, 6, 1, 0); 543 544 box = gtk_hbox_new(FALSE, 3); 545 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3); 546 547 frame = gtk_frame_new(ddir_label[ddir]); 548 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5); 549 550 main_vbox = gtk_vbox_new(FALSE, 3); 551 gtk_container_add(GTK_CONTAINER(frame), main_vbox); 552 553 box = gtk_hbox_new(FALSE, 3); 554 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3); 555 556 label = new_info_label_in_frame(box, "IO"); 557 gtk_label_set_text(GTK_LABEL(label), io_p); 558 label = new_info_label_in_frame(box, "Bandwidth"); 559 gtk_label_set_text(GTK_LABEL(label), bw_p); 560 label = new_info_label_in_frame(box, "IOPS"); 561 gtk_label_set_text(GTK_LABEL(label), iops_p); 562 label = new_info_label_in_frame(box, "Runtime (msec)"); 563 label_set_int_value(label, ts->runtime[ddir]); 564 565 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) { 566 double p_of_agg = 100.0; 567 const char *bw_str = "KB"; 568 char tmp[32]; 569 570 if (rs->agg[ddir]) { 571 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir]; 572 if (p_of_agg > 100.0) 573 p_of_agg = 100.0; 574 } 575 576 if (mean[0] > 999999.9) { 577 min[0] /= 1000.0; 578 max[0] /= 1000.0; 579 mean[0] /= 1000.0; 580 dev[0] /= 1000.0; 581 bw_str = "MB"; 582 } 583 584 sprintf(tmp, "Bandwidth (%s)", bw_str); 585 frame = gtk_frame_new(tmp); 586 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5); 587 588 box = gtk_hbox_new(FALSE, 3); 589 gtk_container_add(GTK_CONTAINER(frame), box); 590 591 label = new_info_label_in_frame(box, "Minimum"); 592 label_set_int_value(label, min[0]); 593 label = new_info_label_in_frame(box, "Maximum"); 594 label_set_int_value(label, max[0]); 595 label = new_info_label_in_frame(box, "Percentage of jobs"); 596 sprintf(tmp, "%3.2f%%", p_of_agg); 597 gtk_label_set_text(GTK_LABEL(label), tmp); 598 label = new_info_label_in_frame(box, "Average"); 599 sprintf(tmp, "%5.02f", mean[0]); 600 gtk_label_set_text(GTK_LABEL(label), tmp); 601 label = new_info_label_in_frame(box, "Standard deviation"); 602 sprintf(tmp, "%5.02f", dev[0]); 603 gtk_label_set_text(GTK_LABEL(label), tmp); 604 } 605 606 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) 607 flags |= GFIO_SLAT; 608 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1])) 609 flags |= GFIO_CLAT; 610 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2])) 611 flags |= GFIO_LAT; 612 613 if (flags) { 614 frame = gtk_frame_new("Latency"); 615 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5); 616 617 vbox = gtk_vbox_new(FALSE, 3); 618 gtk_container_add(GTK_CONTAINER(frame), vbox); 619 620 if (flags & GFIO_SLAT) 621 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]); 622 if (flags & GFIO_CLAT) 623 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]); 624 if (flags & GFIO_LAT) 625 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]); 626 } 627 628 if (ts->clat_percentiles) 629 gfio_show_clat_percentiles(main_vbox, ts, ddir); 630 631 632 free(io_p); 633 free(bw_p); 634 free(iops_p); 635} 636 637static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num, 638 const char **labels) 639{ 640 GtkWidget *tree_view; 641 GtkTreeSelection *selection; 642 GtkListStore *model; 643 GtkTreeIter iter; 644 GType *types; 645 int i, skipped; 646 647 /* 648 * Check if all are empty, in which case don't bother 649 */ 650 for (i = 0, skipped = 0; i < num; i++) 651 if (lat[i] <= 0.0) 652 skipped++; 653 654 if (skipped == num) 655 return NULL; 656 657 types = malloc(num * sizeof(GType)); 658 659 for (i = 0; i < num; i++) 660 types[i] = G_TYPE_STRING; 661 662 model = gtk_list_store_newv(num, types); 663 free(types); 664 types = NULL; 665 666 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); 667 gtk_widget_set_can_focus(tree_view, FALSE); 668 669 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE, 670 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL); 671 672 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); 673 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE); 674 675 for (i = 0; i < num; i++) 676 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE); 677 678 gtk_list_store_append(model, &iter); 679 680 for (i = 0; i < num; i++) { 681 char fbuf[32]; 682 683 if (lat[i] <= 0.0) 684 sprintf(fbuf, "0.00"); 685 else 686 sprintf(fbuf, "%3.2f%%", lat[i]); 687 688 gtk_list_store_set(model, &iter, i, fbuf, -1); 689 } 690 691 return tree_view; 692} 693 694static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts) 695{ 696 GtkWidget *box, *frame, *tree_view; 697 double io_u_lat_u[FIO_IO_U_LAT_U_NR]; 698 double io_u_lat_m[FIO_IO_U_LAT_M_NR]; 699 const char *uranges[] = { "2", "4", "10", "20", "50", "100", 700 "250", "500", "750", "1000", }; 701 const char *mranges[] = { "2", "4", "10", "20", "50", "100", 702 "250", "500", "750", "1000", "2000", 703 ">= 2000", }; 704 705 stat_calc_lat_u(ts, io_u_lat_u); 706 stat_calc_lat_m(ts, io_u_lat_m); 707 708 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges); 709 if (tree_view) { 710 frame = gtk_frame_new("Latency buckets (usec)"); 711 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); 712 713 box = gtk_hbox_new(FALSE, 3); 714 gtk_container_add(GTK_CONTAINER(frame), box); 715 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3); 716 } 717 718 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges); 719 if (tree_view) { 720 frame = gtk_frame_new("Latency buckets (msec)"); 721 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); 722 723 box = gtk_hbox_new(FALSE, 3); 724 gtk_container_add(GTK_CONTAINER(frame), box); 725 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3); 726 } 727} 728 729static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts) 730{ 731 GtkWidget *box, *frame, *entry; 732 double usr_cpu, sys_cpu; 733 unsigned long runtime; 734 char tmp[32]; 735 736 runtime = ts->total_run_time; 737 if (runtime) { 738 double runt = (double) runtime; 739 740 usr_cpu = (double) ts->usr_time * 100 / runt; 741 sys_cpu = (double) ts->sys_time * 100 / runt; 742 } else { 743 usr_cpu = 0; 744 sys_cpu = 0; 745 } 746 747 frame = gtk_frame_new("OS resources"); 748 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); 749 750 box = gtk_hbox_new(FALSE, 3); 751 gtk_container_add(GTK_CONTAINER(frame), box); 752 753 entry = new_info_entry_in_frame(box, "User CPU"); 754 sprintf(tmp, "%3.2f%%", usr_cpu); 755 gtk_entry_set_text(GTK_ENTRY(entry), tmp); 756 entry = new_info_entry_in_frame(box, "System CPU"); 757 sprintf(tmp, "%3.2f%%", sys_cpu); 758 gtk_entry_set_text(GTK_ENTRY(entry), tmp); 759 entry = new_info_entry_in_frame(box, "Context switches"); 760 entry_set_int_value(entry, ts->ctx); 761 entry = new_info_entry_in_frame(box, "Major faults"); 762 entry_set_int_value(entry, ts->majf); 763 entry = new_info_entry_in_frame(box, "Minor faults"); 764 entry_set_int_value(entry, ts->minf); 765} 766static void gfio_add_sc_depths_tree(GtkListStore *model, 767 struct thread_stat *ts, unsigned int len, 768 int submit) 769{ 770 double io_u_dist[FIO_IO_U_MAP_NR]; 771 GtkTreeIter iter; 772 /* Bits 0, and 3-8 */ 773 const int add_mask = 0x1f9; 774 int i, j; 775 776 if (submit) 777 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist); 778 else 779 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist); 780 781 gtk_list_store_append(model, &iter); 782 783 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1); 784 785 for (i = 1, j = 0; i < len; i++) { 786 char fbuf[32]; 787 788 if (!(add_mask & (1UL << (i - 1)))) 789 sprintf(fbuf, "0.0%%"); 790 else { 791 sprintf(fbuf, "%3.1f%%", io_u_dist[j]); 792 j++; 793 } 794 795 gtk_list_store_set(model, &iter, i, fbuf, -1); 796 } 797 798} 799 800static void gfio_add_total_depths_tree(GtkListStore *model, 801 struct thread_stat *ts, unsigned int len) 802{ 803 double io_u_dist[FIO_IO_U_MAP_NR]; 804 GtkTreeIter iter; 805 /* Bits 1-6, and 8 */ 806 const int add_mask = 0x17e; 807 int i, j; 808 809 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist); 810 811 gtk_list_store_append(model, &iter); 812 813 gtk_list_store_set(model, &iter, 0, "Total", -1); 814 815 for (i = 1, j = 0; i < len; i++) { 816 char fbuf[32]; 817 818 if (!(add_mask & (1UL << (i - 1)))) 819 sprintf(fbuf, "0.0%%"); 820 else { 821 sprintf(fbuf, "%3.1f%%", io_u_dist[j]); 822 j++; 823 } 824 825 gtk_list_store_set(model, &iter, i, fbuf, -1); 826 } 827 828} 829 830static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts) 831{ 832 GtkWidget *frame, *box, *tree_view; 833 GtkTreeSelection *selection; 834 GtkListStore *model; 835 GType types[FIO_IO_U_MAP_NR + 1]; 836 int i; 837#define NR_LABELS 10 838 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" }; 839 840 frame = gtk_frame_new("IO depths"); 841 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); 842 843 box = gtk_hbox_new(FALSE, 3); 844 gtk_container_add(GTK_CONTAINER(frame), box); 845 846 for (i = 0; i < NR_LABELS; i++) 847 types[i] = G_TYPE_STRING; 848 849 model = gtk_list_store_newv(NR_LABELS, types); 850 851 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); 852 gtk_widget_set_can_focus(tree_view, FALSE); 853 854 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE, 855 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL); 856 857 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); 858 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE); 859 860 for (i = 0; i < NR_LABELS; i++) 861 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE); 862 863 gfio_add_total_depths_tree(model, ts, NR_LABELS); 864 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1); 865 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0); 866 867 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3); 868} 869 870static gboolean results_window_delete(GtkWidget *w, gpointer data) 871{ 872 struct gui_entry *ge = (struct gui_entry *) data; 873 874 gtk_widget_destroy(w); 875 ge->results_window = NULL; 876 ge->results_notebook = NULL; 877 return TRUE; 878} 879 880static GtkWidget *get_results_window(struct gui_entry *ge) 881{ 882 GtkWidget *win, *notebook; 883 884 if (ge->results_window) 885 return ge->results_notebook; 886 887 win = gtk_window_new(GTK_WINDOW_TOPLEVEL); 888 gtk_window_set_title(GTK_WINDOW(win), "Results"); 889 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768); 890 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge); 891 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge); 892 893 notebook = gtk_notebook_new(); 894 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1); 895 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook)); 896 gtk_container_add(GTK_CONTAINER(win), notebook); 897 898 ge->results_window = win; 899 ge->results_notebook = notebook; 900 return ge->results_notebook; 901} 902 903static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts, 904 struct group_run_stats *rs) 905{ 906 GtkWidget *res_win, *box, *vbox, *entry, *scroll; 907 struct gfio_client *gc = client->client_data; 908 909 gdk_threads_enter(); 910 911 res_win = get_results_window(gc->ge); 912 913 scroll = gtk_scrolled_window_new(NULL, NULL); 914 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5); 915 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); 916 917 vbox = gtk_vbox_new(FALSE, 3); 918 919 box = gtk_hbox_new(FALSE, 0); 920 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5); 921 922 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox); 923 924 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name)); 925 926 gc->results_widget = vbox; 927 928 entry = new_info_entry_in_frame(box, "Name"); 929 gtk_entry_set_text(GTK_ENTRY(entry), ts->name); 930 if (strlen(ts->description)) { 931 entry = new_info_entry_in_frame(box, "Description"); 932 gtk_entry_set_text(GTK_ENTRY(entry), ts->description); 933 } 934 entry = new_info_entry_in_frame(box, "Group ID"); 935 entry_set_int_value(entry, ts->groupid); 936 entry = new_info_entry_in_frame(box, "Jobs"); 937 entry_set_int_value(entry, ts->members); 938 gc->err_entry = entry = new_info_entry_in_frame(box, "Error"); 939 entry_set_int_value(entry, ts->error); 940 entry = new_info_entry_in_frame(box, "PID"); 941 entry_set_int_value(entry, ts->pid); 942 943 if (ts->io_bytes[DDIR_READ]) 944 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ); 945 if (ts->io_bytes[DDIR_WRITE]) 946 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE); 947 948 gfio_show_latency_buckets(vbox, ts); 949 gfio_show_cpu_usage(vbox, ts); 950 gfio_show_io_depths(vbox, ts); 951 952 gtk_widget_show_all(gc->ge->results_window); 953 gdk_threads_leave(); 954} 955 956static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd) 957{ 958 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload; 959 struct gui *ui = &main_ui; 960 GtkTreeIter iter; 961 struct tm *tm; 962 time_t sec; 963 char tmp[64], timebuf[80]; 964 965 sec = p->log_sec; 966 tm = localtime(&sec); 967 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm); 968 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000); 969 970 gdk_threads_enter(); 971 972 gtk_list_store_append(ui->log_model, &iter); 973 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1); 974 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1); 975 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1); 976 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1); 977 978 if (p->level == FIO_LOG_ERR) 979 view_log(NULL, (gpointer) ui); 980 981 gdk_threads_leave(); 982} 983 984static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd) 985{ 986 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload; 987 struct gfio_client *gc = client->client_data; 988 GtkWidget *box, *frame, *entry, *vbox; 989 double util; 990 char tmp[16]; 991 992 gdk_threads_enter(); 993 994 if (!gc->results_widget) 995 goto out; 996 997 if (!gc->disk_util_frame) { 998 gc->disk_util_frame = gtk_frame_new("Disk utilization"); 999 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5); 1000 } 1001 1002 vbox = gtk_vbox_new(FALSE, 3); 1003 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox); 1004 1005 frame = gtk_frame_new((char *) p->dus.name); 1006 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2); 1007 1008 box = gtk_vbox_new(FALSE, 3); 1009 gtk_container_add(GTK_CONTAINER(frame), box); 1010 1011 frame = gtk_frame_new("Read"); 1012 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2); 1013 vbox = gtk_hbox_new(TRUE, 3); 1014 gtk_container_add(GTK_CONTAINER(frame), vbox); 1015 entry = new_info_entry_in_frame(vbox, "IOs"); 1016 entry_set_int_value(entry, p->dus.ios[0]); 1017 entry = new_info_entry_in_frame(vbox, "Merges"); 1018 entry_set_int_value(entry, p->dus.merges[0]); 1019 entry = new_info_entry_in_frame(vbox, "Sectors"); 1020 entry_set_int_value(entry, p->dus.sectors[0]); 1021 entry = new_info_entry_in_frame(vbox, "Ticks"); 1022 entry_set_int_value(entry, p->dus.ticks[0]); 1023 1024 frame = gtk_frame_new("Write"); 1025 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2); 1026 vbox = gtk_hbox_new(TRUE, 3); 1027 gtk_container_add(GTK_CONTAINER(frame), vbox); 1028 entry = new_info_entry_in_frame(vbox, "IOs"); 1029 entry_set_int_value(entry, p->dus.ios[1]); 1030 entry = new_info_entry_in_frame(vbox, "Merges"); 1031 entry_set_int_value(entry, p->dus.merges[1]); 1032 entry = new_info_entry_in_frame(vbox, "Sectors"); 1033 entry_set_int_value(entry, p->dus.sectors[1]); 1034 entry = new_info_entry_in_frame(vbox, "Ticks"); 1035 entry_set_int_value(entry, p->dus.ticks[1]); 1036 1037 frame = gtk_frame_new("Shared"); 1038 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2); 1039 vbox = gtk_hbox_new(TRUE, 3); 1040 gtk_container_add(GTK_CONTAINER(frame), vbox); 1041 entry = new_info_entry_in_frame(vbox, "IO ticks"); 1042 entry_set_int_value(entry, p->dus.io_ticks); 1043 entry = new_info_entry_in_frame(vbox, "Time in queue"); 1044 entry_set_int_value(entry, p->dus.time_in_queue); 1045 1046 util = 0.0; 1047 if (p->dus.msec) 1048 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec; 1049 if (util > 100.0) 1050 util = 100.0; 1051 1052 sprintf(tmp, "%3.2f%%", util); 1053 entry = new_info_entry_in_frame(vbox, "Disk utilization"); 1054 gtk_entry_set_text(GTK_ENTRY(entry), tmp); 1055 1056 gtk_widget_show_all(gc->results_widget); 1057out: 1058 gdk_threads_leave(); 1059} 1060 1061extern int sum_stat_clients; 1062extern struct thread_stat client_ts; 1063extern struct group_run_stats client_gs; 1064 1065static int sum_stat_nr; 1066 1067static void gfio_thread_status_op(struct fio_client *client, 1068 struct fio_net_cmd *cmd) 1069{ 1070 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload; 1071 1072 gfio_display_ts(client, &p->ts, &p->rs); 1073 1074 if (sum_stat_clients == 1) 1075 return; 1076 1077 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr); 1078 sum_group_stats(&client_gs, &p->rs); 1079 1080 client_ts.members++; 1081 client_ts.groupid = p->ts.groupid; 1082 1083 if (++sum_stat_nr == sum_stat_clients) { 1084 strcpy(client_ts.name, "All clients"); 1085 gfio_display_ts(client, &client_ts, &client_gs); 1086 } 1087} 1088 1089static void gfio_group_stats_op(struct fio_client *client, 1090 struct fio_net_cmd *cmd) 1091{ 1092 /* We're ignoring group stats for now */ 1093} 1094 1095static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event, 1096 gpointer data) 1097{ 1098 struct gfio_graphs *g = data; 1099 1100 g->drawing_area_xdim = w->allocation.width; 1101 g->drawing_area_ydim = w->allocation.height; 1102 return TRUE; 1103} 1104 1105static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p) 1106{ 1107 struct gfio_graphs *g = p; 1108 cairo_t *cr; 1109 1110 graph_set_size(g->iops_graph, g->drawing_area_xdim / 2.0, 1111 g->drawing_area_ydim); 1112 graph_set_size(g->bandwidth_graph, g->drawing_area_xdim / 2.0, 1113 g->drawing_area_ydim); 1114 cr = gdk_cairo_create(w->window); 1115 1116 cairo_set_source_rgb(cr, 0, 0, 0); 1117 1118 cairo_save(cr); 1119 cairo_translate(cr, 0, 0); 1120 line_graph_draw(g->bandwidth_graph, cr); 1121 cairo_stroke(cr); 1122 cairo_restore(cr); 1123 1124 cairo_save(cr); 1125 cairo_translate(cr, g->drawing_area_xdim / 2.0, 0); 1126 line_graph_draw(g->iops_graph, cr); 1127 cairo_stroke(cr); 1128 cairo_restore(cr); 1129 cairo_destroy(cr); 1130 1131 return FALSE; 1132} 1133 1134/* 1135 * Client specific ETA 1136 */ 1137static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je) 1138{ 1139 struct gfio_client *gc = client->client_data; 1140 struct gui_entry *ge = gc->ge; 1141 static int eta_good; 1142 char eta_str[128]; 1143 char output[256]; 1144 char tmp[32]; 1145 double perc = 0.0; 1146 int i2p = 0; 1147 1148 gdk_threads_enter(); 1149 1150 eta_str[0] = '\0'; 1151 output[0] = '\0'; 1152 1153 if (je->eta_sec != INT_MAX && je->elapsed_sec) { 1154 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec); 1155 eta_to_str(eta_str, je->eta_sec); 1156 } 1157 1158 sprintf(tmp, "%u", je->nr_running); 1159 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp); 1160 sprintf(tmp, "%u", je->files_open); 1161 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp); 1162 1163#if 0 1164 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) { 1165 if (je->m_rate || je->t_rate) { 1166 char *tr, *mr; 1167 1168 mr = num2str(je->m_rate, 4, 0, i2p); 1169 tr = num2str(je->t_rate, 4, 0, i2p); 1170 gtk_entry_set_text(GTK_ENTRY(ge->eta); 1171 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr); 1172 free(tr); 1173 free(mr); 1174 } else if (je->m_iops || je->t_iops) 1175 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops); 1176 1177 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---"); 1178 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---"); 1179 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---"); 1180 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---"); 1181#endif 1182 1183 if (je->eta_sec != INT_MAX && je->nr_running) { 1184 char *iops_str[2]; 1185 char *rate_str[2]; 1186 1187 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running) 1188 strcpy(output, "-.-% done"); 1189 else { 1190 eta_good = 1; 1191 perc *= 100.0; 1192 sprintf(output, "%3.1f%% done", perc); 1193 } 1194 1195 rate_str[0] = num2str(je->rate[0], 5, 10, i2p); 1196 rate_str[1] = num2str(je->rate[1], 5, 10, i2p); 1197 1198 iops_str[0] = num2str(je->iops[0], 4, 1, 0); 1199 iops_str[1] = num2str(je->iops[1], 4, 1, 0); 1200 1201 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]); 1202 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]); 1203 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]); 1204 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]); 1205 1206 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]); 1207 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]); 1208 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]); 1209 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]); 1210 1211 free(rate_str[0]); 1212 free(rate_str[1]); 1213 free(iops_str[0]); 1214 free(iops_str[1]); 1215 } 1216 1217 if (eta_str[0]) { 1218 char *dst = output + strlen(output); 1219 1220 sprintf(dst, " - %s", eta_str); 1221 } 1222 1223 gfio_update_thread_status(ge, output, perc); 1224 gdk_threads_leave(); 1225} 1226 1227/* 1228 * Update ETA in main window for all clients 1229 */ 1230static void gfio_update_all_eta(struct jobs_eta *je) 1231{ 1232 struct gui *ui = &main_ui; 1233 static int eta_good; 1234 char eta_str[128]; 1235 char output[256]; 1236 double perc = 0.0; 1237 int i2p = 0; 1238 1239 gdk_threads_enter(); 1240 1241 eta_str[0] = '\0'; 1242 output[0] = '\0'; 1243 1244 if (je->eta_sec != INT_MAX && je->elapsed_sec) { 1245 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec); 1246 eta_to_str(eta_str, je->eta_sec); 1247 } 1248 1249#if 0 1250 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) { 1251 if (je->m_rate || je->t_rate) { 1252 char *tr, *mr; 1253 1254 mr = num2str(je->m_rate, 4, 0, i2p); 1255 tr = num2str(je->t_rate, 4, 0, i2p); 1256 gtk_entry_set_text(GTK_ENTRY(ui->eta); 1257 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr); 1258 free(tr); 1259 free(mr); 1260 } else if (je->m_iops || je->t_iops) 1261 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops); 1262 1263 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---"); 1264 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---"); 1265 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---"); 1266 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---"); 1267#endif 1268 1269 entry_set_int_value(ui->eta.jobs, je->nr_running); 1270 1271 if (je->eta_sec != INT_MAX && je->nr_running) { 1272 char *iops_str[2]; 1273 char *rate_str[2]; 1274 1275 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running) 1276 strcpy(output, "-.-% done"); 1277 else { 1278 eta_good = 1; 1279 perc *= 100.0; 1280 sprintf(output, "%3.1f%% done", perc); 1281 } 1282 1283 rate_str[0] = num2str(je->rate[0], 5, 10, i2p); 1284 rate_str[1] = num2str(je->rate[1], 5, 10, i2p); 1285 1286 iops_str[0] = num2str(je->iops[0], 4, 1, 0); 1287 iops_str[1] = num2str(je->iops[1], 4, 1, 0); 1288 1289 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]); 1290 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]); 1291 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]); 1292 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]); 1293 1294 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]); 1295 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]); 1296 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]); 1297 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]); 1298 1299 free(rate_str[0]); 1300 free(rate_str[1]); 1301 free(iops_str[0]); 1302 free(iops_str[1]); 1303 } 1304 1305 if (eta_str[0]) { 1306 char *dst = output + strlen(output); 1307 1308 sprintf(dst, " - %s", eta_str); 1309 } 1310 1311 gfio_update_thread_status_all(output, perc); 1312 gdk_threads_leave(); 1313} 1314 1315static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd) 1316{ 1317 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload; 1318 struct gfio_client *gc = client->client_data; 1319 struct gui_entry *ge = gc->ge; 1320 const char *os, *arch; 1321 char buf[64]; 1322 1323 os = fio_get_os_string(probe->os); 1324 if (!os) 1325 os = "unknown"; 1326 1327 arch = fio_get_arch_string(probe->arch); 1328 if (!arch) 1329 os = "unknown"; 1330 1331 if (!client->name) 1332 client->name = strdup((char *) probe->hostname); 1333 1334 gdk_threads_enter(); 1335 1336 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname); 1337 gtk_label_set_text(GTK_LABEL(ge->probe.os), os); 1338 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch); 1339 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch); 1340 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf); 1341 1342 gfio_set_connected(ge, 1); 1343 1344 gdk_threads_leave(); 1345} 1346 1347static void gfio_update_thread_status(struct gui_entry *ge, 1348 char *status_message, double perc) 1349{ 1350 static char message[100]; 1351 const char *m = message; 1352 1353 strncpy(message, status_message, sizeof(message) - 1); 1354 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m); 1355 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0); 1356 gtk_widget_queue_draw(main_ui.window); 1357} 1358 1359static void gfio_update_thread_status_all(char *status_message, double perc) 1360{ 1361 struct gui *ui = &main_ui; 1362 static char message[100]; 1363 const char *m = message; 1364 1365 strncpy(message, status_message, sizeof(message) - 1); 1366 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m); 1367 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0); 1368 gtk_widget_queue_draw(ui->window); 1369} 1370 1371static void gfio_quit_op(struct fio_client *client) 1372{ 1373 struct gfio_client *gc = client->client_data; 1374 1375 gdk_threads_enter(); 1376 gfio_set_connected(gc->ge, 0); 1377 gdk_threads_leave(); 1378} 1379 1380static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd) 1381{ 1382 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload; 1383 struct gfio_client *gc = client->client_data; 1384 struct thread_options *o = &gc->o; 1385 struct gui_entry *ge = gc->ge; 1386 char tmp[8]; 1387 1388 convert_thread_options_to_cpu(o, &p->top); 1389 1390 gdk_threads_enter(); 1391 1392 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name); 1393 1394 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name); 1395 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0); 1396 1397 gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), ddir_str(o->td_ddir)); 1398 gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), (gchar *) o->ioengine); 1399 1400 sprintf(tmp, "%u", o->iodepth); 1401 gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), tmp); 1402 1403 gc->job_added++; 1404 1405 gdk_threads_leave(); 1406} 1407 1408static void gfio_client_timed_out(struct fio_client *client) 1409{ 1410 struct gfio_client *gc = client->client_data; 1411 GtkWidget *dialog, *label, *content; 1412 char buf[256]; 1413 1414 gdk_threads_enter(); 1415 1416 gfio_set_connected(gc->ge, 0); 1417 clear_ge_ui_info(gc->ge); 1418 1419 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname); 1420 1421 dialog = gtk_dialog_new_with_buttons("Timed out!", 1422 GTK_WINDOW(main_ui.window), 1423 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, 1424 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); 1425 1426 /* gtk_dialog_get_content_area() is 2.14 and newer */ 1427 content = GTK_DIALOG(dialog)->vbox; 1428 1429 label = gtk_label_new((const gchar *) buf); 1430 gtk_container_add(GTK_CONTAINER(content), label); 1431 gtk_widget_show_all(dialog); 1432 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT); 1433 1434 gtk_dialog_run(GTK_DIALOG(dialog)); 1435 gtk_widget_destroy(dialog); 1436 1437 gdk_threads_leave(); 1438} 1439 1440static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd) 1441{ 1442 struct gfio_client *gc = client->client_data; 1443 1444 gdk_threads_enter(); 1445 1446 gfio_set_connected(gc->ge, 0); 1447 1448 if (gc->err_entry) 1449 entry_set_int_value(gc->err_entry, client->error); 1450 1451 gdk_threads_leave(); 1452} 1453 1454struct client_ops gfio_client_ops = { 1455 .text_op = gfio_text_op, 1456 .disk_util = gfio_disk_util_op, 1457 .thread_status = gfio_thread_status_op, 1458 .group_stats = gfio_group_stats_op, 1459 .jobs_eta = gfio_update_client_eta, 1460 .eta = gfio_update_all_eta, 1461 .probe = gfio_probe_op, 1462 .quit = gfio_quit_op, 1463 .add_job = gfio_add_job_op, 1464 .timed_out = gfio_client_timed_out, 1465 .stop = gfio_client_stop, 1466 .stay_connected = 1, 1467}; 1468 1469static void quit_clicked(__attribute__((unused)) GtkWidget *widget, 1470 __attribute__((unused)) gpointer data) 1471{ 1472 gtk_main_quit(); 1473} 1474 1475static void *job_thread(void *arg) 1476{ 1477 struct gui *ui = arg; 1478 1479 ui->handler_running = 1; 1480 fio_handle_clients(&gfio_client_ops); 1481 ui->handler_running = 0; 1482 return NULL; 1483} 1484 1485static int send_job_files(struct gui_entry *ge) 1486{ 1487 struct gfio_client *gc = ge->client; 1488 int i, ret = 0; 1489 1490 for (i = 0; i < ge->nr_job_files; i++) { 1491 ret = fio_client_send_ini(gc->client, ge->job_files[i]); 1492 if (ret < 0) { 1493 GError *error; 1494 1495 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret)); 1496 report_error(error); 1497 g_error_free(error); 1498 break; 1499 } else if (ret) 1500 break; 1501 1502 free(ge->job_files[i]); 1503 ge->job_files[i] = NULL; 1504 } 1505 while (i < ge->nr_job_files) { 1506 free(ge->job_files[i]); 1507 ge->job_files[i] = NULL; 1508 i++; 1509 } 1510 1511 return ret; 1512} 1513 1514static void *server_thread(void *arg) 1515{ 1516 is_backend = 1; 1517 gfio_server_running = 1; 1518 fio_start_server(NULL); 1519 gfio_server_running = 0; 1520 return NULL; 1521} 1522 1523static void gfio_start_server(void) 1524{ 1525 struct gui *ui = &main_ui; 1526 1527 if (!gfio_server_running) { 1528 gfio_server_running = 1; 1529 pthread_create(&ui->server_t, NULL, server_thread, NULL); 1530 pthread_detach(ui->server_t); 1531 } 1532} 1533 1534static void start_job_clicked(__attribute__((unused)) GtkWidget *widget, 1535 gpointer data) 1536{ 1537 struct gui_entry *ge = data; 1538 struct gfio_client *gc = ge->client; 1539 1540 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0); 1541 fio_start_client(gc->client); 1542} 1543 1544static void file_open(GtkWidget *w, gpointer data); 1545 1546static void connect_clicked(GtkWidget *widget, gpointer data) 1547{ 1548 struct gui_entry *ge = data; 1549 struct gfio_client *gc = ge->client; 1550 1551 if (!ge->connected) { 1552 int ret; 1553 1554 if (!ge->nr_job_files) 1555 file_open(widget, data); 1556 if (!ge->nr_job_files) 1557 return; 1558 1559 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running"); 1560 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0); 1561 ret = fio_client_connect(gc->client); 1562 if (!ret) { 1563 if (!ge->ui->handler_running) 1564 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui); 1565 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 0); 1566 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1); 1567 } else { 1568 GError *error; 1569 1570 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret)); 1571 report_error(error); 1572 g_error_free(error); 1573 } 1574 } else { 1575 fio_client_terminate(gc->client); 1576 gfio_set_connected(ge, 0); 1577 clear_ge_ui_info(ge); 1578 } 1579} 1580 1581static void send_clicked(GtkWidget *widget, gpointer data) 1582{ 1583 struct gui_entry *ge = data; 1584 1585 if (send_job_files(ge)) { 1586 GError *error; 1587 1588 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send one or more job files for client %s", ge->client->client->hostname); 1589 report_error(error); 1590 g_error_free(error); 1591 1592 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1); 1593 } 1594 1595 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0); 1596 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1); 1597} 1598 1599static GtkWidget *add_button(GtkWidget *buttonbox, 1600 struct button_spec *buttonspec, gpointer data) 1601{ 1602 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext); 1603 1604 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data); 1605 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3); 1606 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext); 1607 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive); 1608 1609 return button; 1610} 1611 1612static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist, 1613 int nbuttons) 1614{ 1615 int i; 1616 1617 for (i = 0; i < nbuttons; i++) 1618 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge); 1619} 1620 1621static void on_info_bar_response(GtkWidget *widget, gint response, 1622 gpointer data) 1623{ 1624 struct gui *ui = &main_ui; 1625 1626 if (response == GTK_RESPONSE_OK) { 1627 gtk_widget_destroy(widget); 1628 ui->error_info_bar = NULL; 1629 } 1630} 1631 1632void report_error(GError *error) 1633{ 1634 struct gui *ui = &main_ui; 1635 1636 if (ui->error_info_bar == NULL) { 1637 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK, 1638 GTK_RESPONSE_OK, 1639 NULL); 1640 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL); 1641 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar), 1642 GTK_MESSAGE_ERROR); 1643 1644 ui->error_label = gtk_label_new(error->message); 1645 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar)); 1646 gtk_container_add(GTK_CONTAINER(container), ui->error_label); 1647 1648 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0); 1649 gtk_widget_show_all(ui->vbox); 1650 } else { 1651 char buffer[256]; 1652 snprintf(buffer, sizeof(buffer), "Failed to open file."); 1653 gtk_label_set(GTK_LABEL(ui->error_label), buffer); 1654 } 1655} 1656 1657struct connection_widgets 1658{ 1659 GtkWidget *hentry; 1660 GtkWidget *combo; 1661 GtkWidget *button; 1662}; 1663 1664static void hostname_cb(GtkEntry *entry, gpointer data) 1665{ 1666 struct connection_widgets *cw = data; 1667 int uses_net = 0, is_localhost = 0; 1668 const gchar *text; 1669 gchar *ctext; 1670 1671 /* 1672 * Check whether to display the 'auto start backend' box 1673 * or not. Show it if we are a localhost and using network, 1674 * or using a socket. 1675 */ 1676 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo)); 1677 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4)) 1678 uses_net = 1; 1679 g_free(ctext); 1680 1681 if (uses_net) { 1682 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry)); 1683 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") || 1684 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") || 1685 !strcmp(text, "ip6-loopback")) 1686 is_localhost = 1; 1687 } 1688 1689 if (!uses_net || is_localhost) { 1690 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1); 1691 gtk_widget_set_sensitive(cw->button, 1); 1692 } else { 1693 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0); 1694 gtk_widget_set_sensitive(cw->button, 0); 1695 } 1696} 1697 1698static int get_connection_details(char **host, int *port, int *type, 1699 int *server_start) 1700{ 1701 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry; 1702 struct connection_widgets cw; 1703 char *typeentry; 1704 1705 dialog = gtk_dialog_new_with_buttons("Connection details", 1706 GTK_WINDOW(main_ui.window), 1707 GTK_DIALOG_DESTROY_WITH_PARENT, 1708 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, 1709 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL); 1710 1711 frame = gtk_frame_new("Hostname / socket name"); 1712 /* gtk_dialog_get_content_area() is 2.14 and newer */ 1713 vbox = GTK_DIALOG(dialog)->vbox; 1714 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); 1715 1716 box = gtk_vbox_new(FALSE, 6); 1717 gtk_container_add(GTK_CONTAINER(frame), box); 1718 1719 hbox = gtk_hbox_new(TRUE, 10); 1720 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); 1721 cw.hentry = gtk_entry_new(); 1722 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost"); 1723 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0); 1724 1725 frame = gtk_frame_new("Port"); 1726 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); 1727 box = gtk_vbox_new(FALSE, 10); 1728 gtk_container_add(GTK_CONTAINER(frame), box); 1729 1730 hbox = gtk_hbox_new(TRUE, 4); 1731 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); 1732 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT); 1733 1734 frame = gtk_frame_new("Type"); 1735 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); 1736 box = gtk_vbox_new(FALSE, 10); 1737 gtk_container_add(GTK_CONTAINER(frame), box); 1738 1739 hbox = gtk_hbox_new(TRUE, 4); 1740 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); 1741 1742 cw.combo = gtk_combo_box_new_text(); 1743 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4"); 1744 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6"); 1745 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket"); 1746 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0); 1747 1748 gtk_container_add(GTK_CONTAINER(hbox), cw.combo); 1749 1750 frame = gtk_frame_new("Options"); 1751 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); 1752 box = gtk_vbox_new(FALSE, 10); 1753 gtk_container_add(GTK_CONTAINER(frame), box); 1754 1755 hbox = gtk_hbox_new(TRUE, 4); 1756 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); 1757 1758 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend"); 1759 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1); 1760 gtk_widget_set_tooltip_text(cw.button, "When running fio locally, it is necessary to have the backend running on the same system. If this is checked, gfio will start the backend automatically for you if it isn't already running."); 1761 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6); 1762 1763 /* 1764 * Connect edit signal, so we can show/not-show the auto start button 1765 */ 1766 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw); 1767 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw); 1768 1769 gtk_widget_show_all(dialog); 1770 1771 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) { 1772 gtk_widget_destroy(dialog); 1773 return 1; 1774 } 1775 1776 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry))); 1777 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry)); 1778 1779 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo)); 1780 if (!typeentry || !strncmp(typeentry, "IPv4", 4)) 1781 *type = Fio_client_ipv4; 1782 else if (!strncmp(typeentry, "IPv6", 4)) 1783 *type = Fio_client_ipv6; 1784 else 1785 *type = Fio_client_socket; 1786 g_free(typeentry); 1787 1788 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button)); 1789 1790 gtk_widget_destroy(dialog); 1791 return 0; 1792} 1793 1794static void gfio_client_added(struct gui_entry *ge, struct fio_client *client) 1795{ 1796 struct gfio_client *gc; 1797 1798 gc = malloc(sizeof(*gc)); 1799 memset(gc, 0, sizeof(*gc)); 1800 gc->ge = ge; 1801 gc->client = fio_get_client(client); 1802 1803 ge->client = gc; 1804 1805 client->client_data = gc; 1806} 1807 1808static GtkWidget *new_client_page(struct gui_entry *ge); 1809 1810static struct gui_entry *alloc_new_gui_entry(struct gui *ui) 1811{ 1812 struct gui_entry *ge; 1813 1814 ge = malloc(sizeof(*ge)); 1815 memset(ge, 0, sizeof(*ge)); 1816 INIT_FLIST_HEAD(&ge->list); 1817 flist_add_tail(&ge->list, &ui->list); 1818 ge->ui = ui; 1819 return ge; 1820} 1821 1822/* 1823 * FIXME: need more handling here 1824 */ 1825static void ge_destroy(GtkWidget *w, gpointer data) 1826{ 1827 struct gui_entry *ge = data; 1828 struct gfio_client *gc = ge->client; 1829 1830 if (gc->client) 1831 fio_put_client(gc->client); 1832 1833 flist_del(&ge->list); 1834 free(ge); 1835} 1836 1837static struct gui_entry *get_new_ge_with_tab(const char *name) 1838{ 1839 struct gui_entry *ge; 1840 1841 ge = alloc_new_gui_entry(&main_ui); 1842 1843 ge->vbox = new_client_page(ge); 1844 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_destroy), ge); 1845 1846 ge->page_label = gtk_label_new(name); 1847 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label); 1848 1849 gtk_widget_show_all(main_ui.window); 1850 return ge; 1851} 1852 1853static void file_new(GtkWidget *w, gpointer data) 1854{ 1855 get_new_ge_with_tab("Untitled"); 1856} 1857 1858/* 1859 * Return the 'ge' corresponding to the tab. If the active tab is the 1860 * main tab, open a new tab. 1861 */ 1862static struct gui_entry *get_ge_from_page(unsigned int cur_page) 1863{ 1864 struct flist_head *entry; 1865 struct gui_entry *ge; 1866 1867 if (!cur_page) 1868 return get_new_ge_with_tab("Untitled"); 1869 1870 flist_for_each(entry, &main_ui.list) { 1871 ge = flist_entry(entry, struct gui_entry, list); 1872 if (ge->page_num == cur_page) 1873 return ge; 1874 } 1875 1876 return NULL; 1877} 1878 1879static void file_open(GtkWidget *w, gpointer data) 1880{ 1881 struct gui *ui = data; 1882 GtkWidget *dialog; 1883 GSList *filenames, *fn_glist; 1884 GtkFileFilter *filter; 1885 char *host; 1886 int port, type, server_start; 1887 struct gui_entry *ge; 1888 gint cur_page; 1889 1890 /* 1891 * Creates new tab if current tab is the main window, or the 1892 * current tab already has a client. 1893 */ 1894 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook)); 1895 ge = get_ge_from_page(cur_page); 1896 if (ge->client) 1897 ge = get_new_ge_with_tab("Untitled"); 1898 1899 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num); 1900 1901 dialog = gtk_file_chooser_dialog_new("Open File", 1902 GTK_WINDOW(ui->window), 1903 GTK_FILE_CHOOSER_ACTION_OPEN, 1904 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, 1905 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, 1906 NULL); 1907 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE); 1908 1909 filter = gtk_file_filter_new(); 1910 gtk_file_filter_add_pattern(filter, "*.fio"); 1911 gtk_file_filter_add_pattern(filter, "*.job"); 1912 gtk_file_filter_add_pattern(filter, "*.ini"); 1913 gtk_file_filter_add_mime_type(filter, "text/fio"); 1914 gtk_file_filter_set_name(filter, "Fio job file"); 1915 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter); 1916 1917 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) { 1918 gtk_widget_destroy(dialog); 1919 return; 1920 } 1921 1922 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog)); 1923 1924 gtk_widget_destroy(dialog); 1925 1926 if (get_connection_details(&host, &port, &type, &server_start)) 1927 goto err; 1928 1929 filenames = fn_glist; 1930 while (filenames != NULL) { 1931 struct fio_client *client; 1932 1933 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *)); 1934 ge->job_files[ge->nr_job_files] = strdup(filenames->data); 1935 ge->nr_job_files++; 1936 1937 client = fio_client_add_explicit(&gfio_client_ops, host, type, port); 1938 if (!client) { 1939 GError *error; 1940 1941 error = g_error_new(g_quark_from_string("fio"), 1, 1942 "Failed to add client %s", host); 1943 report_error(error); 1944 g_error_free(error); 1945 } 1946 gfio_client_added(ge, client); 1947 1948 g_free(filenames->data); 1949 filenames = g_slist_next(filenames); 1950 } 1951 free(host); 1952 1953 if (server_start) 1954 gfio_start_server(); 1955err: 1956 g_slist_free(fn_glist); 1957} 1958 1959static void file_save(GtkWidget *w, gpointer data) 1960{ 1961 struct gui *ui = data; 1962 GtkWidget *dialog; 1963 1964 dialog = gtk_file_chooser_dialog_new("Save File", 1965 GTK_WINDOW(ui->window), 1966 GTK_FILE_CHOOSER_ACTION_SAVE, 1967 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, 1968 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, 1969 NULL); 1970 1971 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE); 1972 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document"); 1973 1974 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { 1975 char *filename; 1976 1977 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); 1978 // save_job_file(filename); 1979 g_free(filename); 1980 } 1981 gtk_widget_destroy(dialog); 1982} 1983 1984static void view_log_destroy(GtkWidget *w, gpointer data) 1985{ 1986 struct gui *ui = (struct gui *) data; 1987 1988 gtk_widget_ref(ui->log_tree); 1989 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree); 1990 gtk_widget_destroy(w); 1991 ui->log_view = NULL; 1992} 1993 1994static void view_log(GtkWidget *w, gpointer data) 1995{ 1996 GtkWidget *win, *scroll, *vbox, *box; 1997 struct gui *ui = (struct gui *) data; 1998 1999 if (ui->log_view) 2000 return; 2001 2002 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL); 2003 gtk_window_set_title(GTK_WINDOW(win), "Log"); 2004 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500); 2005 2006 scroll = gtk_scrolled_window_new(NULL, NULL); 2007 2008 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5); 2009 2010 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); 2011 2012 box = gtk_hbox_new(TRUE, 0); 2013 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree); 2014 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui); 2015 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box); 2016 2017 vbox = gtk_vbox_new(TRUE, 5); 2018 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll); 2019 2020 gtk_container_add(GTK_CONTAINER(win), vbox); 2021 gtk_widget_show_all(win); 2022} 2023 2024static void preferences(GtkWidget *w, gpointer data) 2025{ 2026 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font; 2027 int i; 2028 2029 dialog = gtk_dialog_new_with_buttons("Preferences", 2030 GTK_WINDOW(main_ui.window), 2031 GTK_DIALOG_DESTROY_WITH_PARENT, 2032 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, 2033 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, 2034 NULL); 2035 2036 frame = gtk_frame_new("Debug logging"); 2037 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5); 2038 2039 vbox = gtk_vbox_new(FALSE, 6); 2040 gtk_container_add(GTK_CONTAINER(frame), vbox); 2041 2042 box = gtk_hbox_new(FALSE, 6); 2043 gtk_container_add(GTK_CONTAINER(vbox), box); 2044 2045 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX); 2046 2047 for (i = 0; i < FD_DEBUG_MAX; i++) { 2048 if (i == 7) { 2049 box = gtk_hbox_new(FALSE, 6); 2050 gtk_container_add(GTK_CONTAINER(vbox), box); 2051 } 2052 2053 2054 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name); 2055 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help); 2056 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6); 2057 } 2058 2059 frame = gtk_frame_new("Graph font"); 2060 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5); 2061 vbox = gtk_vbox_new(FALSE, 6); 2062 gtk_container_add(GTK_CONTAINER(frame), vbox); 2063 2064 font = gtk_font_button_new(); 2065 gtk_box_pack_start(GTK_BOX(vbox), font, FALSE, FALSE, 5); 2066 2067 gtk_widget_show_all(dialog); 2068 2069 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) { 2070 gtk_widget_destroy(dialog); 2071 return; 2072 } 2073 2074 for (i = 0; i < FD_DEBUG_MAX; i++) { 2075 int set; 2076 2077 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i])); 2078 if (set) 2079 fio_debug |= (1UL << i); 2080 } 2081 2082 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font))); 2083 gtk_widget_destroy(dialog); 2084} 2085 2086static void about_dialog(GtkWidget *w, gpointer data) 2087{ 2088 const char *authors[] = { 2089 "Jens Axboe <axboe@kernel.dk>", 2090 "Stephen Carmeron <stephenmcameron@gmail.com>", 2091 NULL 2092 }; 2093 const char *license[] = { 2094 "Fio is free software; you can redistribute it and/or modify " 2095 "it under the terms of the GNU General Public License as published by " 2096 "the Free Software Foundation; either version 2 of the License, or " 2097 "(at your option) any later version.\n", 2098 "Fio is distributed in the hope that it will be useful, " 2099 "but WITHOUT ANY WARRANTY; without even the implied warranty of " 2100 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the " 2101 "GNU General Public License for more details.\n", 2102 "You should have received a copy of the GNU General Public License " 2103 "along with Fio; if not, write to the Free Software Foundation, Inc., " 2104 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n" 2105 }; 2106 char *license_trans; 2107 2108 license_trans = g_strconcat(license[0], "\n", license[1], "\n", 2109 license[2], "\n", NULL); 2110 2111 gtk_show_about_dialog(NULL, 2112 "program-name", "gfio", 2113 "comments", "Gtk2 UI for fio", 2114 "license", license_trans, 2115 "website", "http://git.kernel.dk/?p=fio.git;a=summary", 2116 "authors", authors, 2117 "version", fio_version_string, 2118 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>", 2119 "logo-icon-name", "fio", 2120 /* Must be last: */ 2121 "wrap-license", TRUE, 2122 NULL); 2123 2124 g_free(license_trans); 2125} 2126 2127static GtkActionEntry menu_items[] = { 2128 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL}, 2129 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL}, 2130 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL}, 2131 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) }, 2132 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) }, 2133 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) }, 2134 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) }, 2135 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) }, 2136 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) }, 2137 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) }, 2138}; 2139static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]); 2140 2141static const gchar *ui_string = " \ 2142 <ui> \ 2143 <menubar name=\"MainMenu\"> \ 2144 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \ 2145 <menuitem name=\"New\" action=\"NewFile\" /> \ 2146 <separator name=\"Separator1\"/> \ 2147 <menuitem name=\"Open\" action=\"OpenFile\" /> \ 2148 <menuitem name=\"Save\" action=\"SaveFile\" /> \ 2149 <separator name=\"Separator2\"/> \ 2150 <menuitem name=\"Preferences\" action=\"Preferences\" /> \ 2151 <separator name=\"Separator3\"/> \ 2152 <menuitem name=\"Quit\" action=\"Quit\" /> \ 2153 </menu> \ 2154 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \ 2155 <menuitem name=\"Log\" action=\"ViewLog\" /> \ 2156 </menu>\ 2157 <menu name=\"Help\" action=\"HelpMenuAction\"> \ 2158 <menuitem name=\"About\" action=\"About\" /> \ 2159 </menu> \ 2160 </menubar> \ 2161 </ui> \ 2162"; 2163 2164static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager, 2165 struct gui *ui) 2166{ 2167 GtkActionGroup *action_group = gtk_action_group_new("Menu"); 2168 GError *error = 0; 2169 2170 action_group = gtk_action_group_new("Menu"); 2171 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui); 2172 2173 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0); 2174 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error); 2175 2176 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager)); 2177 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu"); 2178} 2179 2180void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar, 2181 GtkWidget *vbox, GtkUIManager *ui_manager) 2182{ 2183 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0); 2184} 2185 2186static GtkWidget *new_client_page(struct gui_entry *ge) 2187{ 2188 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box; 2189 GdkColor white; 2190 2191 main_vbox = gtk_vbox_new(FALSE, 3); 2192 2193 ge->topalign = gtk_alignment_new(0, 0, 1, 0); 2194 ge->topvbox = gtk_vbox_new(FALSE, 3); 2195 gtk_container_add(GTK_CONTAINER(ge->topalign), ge->topvbox); 2196 gtk_box_pack_start(GTK_BOX(main_vbox), ge->topalign, FALSE, FALSE, 0); 2197 2198 probe = gtk_frame_new("Job"); 2199 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3); 2200 probe_frame = gtk_vbox_new(FALSE, 3); 2201 gtk_container_add(GTK_CONTAINER(probe), probe_frame); 2202 2203 probe_box = gtk_hbox_new(FALSE, 3); 2204 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3); 2205 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host"); 2206 ge->probe.os = new_info_label_in_frame(probe_box, "OS"); 2207 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture"); 2208 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version"); 2209 2210 probe_box = gtk_hbox_new(FALSE, 3); 2211 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3); 2212 2213 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs"); 2214 ge->eta.iotype = new_info_entry_in_frame(probe_box, "IO"); 2215 ge->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine"); 2216 ge->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth"); 2217 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs"); 2218 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files"); 2219 2220 probe_box = gtk_hbox_new(FALSE, 3); 2221 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3); 2222 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW"); 2223 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS"); 2224 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW"); 2225 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS"); 2226 2227 /* 2228 * Only add this if we have a commit rate 2229 */ 2230#if 0 2231 probe_box = gtk_hbox_new(FALSE, 3); 2232 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3); 2233 2234 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW"); 2235 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS"); 2236 2237 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW"); 2238 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS"); 2239#endif 2240 2241 /* 2242 * Set up a drawing area and IOPS and bandwidth graphs 2243 */ 2244 gdk_color_parse("white", &white); 2245 ge->graphs.drawing_area = gtk_drawing_area_new(); 2246 ge->graphs.drawing_area_xdim = DRAWING_AREA_XDIM; 2247 ge->graphs.drawing_area_ydim = DRAWING_AREA_YDIM; 2248 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area), 2249 ge->graphs.drawing_area_xdim, ge->graphs.drawing_area_ydim); 2250 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white); 2251 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event", 2252 G_CALLBACK(on_expose_drawing_area), &ge->graphs); 2253 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event", 2254 G_CALLBACK(on_config_drawing_area), &ge->graphs); 2255 ge->scrolled_window = gtk_scrolled_window_new(NULL, NULL); 2256 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ge->scrolled_window), 2257 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); 2258 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ge->scrolled_window), 2259 ge->graphs.drawing_area); 2260 gtk_box_pack_start(GTK_BOX(main_vbox), ge->scrolled_window, 2261 TRUE, TRUE, 0); 2262 2263 setup_graphs(&ge->graphs); 2264 2265 /* 2266 * Set up alignments for widgets at the bottom of ui, 2267 * align bottom left, expand horizontally but not vertically 2268 */ 2269 ge->bottomalign = gtk_alignment_new(0, 1, 1, 0); 2270 ge->buttonbox = gtk_hbox_new(FALSE, 0); 2271 gtk_container_add(GTK_CONTAINER(ge->bottomalign), ge->buttonbox); 2272 gtk_box_pack_start(GTK_BOX(main_vbox), ge->bottomalign, 2273 FALSE, FALSE, 0); 2274 2275 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist)); 2276 2277 /* 2278 * Set up thread status progress bar 2279 */ 2280 ge->thread_status_pb = gtk_progress_bar_new(); 2281 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0); 2282 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections"); 2283 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb); 2284 2285 2286 return main_vbox; 2287} 2288 2289static GtkWidget *new_main_page(struct gui *ui) 2290{ 2291 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box; 2292 GdkColor white; 2293 2294 main_vbox = gtk_vbox_new(FALSE, 3); 2295 2296 /* 2297 * Set up alignments for widgets at the top of ui, 2298 * align top left, expand horizontally but not vertically 2299 */ 2300 ui->topalign = gtk_alignment_new(0, 0, 1, 0); 2301 ui->topvbox = gtk_vbox_new(FALSE, 0); 2302 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox); 2303 gtk_box_pack_start(GTK_BOX(main_vbox), ui->topalign, FALSE, FALSE, 0); 2304 2305 probe = gtk_frame_new("Run statistics"); 2306 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3); 2307 probe_frame = gtk_vbox_new(FALSE, 3); 2308 gtk_container_add(GTK_CONTAINER(probe), probe_frame); 2309 2310 probe_box = gtk_hbox_new(FALSE, 3); 2311 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3); 2312 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running"); 2313 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW"); 2314 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS"); 2315 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW"); 2316 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS"); 2317 2318 /* 2319 * Only add this if we have a commit rate 2320 */ 2321#if 0 2322 probe_box = gtk_hbox_new(FALSE, 3); 2323 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3); 2324 2325 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW"); 2326 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS"); 2327 2328 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW"); 2329 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS"); 2330#endif 2331 2332 /* 2333 * Set up a drawing area and IOPS and bandwidth graphs 2334 */ 2335 gdk_color_parse("white", &white); 2336 ui->graphs.drawing_area = gtk_drawing_area_new(); 2337 ui->graphs.drawing_area_xdim = DRAWING_AREA_XDIM; 2338 ui->graphs.drawing_area_ydim = DRAWING_AREA_YDIM; 2339 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area), 2340 ui->graphs.drawing_area_xdim, ui->graphs.drawing_area_ydim); 2341 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white); 2342 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event", 2343 G_CALLBACK(on_expose_drawing_area), &ui->graphs); 2344 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event", 2345 G_CALLBACK(on_config_drawing_area), &ui->graphs); 2346 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL); 2347 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window), 2348 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); 2349 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window), 2350 ui->graphs.drawing_area); 2351 gtk_box_pack_start(GTK_BOX(main_vbox), ui->scrolled_window, 2352 TRUE, TRUE, 0); 2353 2354 setup_graphs(&ui->graphs); 2355 2356 /* 2357 * Set up alignments for widgets at the bottom of ui, 2358 * align bottom left, expand horizontally but not vertically 2359 */ 2360 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0); 2361 ui->buttonbox = gtk_hbox_new(FALSE, 0); 2362 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox); 2363 gtk_box_pack_start(GTK_BOX(main_vbox), ui->bottomalign, 2364 FALSE, FALSE, 0); 2365 2366 /* 2367 * Set up thread status progress bar 2368 */ 2369 ui->thread_status_pb = gtk_progress_bar_new(); 2370 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0); 2371 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections"); 2372 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb); 2373 2374 return main_vbox; 2375} 2376 2377static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget, 2378 guint page, gpointer data) 2379 2380{ 2381 return TRUE; 2382} 2383 2384static void init_ui(int *argc, char **argv[], struct gui *ui) 2385{ 2386 GtkSettings *settings; 2387 GtkUIManager *uimanager; 2388 GtkWidget *menu, *vbox; 2389 2390 /* Magical g*thread incantation, you just need this thread stuff. 2391 * Without it, the update that happens in gfio_update_thread_status 2392 * doesn't really happen in a timely fashion, you need expose events 2393 */ 2394 if (!g_thread_supported()) 2395 g_thread_init(NULL); 2396 gdk_threads_init(); 2397 2398 gtk_init(argc, argv); 2399 settings = gtk_settings_get_default(); 2400 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting"); 2401 g_type_init(); 2402 2403 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 2404 gtk_window_set_title(GTK_WINDOW(ui->window), "fio"); 2405 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768); 2406 2407 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL); 2408 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL); 2409 2410 ui->vbox = gtk_vbox_new(FALSE, 0); 2411 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox); 2412 2413 uimanager = gtk_ui_manager_new(); 2414 menu = get_menubar_menu(ui->window, uimanager, ui); 2415 gfio_ui_setup(settings, menu, ui->vbox, uimanager); 2416 2417 ui->notebook = gtk_notebook_new(); 2418 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui); 2419 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1); 2420 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook)); 2421 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook); 2422 2423 vbox = new_main_page(ui); 2424 2425 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main")); 2426 2427 gfio_ui_setup_log(ui); 2428 2429 gtk_widget_show_all(ui->window); 2430} 2431 2432int main(int argc, char *argv[], char *envp[]) 2433{ 2434 if (initialize_fio(envp)) 2435 return 1; 2436 if (fio_init_options()) 2437 return 1; 2438 2439 memset(&main_ui, 0, sizeof(main_ui)); 2440 INIT_FLIST_HEAD(&main_ui.list); 2441 2442 init_ui(&argc, &argv, &main_ui); 2443 2444 gdk_threads_enter(); 2445 gtk_main(); 2446 gdk_threads_leave(); 2447 return 0; 2448} 2449