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