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