gfio.c revision f9d40b4814d88ca1ca13a05cf45b6f36cfb2ba55
1/* 2 * gfio - gui front end for fio - the flexible io tester 3 * 4 * Copyright (C) 2012 Stephen M. Cameron <stephenmcameron@gmail.com> 5 * 6 * The license below covers all files distributed with fio unless otherwise 7 * noted in the file itself. 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License version 2 as 11 * published by the Free Software Foundation. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 * 22 */ 23#include <locale.h> 24#include <malloc.h> 25 26#include <glib.h> 27#include <gtk/gtk.h> 28 29#include "fio.h" 30 31static void gfio_update_thread_status(char *status_message, double perc); 32 33#define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0]))) 34 35typedef void (*clickfunction)(GtkWidget *widget, gpointer data); 36 37static void connect_clicked(GtkWidget *widget, gpointer data); 38static void start_job_clicked(GtkWidget *widget, gpointer data); 39 40static struct button_spec { 41 const char *buttontext; 42 clickfunction f; 43 const char *tooltiptext; 44 const int start_insensitive; 45} buttonspeclist[] = { 46#define CONNECT_BUTTON 0 47#define START_JOB_BUTTON 1 48 { "Connect", connect_clicked, "Connect to host", 0 }, 49 { "Start Job", 50 start_job_clicked, 51 "Send current fio job to fio server to be executed", 1 }, 52}; 53 54struct probe_widget { 55 GtkWidget *hostname; 56 GtkWidget *os; 57 GtkWidget *arch; 58 GtkWidget *fio_ver; 59}; 60 61struct eta_widget { 62 GtkWidget *name; 63 GtkWidget *iotype; 64 GtkWidget *ioengine; 65 GtkWidget *iodepth; 66 GtkWidget *jobs; 67 GtkWidget *files; 68 GtkWidget *read_bw; 69 GtkWidget *read_iops; 70 GtkWidget *cr_bw; 71 GtkWidget *cr_iops; 72 GtkWidget *write_bw; 73 GtkWidget *write_iops; 74 GtkWidget *cw_bw; 75 GtkWidget *cw_iops; 76}; 77 78struct gui { 79 GtkWidget *window; 80 GtkWidget *vbox; 81 GtkWidget *topvbox; 82 GtkWidget *topalign; 83 GtkWidget *bottomalign; 84 GtkWidget *thread_status_pb; 85 GtkWidget *buttonbox; 86 GtkWidget *button[ARRAYSIZE(buttonspeclist)]; 87 GtkWidget *scrolled_window; 88 GtkWidget *textview; 89 GtkWidget *error_info_bar; 90 GtkWidget *error_label; 91 GtkWidget *results_notebook; 92 GtkWidget *results_window; 93 GtkTextBuffer *text; 94 struct probe_widget probe; 95 struct eta_widget eta; 96 int connected; 97 pthread_t t; 98 99 struct fio_client *client; 100 int nr_job_files; 101 char **job_files; 102} ui; 103 104static void clear_ui_info(struct gui *ui) 105{ 106 gtk_label_set_text(GTK_LABEL(ui->probe.hostname), ""); 107 gtk_label_set_text(GTK_LABEL(ui->probe.os), ""); 108 gtk_label_set_text(GTK_LABEL(ui->probe.arch), ""); 109 gtk_label_set_text(GTK_LABEL(ui->probe.fio_ver), ""); 110 gtk_entry_set_text(GTK_ENTRY(ui->eta.name), ""); 111 gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), ""); 112 gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), ""); 113 gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), ""); 114 gtk_entry_set_text(GTK_ENTRY(ui->eta.jobs), ""); 115 gtk_entry_set_text(GTK_ENTRY(ui->eta.files), ""); 116 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), ""); 117 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), ""); 118 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), ""); 119 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), ""); 120} 121 122static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label) 123{ 124 GtkWidget *entry, *frame; 125 126 frame = gtk_frame_new(label); 127 entry = gtk_entry_new(); 128 gtk_entry_set_editable(GTK_ENTRY(entry), 0); 129 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3); 130 gtk_container_add(GTK_CONTAINER(frame), entry); 131 132 return entry; 133} 134 135static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label) 136{ 137 GtkWidget *label_widget; 138 GtkWidget *frame; 139 140 frame = gtk_frame_new(label); 141 label_widget = gtk_label_new(NULL); 142 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3); 143 gtk_container_add(GTK_CONTAINER(frame), label_widget); 144 145 return label_widget; 146} 147 148static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval) 149{ 150 GtkWidget *button, *box; 151 152 box = gtk_hbox_new(FALSE, 3); 153 gtk_container_add(GTK_CONTAINER(hbox), box); 154 155 button = gtk_spin_button_new_with_range(min, max, 1.0); 156 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0); 157 158 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID); 159 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval); 160 161 return button; 162} 163 164static void gfio_set_connected(struct gui *ui, int connected) 165{ 166 if (connected) { 167 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1); 168 ui->connected = 1; 169 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Disconnect"); 170 } else { 171 ui->connected = 0; 172 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Connect"); 173 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0); 174 } 175} 176 177static void label_set_int_value(GtkWidget *entry, unsigned int val) 178{ 179 char tmp[80]; 180 181 sprintf(tmp, "%u", val); 182 gtk_label_set_text(GTK_LABEL(entry), tmp); 183} 184 185static void entry_set_int_value(GtkWidget *entry, unsigned int val) 186{ 187 char tmp[80]; 188 189 sprintf(tmp, "%u", val); 190 gtk_entry_set_text(GTK_ENTRY(entry), tmp); 191} 192 193#define ALIGN_LEFT 1 194#define ALIGN_RIGHT 2 195#define INVISIBLE 4 196#define UNSORTABLE 8 197 198GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags) 199{ 200 GtkCellRenderer *renderer; 201 GtkTreeViewColumn *col; 202 double xalign = 0.0; /* left as default */ 203 PangoAlignment align; 204 gboolean visible; 205 206 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT : 207 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT : 208 PANGO_ALIGN_CENTER; 209 visible = !(flags & INVISIBLE); 210 211 renderer = gtk_cell_renderer_text_new(); 212 col = gtk_tree_view_column_new(); 213 214 gtk_tree_view_column_set_title(col, title); 215 if (!(flags & UNSORTABLE)) 216 gtk_tree_view_column_set_sort_column_id(col, index); 217 gtk_tree_view_column_set_resizable(col, TRUE); 218 gtk_tree_view_column_pack_start(col, renderer, TRUE); 219 gtk_tree_view_column_add_attribute(col, renderer, "text", index); 220 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL); 221 switch (align) { 222 case PANGO_ALIGN_LEFT: 223 xalign = 0.0; 224 break; 225 case PANGO_ALIGN_CENTER: 226 xalign = 0.5; 227 break; 228 case PANGO_ALIGN_RIGHT: 229 xalign = 1.0; 230 break; 231 } 232 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5); 233 gtk_tree_view_column_set_visible(col, visible); 234 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col); 235 return col; 236} 237 238static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals, 239 fio_fp64_t *plist, 240 unsigned int len, 241 const char *base, 242 unsigned int scale) 243{ 244 GType types[FIO_IO_U_LIST_MAX_LEN]; 245 GtkWidget *tree_view; 246 GtkTreeSelection *selection; 247 GtkListStore *model; 248 GtkTreeIter iter; 249 int i; 250 251 for (i = 0; i < len; i++) 252 types[i] = G_TYPE_INT; 253 254 model = gtk_list_store_newv(len, types); 255 256 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); 257 gtk_widget_set_can_focus(tree_view, FALSE); 258 259 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); 260 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE); 261 262 for (i = 0; i < len; i++) { 263 char fbuf[8]; 264 265 sprintf(fbuf, "%2.2f%%", plist[i].u.f); 266 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE); 267 } 268 269 gtk_list_store_append(model, &iter); 270 271 for (i = 0; i < len; i++) 272 gtk_list_store_set(model, &iter, i, ovals[i], -1); 273 274 return tree_view; 275} 276 277static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts, 278 int ddir) 279{ 280 unsigned int *io_u_plat = ts->io_u_plat[ddir]; 281 unsigned long nr = ts->clat_stat[ddir].samples; 282 fio_fp64_t *plist = ts->percentile_list; 283 unsigned int *ovals, len, minv, maxv, scale_down; 284 const char *base; 285 GtkWidget *tree_view, *frame, *hbox; 286 char tmp[64]; 287 288 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv); 289 if (!len) 290 goto out; 291 292 /* 293 * We default to usecs, but if the value range is such that we 294 * should scale down to msecs, do that. 295 */ 296 if (minv > 2000 && maxv > 99999) { 297 scale_down = 1; 298 base = "msec"; 299 } else { 300 scale_down = 0; 301 base = "usec"; 302 } 303 304 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down); 305 306 sprintf(tmp, "Completion percentiles (%s)", base); 307 frame = gtk_frame_new(tmp); 308 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); 309 310 hbox = gtk_hbox_new(FALSE, 3); 311 gtk_container_add(GTK_CONTAINER(frame), hbox); 312 313 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3); 314out: 315 if (ovals) 316 free(ovals); 317} 318 319static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min, 320 unsigned long max, double mean, double dev) 321{ 322 const char *base = "(usec)"; 323 GtkWidget *hbox, *label, *frame; 324 char *minp, *maxp; 325 char tmp[64]; 326 327 if (!usec_to_msec(&min, &max, &mean, &dev)) 328 base = "(msec)"; 329 330 minp = num2str(min, 6, 1, 0); 331 maxp = num2str(max, 6, 1, 0); 332 333 sprintf(tmp, "%s %s", name, base); 334 frame = gtk_frame_new(tmp); 335 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); 336 337 hbox = gtk_hbox_new(FALSE, 3); 338 gtk_container_add(GTK_CONTAINER(frame), hbox); 339 340 label = new_info_label_in_frame(hbox, "Minimum"); 341 gtk_label_set_text(GTK_LABEL(label), minp); 342 label = new_info_label_in_frame(hbox, "Maximum"); 343 gtk_label_set_text(GTK_LABEL(label), maxp); 344 label = new_info_label_in_frame(hbox, "Average"); 345 sprintf(tmp, "%5.02f", mean); 346 gtk_label_set_text(GTK_LABEL(label), tmp); 347 label = new_info_label_in_frame(hbox, "Standard deviation"); 348 sprintf(tmp, "%5.02f", dev); 349 gtk_label_set_text(GTK_LABEL(label), tmp); 350 351 free(minp); 352 free(maxp); 353 354} 355 356#define GFIO_CLAT 1 357#define GFIO_SLAT 2 358#define GFIO_LAT 4 359 360static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs, 361 struct thread_stat *ts, int ddir) 362{ 363 const char *ddir_label[2] = { "Read", "Write" }; 364 GtkWidget *frame, *label, *box, *vbox, *main_vbox; 365 unsigned long min, max, runt; 366 unsigned long long bw, iops; 367 unsigned int flags = 0; 368 double mean, dev; 369 char *io_p, *bw_p, *iops_p; 370 int i2p; 371 372 if (!ts->runtime[ddir]) 373 return; 374 375 i2p = is_power_of_2(rs->kb_base); 376 runt = ts->runtime[ddir]; 377 378 bw = (1000 * ts->io_bytes[ddir]) / runt; 379 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p); 380 bw_p = num2str(bw, 6, 1, i2p); 381 382 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt; 383 iops_p = num2str(iops, 6, 1, 0); 384 385 box = gtk_hbox_new(FALSE, 3); 386 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3); 387 388 frame = gtk_frame_new(ddir_label[ddir]); 389 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5); 390 391 main_vbox = gtk_vbox_new(FALSE, 3); 392 gtk_container_add(GTK_CONTAINER(frame), main_vbox); 393 394 box = gtk_hbox_new(FALSE, 3); 395 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3); 396 397 label = new_info_label_in_frame(box, "IO"); 398 gtk_label_set_text(GTK_LABEL(label), io_p); 399 label = new_info_label_in_frame(box, "Bandwidth"); 400 gtk_label_set_text(GTK_LABEL(label), bw_p); 401 label = new_info_label_in_frame(box, "IOPS"); 402 gtk_label_set_text(GTK_LABEL(label), iops_p); 403 label = new_info_label_in_frame(box, "Runtime (msec)"); 404 label_set_int_value(label, ts->runtime[ddir]); 405 406 if (calc_lat(&ts->bw_stat[ddir], &min, &max, &mean, &dev)) { 407 double p_of_agg = 100.0; 408 const char *bw_str = "KB"; 409 char tmp[32]; 410 411 if (rs->agg[ddir]) { 412 p_of_agg = mean * 100 / (double) rs->agg[ddir]; 413 if (p_of_agg > 100.0) 414 p_of_agg = 100.0; 415 } 416 417 if (mean > 999999.9) { 418 min /= 1000.0; 419 max /= 1000.0; 420 mean /= 1000.0; 421 dev /= 1000.0; 422 bw_str = "MB"; 423 } 424 425 sprintf(tmp, "Bandwidth (%s)", bw_str); 426 frame = gtk_frame_new(tmp); 427 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5); 428 429 box = gtk_hbox_new(FALSE, 3); 430 gtk_container_add(GTK_CONTAINER(frame), box); 431 432 label = new_info_label_in_frame(box, "Minimum"); 433 label_set_int_value(label, min); 434 label = new_info_label_in_frame(box, "Maximum"); 435 label_set_int_value(label, max); 436 label = new_info_label_in_frame(box, "Percentage of jobs"); 437 sprintf(tmp, "%3.2f%%", p_of_agg); 438 gtk_label_set_text(GTK_LABEL(label), tmp); 439 label = new_info_label_in_frame(box, "Average"); 440 sprintf(tmp, "%5.02f", mean); 441 gtk_label_set_text(GTK_LABEL(label), tmp); 442 label = new_info_label_in_frame(box, "Standard deviation"); 443 sprintf(tmp, "%5.02f", dev); 444 gtk_label_set_text(GTK_LABEL(label), tmp); 445 } 446 447 if (calc_lat(&ts->slat_stat[ddir], &min, &max, &mean, &dev)) 448 flags |= GFIO_SLAT; 449 if (calc_lat(&ts->clat_stat[ddir], &min, &max, &mean, &dev)) 450 flags |= GFIO_CLAT; 451 if (calc_lat(&ts->lat_stat[ddir], &min, &max, &mean, &dev)) 452 flags |= GFIO_LAT; 453 454 if (flags) { 455 frame = gtk_frame_new("Latency"); 456 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5); 457 458 vbox = gtk_vbox_new(FALSE, 3); 459 gtk_container_add(GTK_CONTAINER(frame), vbox); 460 461 if (flags & GFIO_SLAT) 462 gfio_show_lat(vbox, "Submission latency", min, max, mean, dev); 463 if (flags & GFIO_CLAT) 464 gfio_show_lat(vbox, "Completion latency", min, max, mean, dev); 465 if (flags & GFIO_LAT) 466 gfio_show_lat(vbox, "Total latency", min, max, mean, dev); 467 } 468 469 if (ts->clat_percentiles) 470 gfio_show_clat_percentiles(main_vbox, ts, ddir); 471 472 473 free(io_p); 474 free(bw_p); 475 free(iops_p); 476} 477 478static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num, 479 const char **labels) 480{ 481 GtkWidget *tree_view; 482 GtkTreeSelection *selection; 483 GtkListStore *model; 484 GtkTreeIter iter; 485 GType *types; 486 int i, skipped; 487 488 /* 489 * Check if all are empty, in which case don't bother 490 */ 491 for (i = 0, skipped = 0; i < num; i++) 492 if (lat[i] <= 0.0) 493 skipped++; 494 495 if (skipped == num) 496 return NULL; 497 498 types = malloc(num * sizeof(GType)); 499 500 for (i = 0; i < num; i++) 501 types[i] = G_TYPE_STRING; 502 503 model = gtk_list_store_newv(num, types); 504 free(types); 505 types = NULL; 506 507 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); 508 gtk_widget_set_can_focus(tree_view, FALSE); 509 510 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); 511 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE); 512 513 for (i = 0; i < num; i++) 514 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE); 515 516 gtk_list_store_append(model, &iter); 517 518 for (i = 0; i < num; i++) { 519 char fbuf[32]; 520 521 if (lat[i] <= 0.0) 522 sprintf(fbuf, "0.00"); 523 else 524 sprintf(fbuf, "%3.2f%%", lat[i]); 525 526 gtk_list_store_set(model, &iter, i, fbuf, -1); 527 } 528 529 return tree_view; 530} 531 532static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts) 533{ 534 GtkWidget *box, *frame, *tree_view; 535 double io_u_lat_u[FIO_IO_U_LAT_U_NR]; 536 double io_u_lat_m[FIO_IO_U_LAT_M_NR]; 537 const char *uranges[] = { "2", "4", "10", "20", "50", "100", 538 "250", "500", "750", "1000", }; 539 const char *mranges[] = { "2", "4", "10", "20", "50", "100", 540 "250", "500", "750", "1000", "2000", 541 ">= 2000", }; 542 543 stat_calc_lat_u(ts, io_u_lat_u); 544 stat_calc_lat_m(ts, io_u_lat_m); 545 546 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges); 547 if (tree_view) { 548 frame = gtk_frame_new("Latency buckets (usec)"); 549 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); 550 551 box = gtk_hbox_new(FALSE, 3); 552 gtk_container_add(GTK_CONTAINER(frame), box); 553 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3); 554 } 555 556 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges); 557 if (tree_view) { 558 frame = gtk_frame_new("Latency buckets (msec)"); 559 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); 560 561 box = gtk_hbox_new(FALSE, 3); 562 gtk_container_add(GTK_CONTAINER(frame), box); 563 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3); 564 } 565} 566 567static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts) 568{ 569 GtkWidget *box, *frame, *entry; 570 double usr_cpu, sys_cpu; 571 unsigned long runtime; 572 char tmp[32]; 573 574 runtime = ts->total_run_time; 575 if (runtime) { 576 double runt = (double) runtime; 577 578 usr_cpu = (double) ts->usr_time * 100 / runt; 579 sys_cpu = (double) ts->sys_time * 100 / runt; 580 } else { 581 usr_cpu = 0; 582 sys_cpu = 0; 583 } 584 585 frame = gtk_frame_new("OS resources"); 586 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); 587 588 box = gtk_hbox_new(FALSE, 3); 589 gtk_container_add(GTK_CONTAINER(frame), box); 590 591 entry = new_info_entry_in_frame(box, "User CPU"); 592 sprintf(tmp, "%3.2f%%", usr_cpu); 593 gtk_entry_set_text(GTK_ENTRY(entry), tmp); 594 entry = new_info_entry_in_frame(box, "System CPU"); 595 sprintf(tmp, "%3.2f%%", sys_cpu); 596 gtk_entry_set_text(GTK_ENTRY(entry), tmp); 597 entry = new_info_entry_in_frame(box, "Context switches"); 598 entry_set_int_value(entry, ts->ctx); 599 entry = new_info_entry_in_frame(box, "Major faults"); 600 entry_set_int_value(entry, ts->majf); 601 entry = new_info_entry_in_frame(box, "Minor faults"); 602 entry_set_int_value(entry, ts->minf); 603} 604static void gfio_add_sc_depths_tree(GtkListStore *model, 605 struct thread_stat *ts, unsigned int len, 606 int submit) 607{ 608 double io_u_dist[FIO_IO_U_MAP_NR]; 609 GtkTreeIter iter; 610 /* Bits 0, and 3-8 */ 611 const int add_mask = 0x1f9; 612 int i, j; 613 614 if (submit) 615 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist); 616 else 617 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist); 618 619 gtk_list_store_append(model, &iter); 620 621 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1); 622 623 for (i = 1, j = 0; i < len; i++) { 624 char fbuf[32]; 625 626 if (!(add_mask & (1UL << (i - 1)))) 627 sprintf(fbuf, "0.0%%"); 628 else { 629 sprintf(fbuf, "%3.1f%%", io_u_dist[j]); 630 j++; 631 } 632 633 gtk_list_store_set(model, &iter, i, fbuf, -1); 634 } 635 636} 637 638static void gfio_add_total_depths_tree(GtkListStore *model, 639 struct thread_stat *ts, unsigned int len) 640{ 641 double io_u_dist[FIO_IO_U_MAP_NR]; 642 GtkTreeIter iter; 643 /* Bits 1-6, and 8 */ 644 const int add_mask = 0x17e; 645 int i, j; 646 647 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist); 648 649 gtk_list_store_append(model, &iter); 650 651 gtk_list_store_set(model, &iter, 0, "Total", -1); 652 653 for (i = 1, j = 0; i < len; i++) { 654 char fbuf[32]; 655 656 if (!(add_mask & (1UL << (i - 1)))) 657 sprintf(fbuf, "0.0%%"); 658 else { 659 sprintf(fbuf, "%3.1f%%", io_u_dist[j]); 660 j++; 661 } 662 663 gtk_list_store_set(model, &iter, i, fbuf, -1); 664 } 665 666} 667 668static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts) 669{ 670 GtkWidget *frame, *box, *tree_view; 671 GtkTreeSelection *selection; 672 GtkListStore *model; 673 GType types[FIO_IO_U_MAP_NR + 1]; 674 int i; 675#define NR_LABELS 10 676 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" }; 677 678 frame = gtk_frame_new("IO depths"); 679 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); 680 681 box = gtk_hbox_new(FALSE, 3); 682 gtk_container_add(GTK_CONTAINER(frame), box); 683 684 for (i = 0; i < NR_LABELS; i++) 685 types[i] = G_TYPE_STRING; 686 687 model = gtk_list_store_newv(NR_LABELS, types); 688 689 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); 690 gtk_widget_set_can_focus(tree_view, FALSE); 691 692 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); 693 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE); 694 695 for (i = 0; i < NR_LABELS; i++) 696 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE); 697 698 gfio_add_total_depths_tree(model, ts, NR_LABELS); 699 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1); 700 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0); 701 702 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3); 703} 704 705static gboolean results_window_delete(GtkWidget *w, gpointer data) 706{ 707 struct gui *ui = (struct gui *) data; 708 709 gtk_widget_destroy(w); 710 ui->results_window = NULL; 711 ui->results_notebook = NULL; 712 return TRUE; 713} 714 715static GtkWidget *get_results_window(struct gui *ui) 716{ 717 GtkWidget *win, *notebook; 718 719 if (ui->results_window) 720 return ui->results_notebook; 721 722 win = gtk_window_new(GTK_WINDOW_TOPLEVEL); 723 gtk_window_set_title(GTK_WINDOW(win), "Results"); 724 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ui); 725 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ui); 726 727 notebook = gtk_notebook_new(); 728 gtk_container_add(GTK_CONTAINER(win), notebook); 729 730 ui->results_window = win; 731 ui->results_notebook = notebook; 732 return ui->results_notebook; 733} 734 735static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts, 736 struct group_run_stats *rs) 737{ 738 GtkWidget *res_win, *box, *vbox, *entry; 739 struct gui *ui = client->client_data; 740 741 gdk_threads_enter(); 742 743 res_win = get_results_window(ui); 744 745 vbox = gtk_vbox_new(FALSE, 3); 746 747 box = gtk_hbox_new(TRUE, 3); 748 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5); 749 750 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), vbox, gtk_label_new(ts->name)); 751 752 entry = new_info_entry_in_frame(box, "Name"); 753 gtk_entry_set_text(GTK_ENTRY(entry), ts->name); 754 if (strlen(ts->description)) { 755 entry = new_info_entry_in_frame(box, "Description"); 756 gtk_entry_set_text(GTK_ENTRY(entry), ts->description); 757 } 758 entry = new_info_entry_in_frame(box, "Group ID"); 759 entry_set_int_value(entry, ts->groupid); 760 entry = new_info_entry_in_frame(box, "Jobs"); 761 entry_set_int_value(entry, ts->members); 762 entry = new_info_entry_in_frame(box, "Error"); 763 entry_set_int_value(entry, ts->error); 764 entry = new_info_entry_in_frame(box, "PID"); 765 entry_set_int_value(entry, ts->pid); 766 767 if (ts->io_bytes[DDIR_READ]) 768 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ); 769 if (ts->io_bytes[DDIR_WRITE]) 770 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE); 771 772 gfio_show_latency_buckets(vbox, ts); 773 gfio_show_cpu_usage(vbox, ts); 774 gfio_show_io_depths(vbox, ts); 775 776 gtk_widget_show_all(ui->results_window); 777 gdk_threads_leave(); 778} 779 780static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd) 781{ 782#if 0 783 GtkTextBuffer *buffer; 784 GtkTextIter end; 785 786 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui.textview)); 787 gdk_threads_enter(); 788 gtk_text_buffer_get_end_iter(buffer, &end); 789 gtk_text_buffer_insert(buffer, &end, buf, -1); 790 gdk_threads_leave(); 791 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(ui.textview), 792 &end, 0.0, FALSE, 0.0,0.0); 793#else 794 fio_client_ops.text_op(client, cmd); 795#endif 796} 797 798static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd) 799{ 800 gdk_threads_enter(); 801 printf("gfio_disk_util_op called\n"); 802 fio_client_ops.disk_util(client, cmd); 803 gdk_threads_leave(); 804} 805 806extern int sum_stat_clients; 807extern struct thread_stat client_ts; 808extern struct group_run_stats client_gs; 809 810static int sum_stat_nr; 811 812static void gfio_thread_status_op(struct fio_client *client, 813 struct fio_net_cmd *cmd) 814{ 815 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload; 816 817 gfio_display_ts(client, &p->ts, &p->rs); 818 819 if (sum_stat_clients == 1) 820 return; 821 822 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr); 823 sum_group_stats(&client_gs, &p->rs); 824 825 client_ts.members++; 826 client_ts.groupid = p->ts.groupid; 827 828 if (++sum_stat_nr == sum_stat_clients) { 829 strcpy(client_ts.name, "All clients"); 830 gfio_display_ts(client, &client_ts, &client_gs); 831 } 832} 833 834static void gfio_group_stats_op(struct fio_client *client, 835 struct fio_net_cmd *cmd) 836{ 837 gdk_threads_enter(); 838 printf("gfio_group_stats_op called\n"); 839 fio_client_ops.group_stats(client, cmd); 840 gdk_threads_leave(); 841} 842 843static void gfio_update_eta(struct jobs_eta *je) 844{ 845 static int eta_good; 846 char eta_str[128]; 847 char output[256]; 848 char tmp[32]; 849 double perc = 0.0; 850 int i2p = 0; 851 852 gdk_threads_enter(); 853 854 eta_str[0] = '\0'; 855 output[0] = '\0'; 856 857 if (je->eta_sec != INT_MAX && je->elapsed_sec) { 858 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec); 859 eta_to_str(eta_str, je->eta_sec); 860 } 861 862 sprintf(tmp, "%u", je->nr_running); 863 gtk_entry_set_text(GTK_ENTRY(ui.eta.jobs), tmp); 864 sprintf(tmp, "%u", je->files_open); 865 gtk_entry_set_text(GTK_ENTRY(ui.eta.files), tmp); 866 867#if 0 868 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) { 869 if (je->m_rate || je->t_rate) { 870 char *tr, *mr; 871 872 mr = num2str(je->m_rate, 4, 0, i2p); 873 tr = num2str(je->t_rate, 4, 0, i2p); 874 gtk_entry_set_text(GTK_ENTRY(ui.eta); 875 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr); 876 free(tr); 877 free(mr); 878 } else if (je->m_iops || je->t_iops) 879 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops); 880 881 gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_bw), "---"); 882 gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_iops), "---"); 883 gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_bw), "---"); 884 gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_iops), "---"); 885#endif 886 887 if (je->eta_sec != INT_MAX && je->nr_running) { 888 char *iops_str[2]; 889 char *rate_str[2]; 890 891 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running) 892 strcpy(output, "-.-% done"); 893 else { 894 eta_good = 1; 895 perc *= 100.0; 896 sprintf(output, "%3.1f%% done", perc); 897 } 898 899 rate_str[0] = num2str(je->rate[0], 5, 10, i2p); 900 rate_str[1] = num2str(je->rate[1], 5, 10, i2p); 901 902 iops_str[0] = num2str(je->iops[0], 4, 1, 0); 903 iops_str[1] = num2str(je->iops[1], 4, 1, 0); 904 905 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_bw), rate_str[0]); 906 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_iops), iops_str[0]); 907 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_bw), rate_str[1]); 908 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_iops), iops_str[1]); 909 910 free(rate_str[0]); 911 free(rate_str[1]); 912 free(iops_str[0]); 913 free(iops_str[1]); 914 } 915 916 if (eta_str[0]) { 917 char *dst = output + strlen(output); 918 919 sprintf(dst, " - %s", eta_str); 920 } 921 922 gfio_update_thread_status(output, perc); 923 gdk_threads_leave(); 924} 925 926static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd) 927{ 928 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload; 929 const char *os, *arch; 930 char buf[64]; 931 932 os = fio_get_os_string(probe->os); 933 if (!os) 934 os = "unknown"; 935 936 arch = fio_get_arch_string(probe->arch); 937 if (!arch) 938 os = "unknown"; 939 940 if (!client->name) 941 client->name = strdup((char *) probe->hostname); 942 943 gdk_threads_enter(); 944 945 gtk_label_set_text(GTK_LABEL(ui.probe.hostname), (char *) probe->hostname); 946 gtk_label_set_text(GTK_LABEL(ui.probe.os), os); 947 gtk_label_set_text(GTK_LABEL(ui.probe.arch), arch); 948 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch); 949 gtk_label_set_text(GTK_LABEL(ui.probe.fio_ver), buf); 950 951 gdk_threads_leave(); 952} 953 954static void gfio_update_thread_status(char *status_message, double perc) 955{ 956 static char message[100]; 957 const char *m = message; 958 959 strncpy(message, status_message, sizeof(message) - 1); 960 gtk_progress_bar_set_text( 961 GTK_PROGRESS_BAR(ui.thread_status_pb), m); 962 gtk_progress_bar_set_fraction( 963 GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0); 964 gtk_widget_queue_draw(ui.window); 965} 966 967static void gfio_quit_op(struct fio_client *client) 968{ 969 struct gui *ui = client->client_data; 970 971 gdk_threads_enter(); 972 gfio_set_connected(ui, 0); 973 gdk_threads_leave(); 974} 975 976static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd) 977{ 978 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload; 979 struct gui *ui = client->client_data; 980 char tmp[8]; 981 int i; 982 983 p->iodepth = le32_to_cpu(p->iodepth); 984 p->rw = le32_to_cpu(p->rw); 985 986 for (i = 0; i < 2; i++) { 987 p->min_bs[i] = le32_to_cpu(p->min_bs[i]); 988 p->max_bs[i] = le32_to_cpu(p->max_bs[i]); 989 } 990 991 p->numjobs = le32_to_cpu(p->numjobs); 992 p->group_reporting = le32_to_cpu(p->group_reporting); 993 994 gdk_threads_enter(); 995 996 gtk_entry_set_text(GTK_ENTRY(ui->eta.name), (gchar *) p->jobname); 997 gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), ddir_str(p->rw)); 998 gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), (gchar *) p->ioengine); 999 1000 sprintf(tmp, "%u", p->iodepth); 1001 gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), tmp); 1002 1003 gdk_threads_leave(); 1004} 1005 1006static void gfio_client_timed_out(struct fio_client *client) 1007{ 1008 struct gui *ui = client->client_data; 1009 GtkWidget *dialog, *label, *content; 1010 char buf[256]; 1011 1012 gdk_threads_enter(); 1013 1014 gfio_set_connected(ui, 0); 1015 clear_ui_info(ui); 1016 1017 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname); 1018 1019 dialog = gtk_dialog_new_with_buttons("Timed out!", 1020 GTK_WINDOW(ui->window), 1021 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, 1022 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); 1023 1024 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); 1025 label = gtk_label_new((const gchar *) buf); 1026 gtk_container_add(GTK_CONTAINER(content), label); 1027 gtk_widget_show_all(dialog); 1028 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT); 1029 1030 gtk_dialog_run(GTK_DIALOG(dialog)); 1031 gtk_widget_destroy(dialog); 1032 1033 gdk_threads_leave(); 1034} 1035 1036struct client_ops gfio_client_ops = { 1037 .text_op = gfio_text_op, 1038 .disk_util = gfio_disk_util_op, 1039 .thread_status = gfio_thread_status_op, 1040 .group_stats = gfio_group_stats_op, 1041 .eta = gfio_update_eta, 1042 .probe = gfio_probe_op, 1043 .quit = gfio_quit_op, 1044 .add_job = gfio_add_job_op, 1045 .timed_out = gfio_client_timed_out, 1046 .stay_connected = 1, 1047}; 1048 1049static void quit_clicked(__attribute__((unused)) GtkWidget *widget, 1050 __attribute__((unused)) gpointer data) 1051{ 1052 gtk_main_quit(); 1053} 1054 1055static void *job_thread(void *arg) 1056{ 1057 fio_handle_clients(&gfio_client_ops); 1058 return NULL; 1059} 1060 1061static int send_job_files(struct gui *ui) 1062{ 1063 int i, ret = 0; 1064 1065 for (i = 0; i < ui->nr_job_files; i++) { 1066 ret = fio_clients_send_ini(ui->job_files[i]); 1067 if (ret) 1068 break; 1069 1070 free(ui->job_files[i]); 1071 ui->job_files[i] = NULL; 1072 } 1073 while (i < ui->nr_job_files) { 1074 free(ui->job_files[i]); 1075 ui->job_files[i] = NULL; 1076 i++; 1077 } 1078 1079 return ret; 1080} 1081 1082static void start_job_thread(struct gui *ui) 1083{ 1084 if (send_job_files(ui)) { 1085 printf("Yeah, I didn't really like those options too much.\n"); 1086 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1); 1087 return; 1088 } 1089} 1090 1091static void start_job_clicked(__attribute__((unused)) GtkWidget *widget, 1092 gpointer data) 1093{ 1094 struct gui *ui = data; 1095 1096 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0); 1097 start_job_thread(ui); 1098} 1099 1100static void file_open(GtkWidget *w, gpointer data); 1101 1102static void connect_clicked(GtkWidget *widget, gpointer data) 1103{ 1104 struct gui *ui = data; 1105 1106 if (!ui->connected) { 1107 if (!ui->nr_job_files) 1108 file_open(widget, data); 1109 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running"); 1110 fio_clients_connect(); 1111 pthread_create(&ui->t, NULL, job_thread, NULL); 1112 gfio_set_connected(ui, 1); 1113 } else { 1114 fio_clients_terminate(); 1115 gfio_set_connected(ui, 0); 1116 clear_ui_info(ui); 1117 } 1118} 1119 1120static void add_button(struct gui *ui, int i, GtkWidget *buttonbox, 1121 struct button_spec *buttonspec) 1122{ 1123 ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext); 1124 g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui); 1125 gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3); 1126 gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext); 1127 gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive); 1128} 1129 1130static void add_buttons(struct gui *ui, 1131 struct button_spec *buttonlist, 1132 int nbuttons) 1133{ 1134 int i; 1135 1136 for (i = 0; i < nbuttons; i++) 1137 add_button(ui, i, ui->buttonbox, &buttonlist[i]); 1138} 1139 1140static void on_info_bar_response(GtkWidget *widget, gint response, 1141 gpointer data) 1142{ 1143 if (response == GTK_RESPONSE_OK) { 1144 gtk_widget_destroy(widget); 1145 ui.error_info_bar = NULL; 1146 } 1147} 1148 1149void report_error(GError *error) 1150{ 1151 if (ui.error_info_bar == NULL) { 1152 ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK, 1153 GTK_RESPONSE_OK, 1154 NULL); 1155 g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL); 1156 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar), 1157 GTK_MESSAGE_ERROR); 1158 1159 ui.error_label = gtk_label_new(error->message); 1160 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar)); 1161 gtk_container_add(GTK_CONTAINER(container), ui.error_label); 1162 1163 gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0); 1164 gtk_widget_show_all(ui.vbox); 1165 } else { 1166 char buffer[256]; 1167 snprintf(buffer, sizeof(buffer), "Failed to open file."); 1168 gtk_label_set(GTK_LABEL(ui.error_label), buffer); 1169 } 1170} 1171 1172static int get_connection_details(char **host, int *port, int *type, 1173 int *server_start) 1174{ 1175 GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo; 1176 GtkWidget *button; 1177 char *typeentry; 1178 1179 dialog = gtk_dialog_new_with_buttons("Connection details", 1180 GTK_WINDOW(ui.window), 1181 GTK_DIALOG_DESTROY_WITH_PARENT, 1182 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, 1183 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL); 1184 1185 frame = gtk_frame_new("Hostname / socket name"); 1186 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); 1187 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); 1188 1189 box = gtk_vbox_new(FALSE, 6); 1190 gtk_container_add(GTK_CONTAINER(frame), box); 1191 1192 hbox = gtk_hbox_new(TRUE, 10); 1193 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); 1194 hentry = gtk_entry_new(); 1195 gtk_entry_set_text(GTK_ENTRY(hentry), "localhost"); 1196 gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 0); 1197 1198 frame = gtk_frame_new("Port"); 1199 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); 1200 box = gtk_vbox_new(FALSE, 10); 1201 gtk_container_add(GTK_CONTAINER(frame), box); 1202 1203 hbox = gtk_hbox_new(TRUE, 4); 1204 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); 1205 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT); 1206 1207 frame = gtk_frame_new("Type"); 1208 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); 1209 box = gtk_vbox_new(FALSE, 10); 1210 gtk_container_add(GTK_CONTAINER(frame), box); 1211 1212 hbox = gtk_hbox_new(TRUE, 4); 1213 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); 1214 1215 combo = gtk_combo_box_text_new(); 1216 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv4"); 1217 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv6"); 1218 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "local socket"); 1219 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0); 1220 1221 gtk_container_add(GTK_CONTAINER(hbox), combo); 1222 1223 frame = gtk_frame_new("Options"); 1224 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); 1225 box = gtk_vbox_new(FALSE, 10); 1226 gtk_container_add(GTK_CONTAINER(frame), box); 1227 1228 hbox = gtk_hbox_new(TRUE, 4); 1229 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); 1230 1231 button = gtk_check_button_new_with_label("Auto-spawn fio backend"); 1232 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1); 1233 gtk_widget_set_tooltip_text(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."); 1234 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6); 1235 1236 gtk_widget_show_all(dialog); 1237 1238 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) { 1239 gtk_widget_destroy(dialog); 1240 return 1; 1241 } 1242 1243 *host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry))); 1244 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry)); 1245 1246 typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo)); 1247 if (!typeentry || !strncmp(typeentry, "IPv4", 4)) 1248 *type = Fio_client_ipv4; 1249 else if (!strncmp(typeentry, "IPv6", 4)) 1250 *type = Fio_client_ipv6; 1251 else 1252 *type = Fio_client_socket; 1253 g_free(typeentry); 1254 1255 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)); 1256 1257 gtk_widget_destroy(dialog); 1258 return 0; 1259} 1260 1261static void file_open(GtkWidget *w, gpointer data) 1262{ 1263 GtkWidget *dialog; 1264 GSList *filenames, *fn_glist; 1265 GtkFileFilter *filter; 1266 char *host; 1267 int port, type, server_start; 1268 1269 dialog = gtk_file_chooser_dialog_new("Open File", 1270 GTK_WINDOW(ui.window), 1271 GTK_FILE_CHOOSER_ACTION_OPEN, 1272 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, 1273 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, 1274 NULL); 1275 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE); 1276 1277 filter = gtk_file_filter_new(); 1278 gtk_file_filter_add_pattern(filter, "*.fio"); 1279 gtk_file_filter_add_pattern(filter, "*.job"); 1280 gtk_file_filter_add_mime_type(filter, "text/fio"); 1281 gtk_file_filter_set_name(filter, "Fio job file"); 1282 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter); 1283 1284 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) { 1285 gtk_widget_destroy(dialog); 1286 return; 1287 } 1288 1289 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog)); 1290 1291 gtk_widget_destroy(dialog); 1292 1293 if (get_connection_details(&host, &port, &type, &server_start)) 1294 goto err; 1295 1296 filenames = fn_glist; 1297 while (filenames != NULL) { 1298 ui.job_files = realloc(ui.job_files, (ui.nr_job_files + 1) * sizeof(char *)); 1299 ui.job_files[ui.nr_job_files] = strdup(filenames->data); 1300 ui.nr_job_files++; 1301 1302 ui.client = fio_client_add_explicit(&gfio_client_ops, host, type, port); 1303 if (!ui.client) { 1304 GError *error; 1305 1306 error = g_error_new(g_quark_from_string("fio"), 1, 1307 "Failed to add client %s", host); 1308 report_error(error); 1309 g_error_free(error); 1310 } 1311 ui.client->client_data = &ui; 1312 1313 g_free(filenames->data); 1314 filenames = g_slist_next(filenames); 1315 } 1316 free(host); 1317err: 1318 g_slist_free(fn_glist); 1319} 1320 1321static void file_save(GtkWidget *w, gpointer data) 1322{ 1323 GtkWidget *dialog; 1324 1325 dialog = gtk_file_chooser_dialog_new("Save File", 1326 GTK_WINDOW(ui.window), 1327 GTK_FILE_CHOOSER_ACTION_SAVE, 1328 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, 1329 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, 1330 NULL); 1331 1332 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE); 1333 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document"); 1334 1335 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { 1336 char *filename; 1337 1338 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); 1339 // save_job_file(filename); 1340 g_free(filename); 1341 } 1342 gtk_widget_destroy(dialog); 1343} 1344 1345static void preferences(GtkWidget *w, gpointer data) 1346{ 1347 GtkWidget *dialog, *frame, *box, **buttons; 1348 int i; 1349 1350 dialog = gtk_dialog_new_with_buttons("Preferences", 1351 GTK_WINDOW(ui.window), 1352 GTK_DIALOG_DESTROY_WITH_PARENT, 1353 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, 1354 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, 1355 NULL); 1356 1357 frame = gtk_frame_new("Debug logging"); 1358 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5); 1359 box = gtk_hbox_new(FALSE, 6); 1360 gtk_container_add(GTK_CONTAINER(frame), box); 1361 1362 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX); 1363 1364 for (i = 0; i < FD_DEBUG_MAX; i++) { 1365 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name); 1366 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help); 1367 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6); 1368 } 1369 1370 gtk_widget_show_all(dialog); 1371 1372 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) { 1373 gtk_widget_destroy(dialog); 1374 return; 1375 } 1376 1377 for (i = 0; i < FD_DEBUG_MAX; i++) { 1378 int set; 1379 1380 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i])); 1381 if (set) 1382 fio_debug |= (1UL << i); 1383 } 1384 1385 gtk_widget_destroy(dialog); 1386} 1387 1388static void about_dialog(GtkWidget *w, gpointer data) 1389{ 1390 gtk_show_about_dialog(NULL, 1391 "program-name", "gfio", 1392 "comments", "Gtk2 UI for fio", 1393 "license", "GPLv2", 1394 "version", fio_version_string, 1395 "copyright", "Jens Axboe <axboe@kernel.dk> 2012", 1396 "logo-icon-name", "fio", 1397 /* Must be last: */ 1398 NULL, NULL, 1399 NULL); 1400} 1401 1402static GtkActionEntry menu_items[] = { 1403 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL}, 1404 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL}, 1405 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) }, 1406 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) }, 1407 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) }, 1408 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) }, 1409 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) }, 1410}; 1411static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]); 1412 1413static const gchar *ui_string = " \ 1414 <ui> \ 1415 <menubar name=\"MainMenu\"> \ 1416 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \ 1417 <menuitem name=\"Open\" action=\"OpenFile\" /> \ 1418 <menuitem name=\"Save\" action=\"SaveFile\" /> \ 1419 <separator name=\"Separator\"/> \ 1420 <menuitem name=\"Preferences\" action=\"Preferences\" /> \ 1421 <separator name=\"Separator2\"/> \ 1422 <menuitem name=\"Quit\" action=\"Quit\" /> \ 1423 </menu> \ 1424 <menu name=\"Help\" action=\"HelpMenuAction\"> \ 1425 <menuitem name=\"About\" action=\"About\" /> \ 1426 </menu> \ 1427 </menubar> \ 1428 </ui> \ 1429"; 1430 1431static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager) 1432{ 1433 GtkActionGroup *action_group = gtk_action_group_new("Menu"); 1434 GError *error = 0; 1435 1436 action_group = gtk_action_group_new("Menu"); 1437 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0); 1438 1439 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0); 1440 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error); 1441 1442 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager)); 1443 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu"); 1444} 1445 1446void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar, 1447 GtkWidget *vbox, GtkUIManager *ui_manager) 1448{ 1449 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0); 1450} 1451 1452static void init_ui(int *argc, char **argv[], struct gui *ui) 1453{ 1454 GtkSettings *settings; 1455 GtkUIManager *uimanager; 1456 GtkWidget *menu, *probe, *probe_frame, *probe_box; 1457 1458 memset(ui, 0, sizeof(*ui)); 1459 1460 /* Magical g*thread incantation, you just need this thread stuff. 1461 * Without it, the update that happens in gfio_update_thread_status 1462 * doesn't really happen in a timely fashion, you need expose events 1463 */ 1464 if (!g_thread_supported()) 1465 g_thread_init(NULL); 1466 gdk_threads_init(); 1467 1468 gtk_init(argc, argv); 1469 settings = gtk_settings_get_default(); 1470 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting"); 1471 g_type_init(); 1472 1473 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 1474 gtk_window_set_title(GTK_WINDOW(ui->window), "fio"); 1475 gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500); 1476 1477 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL); 1478 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL); 1479 1480 ui->vbox = gtk_vbox_new(FALSE, 0); 1481 gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox); 1482 1483 uimanager = gtk_ui_manager_new(); 1484 menu = get_menubar_menu(ui->window, uimanager); 1485 gfio_ui_setup(settings, menu, ui->vbox, uimanager); 1486 1487 /* 1488 * Set up alignments for widgets at the top of ui, 1489 * align top left, expand horizontally but not vertically 1490 */ 1491 ui->topalign = gtk_alignment_new(0, 0, 1, 0); 1492 ui->topvbox = gtk_vbox_new(FALSE, 3); 1493 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox); 1494 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0); 1495 1496 probe = gtk_frame_new("Job"); 1497 gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3); 1498 probe_frame = gtk_vbox_new(FALSE, 3); 1499 gtk_container_add(GTK_CONTAINER(probe), probe_frame); 1500 1501 probe_box = gtk_hbox_new(FALSE, 3); 1502 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3); 1503 ui->probe.hostname = new_info_label_in_frame(probe_box, "Host"); 1504 ui->probe.os = new_info_label_in_frame(probe_box, "OS"); 1505 ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture"); 1506 ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version"); 1507 1508 probe_box = gtk_hbox_new(FALSE, 3); 1509 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3); 1510 1511 ui->eta.name = new_info_entry_in_frame(probe_box, "Name"); 1512 ui->eta.iotype = new_info_entry_in_frame(probe_box, "IO"); 1513 ui->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine"); 1514 ui->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth"); 1515 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs"); 1516 ui->eta.files = new_info_entry_in_frame(probe_box, "Open files"); 1517 1518 probe_box = gtk_hbox_new(FALSE, 3); 1519 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3); 1520 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW"); 1521 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS"); 1522 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW"); 1523 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS"); 1524 1525 /* 1526 * Only add this if we have a commit rate 1527 */ 1528#if 0 1529 probe_box = gtk_hbox_new(FALSE, 3); 1530 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3); 1531 1532 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW"); 1533 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS"); 1534 1535 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW"); 1536 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS"); 1537#endif 1538 1539 /* 1540 * Add a text box for text op messages 1541 */ 1542 ui->textview = gtk_text_view_new(); 1543 ui->text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->textview)); 1544 gtk_text_buffer_set_text(ui->text, "", -1); 1545 gtk_text_view_set_editable(GTK_TEXT_VIEW(ui->textview), FALSE); 1546 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ui->textview), FALSE); 1547 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL); 1548 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window), 1549 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); 1550 gtk_container_add(GTK_CONTAINER(ui->scrolled_window), ui->textview); 1551 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window, 1552 TRUE, TRUE, 0); 1553 1554 /* 1555 * Set up alignments for widgets at the bottom of ui, 1556 * align bottom left, expand horizontally but not vertically 1557 */ 1558 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0); 1559 ui->buttonbox = gtk_hbox_new(FALSE, 0); 1560 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox); 1561 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign, 1562 FALSE, FALSE, 0); 1563 1564 add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist)); 1565 1566 /* 1567 * Set up thread status progress bar 1568 */ 1569 ui->thread_status_pb = gtk_progress_bar_new(); 1570 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0); 1571 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections"); 1572 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb); 1573 1574 1575 gtk_widget_show_all(ui->window); 1576} 1577 1578int main(int argc, char *argv[], char *envp[]) 1579{ 1580 if (initialize_fio(envp)) 1581 return 1; 1582 if (fio_init_options()) 1583 return 1; 1584 1585 init_ui(&argc, &argv, &ui); 1586 1587 gdk_threads_enter(); 1588 gtk_main(); 1589 gdk_threads_leave(); 1590 return 0; 1591} 1592