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