gfio.c revision 1c1e4a5bbe3ebb614eacfa902c06564e6739a750
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 GtkTextBuffer *text; 92 struct probe_widget probe; 93 struct eta_widget eta; 94 int connected; 95 pthread_t t; 96 97 struct fio_client *client; 98 int nr_job_files; 99 char **job_files; 100} ui; 101 102static void clear_ui_info(struct gui *ui) 103{ 104 gtk_label_set_text(GTK_LABEL(ui->probe.hostname), ""); 105 gtk_label_set_text(GTK_LABEL(ui->probe.os), ""); 106 gtk_label_set_text(GTK_LABEL(ui->probe.arch), ""); 107 gtk_label_set_text(GTK_LABEL(ui->probe.fio_ver), ""); 108 gtk_label_set_text(GTK_LABEL(ui->eta.name), ""); 109 gtk_label_set_text(GTK_LABEL(ui->eta.iotype), ""); 110 gtk_label_set_text(GTK_LABEL(ui->eta.ioengine), ""); 111 gtk_label_set_text(GTK_LABEL(ui->eta.iodepth), ""); 112 gtk_label_set_text(GTK_LABEL(ui->eta.jobs), ""); 113 gtk_label_set_text(GTK_LABEL(ui->eta.files), ""); 114 gtk_label_set_text(GTK_LABEL(ui->eta.read_bw), ""); 115 gtk_label_set_text(GTK_LABEL(ui->eta.read_iops), ""); 116 gtk_label_set_text(GTK_LABEL(ui->eta.write_bw), ""); 117 gtk_label_set_text(GTK_LABEL(ui->eta.write_iops), ""); 118} 119 120static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label) 121{ 122 GtkWidget *entry, *frame; 123 124 frame = gtk_frame_new(label); 125 entry = gtk_entry_new(); 126 gtk_entry_set_editable(GTK_ENTRY(entry), 0); 127 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3); 128 gtk_container_add(GTK_CONTAINER(frame), entry); 129 130 return entry; 131} 132 133static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label) 134{ 135 GtkWidget *label_widget; 136 GtkWidget *frame; 137 138 frame = gtk_frame_new(label); 139 label_widget = gtk_label_new(NULL); 140 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3); 141 gtk_container_add(GTK_CONTAINER(frame), label_widget); 142 143 return label_widget; 144} 145 146static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval) 147{ 148 GtkWidget *button, *box; 149 150 box = gtk_hbox_new(FALSE, 3); 151 gtk_container_add(GTK_CONTAINER(hbox), box); 152 153 button = gtk_spin_button_new_with_range(min, max, 1.0); 154 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0); 155 156 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID); 157 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval); 158 159 return button; 160} 161 162static void gfio_set_connected(struct gui *ui, int connected) 163{ 164 if (connected) { 165 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1); 166 ui->connected = 1; 167 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Disconnect"); 168 } else { 169 ui->connected = 0; 170 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Connect"); 171 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0); 172 } 173} 174 175static void label_set_int_value(GtkWidget *entry, unsigned int val) 176{ 177 char tmp[80]; 178 179 sprintf(tmp, "%u", val); 180 gtk_label_set_text(GTK_LABEL(entry), tmp); 181} 182 183static void entry_set_int_value(GtkWidget *entry, unsigned int val) 184{ 185 char tmp[80]; 186 187 sprintf(tmp, "%u", val); 188 gtk_entry_set_text(GTK_ENTRY(entry), tmp); 189} 190 191static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min, 192 unsigned long max, double mean, double dev) 193{ 194 const char *base = "(usec)"; 195 GtkWidget *hbox, *label, *frame; 196 char *minp, *maxp; 197 char tmp[64]; 198 199 if (!usec_to_msec(&min, &max, &mean, &dev)) 200 base = "(msec)"; 201 202 minp = num2str(min, 6, 1, 0); 203 maxp = num2str(max, 6, 1, 0); 204 205 sprintf(tmp, "%s %s", name, base); 206 frame = gtk_frame_new(tmp); 207 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); 208 209 hbox = gtk_hbox_new(FALSE, 3); 210 gtk_container_add(GTK_CONTAINER(frame), hbox); 211 212 label = new_info_label_in_frame(hbox, "Minimum"); 213 gtk_label_set_text(GTK_LABEL(label), minp); 214 label = new_info_label_in_frame(hbox, "Maximum"); 215 gtk_label_set_text(GTK_LABEL(label), maxp); 216 label = new_info_label_in_frame(hbox, "Average"); 217 sprintf(tmp, "%5.02f", mean); 218 gtk_label_set_text(GTK_LABEL(label), tmp); 219 label = new_info_label_in_frame(hbox, "Standard deviation"); 220 sprintf(tmp, "%5.02f", dev); 221 gtk_label_set_text(GTK_LABEL(label), tmp); 222 223 free(minp); 224 free(maxp); 225 226} 227 228static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs, 229 struct thread_stat *ts, int ddir) 230{ 231 const char *ddir_label[2] = { "Read", "Write" }; 232 GtkWidget *frame, *label, *box, *vbox; 233 unsigned long min, max, runt; 234 unsigned long long bw, iops; 235 double mean, dev; 236 char *io_p, *bw_p, *iops_p; 237 int i2p; 238 239 if (!ts->runtime[ddir]) 240 return; 241 242 i2p = is_power_of_2(rs->kb_base); 243 runt = ts->runtime[ddir]; 244 245 bw = (1000 * ts->io_bytes[ddir]) / runt; 246 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p); 247 bw_p = num2str(bw, 6, 1, i2p); 248 249 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt; 250 iops_p = num2str(iops, 6, 1, 0); 251 252 box = gtk_hbox_new(FALSE, 3); 253 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3); 254 255 frame = gtk_frame_new(ddir_label[ddir]); 256 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5); 257 258 vbox = gtk_vbox_new(FALSE, 3); 259 gtk_container_add(GTK_CONTAINER(frame), vbox); 260 261 box = gtk_hbox_new(FALSE, 3); 262 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 3); 263 264 label = new_info_label_in_frame(box, "IO"); 265 gtk_label_set_text(GTK_LABEL(label), io_p); 266 label = new_info_label_in_frame(box, "Bandwidth"); 267 gtk_label_set_text(GTK_LABEL(label), bw_p); 268 label = new_info_label_in_frame(box, "IOPS"); 269 gtk_label_set_text(GTK_LABEL(label), iops_p); 270 label = new_info_label_in_frame(box, "Runtime (msec)"); 271 label_set_int_value(label, ts->runtime[ddir]); 272 273 frame = gtk_frame_new("Latency"); 274 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); 275 276 vbox = gtk_vbox_new(FALSE, 3); 277 gtk_container_add(GTK_CONTAINER(frame), vbox); 278 279 if (calc_lat(&ts->slat_stat[ddir], &min, &max, &mean, &dev)) 280 gfio_show_lat(vbox, "Submission latency", min, max, mean, dev); 281 if (calc_lat(&ts->clat_stat[ddir], &min, &max, &mean, &dev)) 282 gfio_show_lat(vbox, "Completion latency", min, max, mean, dev); 283 if (calc_lat(&ts->lat_stat[ddir], &min, &max, &mean, &dev)) 284 gfio_show_lat(vbox, "Total latency", min, max, mean, dev); 285 286 free(io_p); 287 free(bw_p); 288 free(iops_p); 289} 290 291static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts, 292 struct group_run_stats *rs) 293{ 294 GtkWidget *dialog, *box, *vbox, *entry, *content; 295 struct gui *ui = client->client_data; 296 297 gdk_threads_enter(); 298 299 dialog = gtk_dialog_new_with_buttons("Results", GTK_WINDOW(ui->window), 300 GTK_DIALOG_DESTROY_WITH_PARENT, 301 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL); 302 303 g_signal_connect_swapped(dialog, "response", 304 G_CALLBACK(gtk_widget_destroy), 305 dialog); 306 307 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); 308 309 vbox = gtk_vbox_new(FALSE, 3); 310 gtk_container_add(GTK_CONTAINER(content), vbox); 311 312 box = gtk_hbox_new(TRUE, 3); 313 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5); 314 315 entry = new_info_entry_in_frame(box, "Name"); 316 gtk_entry_set_text(GTK_ENTRY(entry), ts->name); 317 if (strlen(ts->description)) { 318 entry = new_info_entry_in_frame(box, "Description"); 319 gtk_entry_set_text(GTK_ENTRY(entry), ts->description); 320 } 321 entry = new_info_entry_in_frame(box, "Group ID"); 322 entry_set_int_value(entry, ts->groupid); 323 entry = new_info_entry_in_frame(box, "Jobs"); 324 entry_set_int_value(entry, ts->members); 325 entry = new_info_entry_in_frame(box, "Error"); 326 entry_set_int_value(entry, ts->error); 327 entry = new_info_entry_in_frame(box, "PID"); 328 entry_set_int_value(entry, ts->pid); 329 330 if (ts->io_bytes[DDIR_READ]) 331 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ); 332 if (ts->io_bytes[DDIR_WRITE]) 333 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE); 334 335 gtk_widget_show_all(dialog); 336 337 gdk_threads_leave(); 338} 339 340static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd) 341{ 342#if 0 343 GtkTextBuffer *buffer; 344 GtkTextIter end; 345 346 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui.textview)); 347 gdk_threads_enter(); 348 gtk_text_buffer_get_end_iter(buffer, &end); 349 gtk_text_buffer_insert(buffer, &end, buf, -1); 350 gdk_threads_leave(); 351 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(ui.textview), 352 &end, 0.0, FALSE, 0.0,0.0); 353#else 354 fio_client_ops.text_op(client, cmd); 355#endif 356} 357 358static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd) 359{ 360 printf("gfio_disk_util_op called\n"); 361 fio_client_ops.disk_util(client, cmd); 362} 363 364extern int sum_stat_clients; 365extern struct thread_stat client_ts; 366extern struct group_run_stats client_gs; 367 368static int sum_stat_nr; 369 370static void gfio_thread_status_op(struct fio_client *client, 371 struct fio_net_cmd *cmd) 372{ 373 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload; 374 375 gfio_display_ts(client, &p->ts, &p->rs); 376 377 if (sum_stat_clients == 1) 378 return; 379 380 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr); 381 sum_group_stats(&client_gs, &p->rs); 382 383 client_ts.members++; 384 client_ts.groupid = p->ts.groupid; 385 386 if (++sum_stat_nr == sum_stat_clients) { 387 strcpy(client_ts.name, "All clients"); 388 gfio_display_ts(client, &client_ts, &client_gs); 389 } 390} 391 392static void gfio_group_stats_op(struct fio_client *client, 393 struct fio_net_cmd *cmd) 394{ 395 printf("gfio_group_stats_op called\n"); 396 fio_client_ops.group_stats(client, cmd); 397} 398 399static void gfio_update_eta(struct jobs_eta *je) 400{ 401 static int eta_good; 402 char eta_str[128]; 403 char output[256]; 404 char tmp[32]; 405 double perc = 0.0; 406 int i2p = 0; 407 408 eta_str[0] = '\0'; 409 output[0] = '\0'; 410 411 if (je->eta_sec != INT_MAX && je->elapsed_sec) { 412 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec); 413 eta_to_str(eta_str, je->eta_sec); 414 } 415 416 sprintf(tmp, "%u", je->nr_running); 417 gtk_label_set_text(GTK_LABEL(ui.eta.jobs), tmp); 418 sprintf(tmp, "%u", je->files_open); 419 gtk_label_set_text(GTK_LABEL(ui.eta.files), tmp); 420 421#if 0 422 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) { 423 if (je->m_rate || je->t_rate) { 424 char *tr, *mr; 425 426 mr = num2str(je->m_rate, 4, 0, i2p); 427 tr = num2str(je->t_rate, 4, 0, i2p); 428 gtk_label_set_text(GTK_LABEL(ui.eta. 429 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr); 430 free(tr); 431 free(mr); 432 } else if (je->m_iops || je->t_iops) 433 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops); 434 435 gtk_label_set_text(GTK_LABEL(ui.eta.cr_bw), "---"); 436 gtk_label_set_text(GTK_LABEL(ui.eta.cr_iops), "---"); 437 gtk_label_set_text(GTK_LABEL(ui.eta.cw_bw), "---"); 438 gtk_label_set_text(GTK_LABEL(ui.eta.cw_iops), "---"); 439#endif 440 441 if (je->eta_sec != INT_MAX && je->nr_running) { 442 char *iops_str[2]; 443 char *rate_str[2]; 444 445 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running) 446 strcpy(output, "-.-% done"); 447 else { 448 eta_good = 1; 449 perc *= 100.0; 450 sprintf(output, "%3.1f%% done", perc); 451 } 452 453 rate_str[0] = num2str(je->rate[0], 5, 10, i2p); 454 rate_str[1] = num2str(je->rate[1], 5, 10, i2p); 455 456 iops_str[0] = num2str(je->iops[0], 4, 1, 0); 457 iops_str[1] = num2str(je->iops[1], 4, 1, 0); 458 459 gtk_label_set_text(GTK_LABEL(ui.eta.read_bw), rate_str[0]); 460 gtk_label_set_text(GTK_LABEL(ui.eta.read_iops), iops_str[0]); 461 gtk_label_set_text(GTK_LABEL(ui.eta.write_bw), rate_str[1]); 462 gtk_label_set_text(GTK_LABEL(ui.eta.write_iops), iops_str[1]); 463 464 free(rate_str[0]); 465 free(rate_str[1]); 466 free(iops_str[0]); 467 free(iops_str[1]); 468 } 469 470 if (eta_str[0]) { 471 char *dst = output + strlen(output); 472 473 sprintf(dst, " - %s", eta_str); 474 } 475 476 gfio_update_thread_status(output, perc); 477} 478 479static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd) 480{ 481 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload; 482 const char *os, *arch; 483 char buf[64]; 484 485 os = fio_get_os_string(probe->os); 486 if (!os) 487 os = "unknown"; 488 489 arch = fio_get_arch_string(probe->arch); 490 if (!arch) 491 os = "unknown"; 492 493 if (!client->name) 494 client->name = strdup((char *) probe->hostname); 495 496 gtk_label_set_text(GTK_LABEL(ui.probe.hostname), (char *) probe->hostname); 497 gtk_label_set_text(GTK_LABEL(ui.probe.os), os); 498 gtk_label_set_text(GTK_LABEL(ui.probe.arch), arch); 499 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch); 500 gtk_label_set_text(GTK_LABEL(ui.probe.fio_ver), buf); 501} 502 503static void gfio_update_thread_status(char *status_message, double perc) 504{ 505 static char message[100]; 506 const char *m = message; 507 508 strncpy(message, status_message, sizeof(message) - 1); 509 gtk_progress_bar_set_text( 510 GTK_PROGRESS_BAR(ui.thread_status_pb), m); 511 gtk_progress_bar_set_fraction( 512 GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0); 513 gdk_threads_enter(); 514 gtk_widget_queue_draw(ui.window); 515 gdk_threads_leave(); 516} 517 518static void gfio_quit_op(struct fio_client *client) 519{ 520 struct gui *ui = client->client_data; 521 522 gfio_set_connected(ui, 0); 523} 524 525static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd) 526{ 527 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload; 528 struct gui *ui = client->client_data; 529 char tmp[8]; 530 int i; 531 532 p->iodepth = le32_to_cpu(p->iodepth); 533 p->rw = le32_to_cpu(p->rw); 534 535 for (i = 0; i < 2; i++) { 536 p->min_bs[i] = le32_to_cpu(p->min_bs[i]); 537 p->max_bs[i] = le32_to_cpu(p->max_bs[i]); 538 } 539 540 p->numjobs = le32_to_cpu(p->numjobs); 541 p->group_reporting = le32_to_cpu(p->group_reporting); 542 543 gtk_label_set_text(GTK_LABEL(ui->eta.name), (gchar *) p->jobname); 544 gtk_label_set_text(GTK_LABEL(ui->eta.iotype), ddir_str(p->rw)); 545 gtk_label_set_text(GTK_LABEL(ui->eta.ioengine), (gchar *) p->ioengine); 546 547 sprintf(tmp, "%u", p->iodepth); 548 gtk_label_set_text(GTK_LABEL(ui->eta.iodepth), tmp); 549} 550 551static void gfio_client_timed_out(struct fio_client *client) 552{ 553 struct gui *ui = client->client_data; 554 GtkWidget *dialog, *label, *content; 555 char buf[256]; 556 557 gdk_threads_enter(); 558 559 gfio_set_connected(ui, 0); 560 clear_ui_info(ui); 561 562 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname); 563 564 dialog = gtk_dialog_new_with_buttons("Timed out!", 565 GTK_WINDOW(ui->window), 566 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, 567 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); 568 569 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); 570 label = gtk_label_new((const gchar *) buf); 571 gtk_container_add(GTK_CONTAINER(content), label); 572 gtk_widget_show_all(dialog); 573 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT); 574 575 gtk_dialog_run(GTK_DIALOG(dialog)); 576 gtk_widget_destroy(dialog); 577 578 gdk_threads_leave(); 579} 580 581struct client_ops gfio_client_ops = { 582 .text_op = gfio_text_op, 583 .disk_util = gfio_disk_util_op, 584 .thread_status = gfio_thread_status_op, 585 .group_stats = gfio_group_stats_op, 586 .eta = gfio_update_eta, 587 .probe = gfio_probe_op, 588 .quit = gfio_quit_op, 589 .add_job = gfio_add_job_op, 590 .timed_out = gfio_client_timed_out, 591 .stay_connected = 1, 592}; 593 594static void quit_clicked(__attribute__((unused)) GtkWidget *widget, 595 __attribute__((unused)) gpointer data) 596{ 597 gtk_main_quit(); 598} 599 600static void *job_thread(void *arg) 601{ 602 fio_handle_clients(&gfio_client_ops); 603 return NULL; 604} 605 606static int send_job_files(struct gui *ui) 607{ 608 int i, ret = 0; 609 610 for (i = 0; i < ui->nr_job_files; i++) { 611 ret = fio_clients_send_ini(ui->job_files[i]); 612 if (ret) 613 break; 614 615 free(ui->job_files[i]); 616 ui->job_files[i] = NULL; 617 } 618 while (i < ui->nr_job_files) { 619 free(ui->job_files[i]); 620 ui->job_files[i] = NULL; 621 i++; 622 } 623 624 return ret; 625} 626 627static void start_job_thread(struct gui *ui) 628{ 629 if (send_job_files(ui)) { 630 printf("Yeah, I didn't really like those options too much.\n"); 631 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1); 632 return; 633 } 634} 635 636static void start_job_clicked(__attribute__((unused)) GtkWidget *widget, 637 gpointer data) 638{ 639 struct gui *ui = data; 640 641 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0); 642 start_job_thread(ui); 643} 644 645static void file_open(GtkWidget *w, gpointer data); 646 647static void connect_clicked(GtkWidget *widget, gpointer data) 648{ 649 struct gui *ui = data; 650 651 if (!ui->connected) { 652 if (!ui->nr_job_files) 653 file_open(widget, data); 654 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running"); 655 fio_clients_connect(); 656 pthread_create(&ui->t, NULL, job_thread, NULL); 657 gfio_set_connected(ui, 1); 658 } else { 659 fio_clients_terminate(); 660 gfio_set_connected(ui, 0); 661 clear_ui_info(ui); 662 } 663} 664 665static void add_button(struct gui *ui, int i, GtkWidget *buttonbox, 666 struct button_spec *buttonspec) 667{ 668 ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext); 669 g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui); 670 gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3); 671 gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext); 672 gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive); 673} 674 675static void add_buttons(struct gui *ui, 676 struct button_spec *buttonlist, 677 int nbuttons) 678{ 679 int i; 680 681 for (i = 0; i < nbuttons; i++) 682 add_button(ui, i, ui->buttonbox, &buttonlist[i]); 683} 684 685static void on_info_bar_response(GtkWidget *widget, gint response, 686 gpointer data) 687{ 688 if (response == GTK_RESPONSE_OK) { 689 gtk_widget_destroy(widget); 690 ui.error_info_bar = NULL; 691 } 692} 693 694void report_error(GError *error) 695{ 696 if (ui.error_info_bar == NULL) { 697 ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK, 698 GTK_RESPONSE_OK, 699 NULL); 700 g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL); 701 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar), 702 GTK_MESSAGE_ERROR); 703 704 ui.error_label = gtk_label_new(error->message); 705 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar)); 706 gtk_container_add(GTK_CONTAINER(container), ui.error_label); 707 708 gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0); 709 gtk_widget_show_all(ui.vbox); 710 } else { 711 char buffer[256]; 712 snprintf(buffer, sizeof(buffer), "Failed to open file."); 713 gtk_label_set(GTK_LABEL(ui.error_label), buffer); 714 } 715} 716 717static int get_connection_details(char **host, int *port, int *type, 718 int *server_start) 719{ 720 GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo; 721 GtkWidget *button; 722 char *typeentry; 723 724 dialog = gtk_dialog_new_with_buttons("Connection details", 725 GTK_WINDOW(ui.window), 726 GTK_DIALOG_DESTROY_WITH_PARENT, 727 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, 728 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL); 729 730 frame = gtk_frame_new("Hostname / socket name"); 731 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); 732 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); 733 734 box = gtk_vbox_new(FALSE, 6); 735 gtk_container_add(GTK_CONTAINER(frame), box); 736 737 hbox = gtk_hbox_new(TRUE, 10); 738 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); 739 hentry = gtk_entry_new(); 740 gtk_entry_set_text(GTK_ENTRY(hentry), "localhost"); 741 gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 0); 742 743 frame = gtk_frame_new("Port"); 744 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); 745 box = gtk_vbox_new(FALSE, 10); 746 gtk_container_add(GTK_CONTAINER(frame), box); 747 748 hbox = gtk_hbox_new(TRUE, 4); 749 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); 750 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT); 751 752 frame = gtk_frame_new("Type"); 753 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); 754 box = gtk_vbox_new(FALSE, 10); 755 gtk_container_add(GTK_CONTAINER(frame), box); 756 757 hbox = gtk_hbox_new(TRUE, 4); 758 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); 759 760 combo = gtk_combo_box_text_new(); 761 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv4"); 762 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv6"); 763 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "local socket"); 764 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0); 765 766 gtk_container_add(GTK_CONTAINER(hbox), combo); 767 768 frame = gtk_frame_new("Options"); 769 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); 770 box = gtk_vbox_new(FALSE, 10); 771 gtk_container_add(GTK_CONTAINER(frame), box); 772 773 hbox = gtk_hbox_new(TRUE, 4); 774 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); 775 776 button = gtk_check_button_new_with_label("Auto-spawn fio backend"); 777 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1); 778 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."); 779 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6); 780 781 gtk_widget_show_all(dialog); 782 783 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) { 784 gtk_widget_destroy(dialog); 785 return 1; 786 } 787 788 *host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry))); 789 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry)); 790 791 typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo)); 792 if (!typeentry || !strncmp(typeentry, "IPv4", 4)) 793 *type = Fio_client_ipv4; 794 else if (!strncmp(typeentry, "IPv6", 4)) 795 *type = Fio_client_ipv6; 796 else 797 *type = Fio_client_socket; 798 g_free(typeentry); 799 800 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)); 801 802 gtk_widget_destroy(dialog); 803 return 0; 804} 805 806static void file_open(GtkWidget *w, gpointer data) 807{ 808 GtkWidget *dialog; 809 GSList *filenames, *fn_glist; 810 GtkFileFilter *filter; 811 char *host; 812 int port, type, server_start; 813 814 dialog = gtk_file_chooser_dialog_new("Open File", 815 GTK_WINDOW(ui.window), 816 GTK_FILE_CHOOSER_ACTION_OPEN, 817 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, 818 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, 819 NULL); 820 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE); 821 822 filter = gtk_file_filter_new(); 823 gtk_file_filter_add_pattern(filter, "*.fio"); 824 gtk_file_filter_add_pattern(filter, "*.job"); 825 gtk_file_filter_add_mime_type(filter, "text/fio"); 826 gtk_file_filter_set_name(filter, "Fio job file"); 827 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter); 828 829 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) { 830 gtk_widget_destroy(dialog); 831 return; 832 } 833 834 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog)); 835 836 gtk_widget_destroy(dialog); 837 838 if (get_connection_details(&host, &port, &type, &server_start)) 839 goto err; 840 841 filenames = fn_glist; 842 while (filenames != NULL) { 843 ui.job_files = realloc(ui.job_files, (ui.nr_job_files + 1) * sizeof(char *)); 844 ui.job_files[ui.nr_job_files] = strdup(filenames->data); 845 ui.nr_job_files++; 846 847 ui.client = fio_client_add_explicit(&gfio_client_ops, host, type, port); 848 if (!ui.client) { 849 GError *error; 850 851 error = g_error_new(g_quark_from_string("fio"), 1, 852 "Failed to add client %s", host); 853 report_error(error); 854 g_error_free(error); 855 } 856 ui.client->client_data = &ui; 857 858 g_free(filenames->data); 859 filenames = g_slist_next(filenames); 860 } 861 free(host); 862err: 863 g_slist_free(fn_glist); 864} 865 866static void file_save(GtkWidget *w, gpointer data) 867{ 868 GtkWidget *dialog; 869 870 dialog = gtk_file_chooser_dialog_new("Save File", 871 GTK_WINDOW(ui.window), 872 GTK_FILE_CHOOSER_ACTION_SAVE, 873 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, 874 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, 875 NULL); 876 877 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE); 878 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document"); 879 880 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { 881 char *filename; 882 883 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); 884 // save_job_file(filename); 885 g_free(filename); 886 } 887 gtk_widget_destroy(dialog); 888} 889 890static void preferences(GtkWidget *w, gpointer data) 891{ 892 GtkWidget *dialog, *frame, *box, **buttons; 893 int i; 894 895 dialog = gtk_dialog_new_with_buttons("Preferences", 896 GTK_WINDOW(ui.window), 897 GTK_DIALOG_DESTROY_WITH_PARENT, 898 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, 899 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, 900 NULL); 901 902 frame = gtk_frame_new("Debug logging"); 903 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5); 904 box = gtk_hbox_new(FALSE, 6); 905 gtk_container_add(GTK_CONTAINER(frame), box); 906 907 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX); 908 909 for (i = 0; i < FD_DEBUG_MAX; i++) { 910 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name); 911 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help); 912 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6); 913 } 914 915 gtk_widget_show_all(dialog); 916 917 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) { 918 gtk_widget_destroy(dialog); 919 return; 920 } 921 922 for (i = 0; i < FD_DEBUG_MAX; i++) { 923 int set; 924 925 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i])); 926 if (set) 927 fio_debug |= (1UL << i); 928 } 929 930 gtk_widget_destroy(dialog); 931} 932 933static void about_dialog(GtkWidget *w, gpointer data) 934{ 935 gtk_show_about_dialog(NULL, 936 "program-name", "gfio", 937 "comments", "Gtk2 UI for fio", 938 "license", "GPLv2", 939 "version", fio_version_string, 940 "copyright", "Jens Axboe <axboe@kernel.dk> 2012", 941 "logo-icon-name", "fio", 942 /* Must be last: */ 943 NULL, NULL, 944 NULL); 945} 946 947static GtkActionEntry menu_items[] = { 948 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL}, 949 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL}, 950 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) }, 951 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) }, 952 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) }, 953 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) }, 954 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) }, 955}; 956static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]); 957 958static const gchar *ui_string = " \ 959 <ui> \ 960 <menubar name=\"MainMenu\"> \ 961 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \ 962 <menuitem name=\"Open\" action=\"OpenFile\" /> \ 963 <menuitem name=\"Save\" action=\"SaveFile\" /> \ 964 <separator name=\"Separator\"/> \ 965 <menuitem name=\"Preferences\" action=\"Preferences\" /> \ 966 <separator name=\"Separator2\"/> \ 967 <menuitem name=\"Quit\" action=\"Quit\" /> \ 968 </menu> \ 969 <menu name=\"Help\" action=\"HelpMenuAction\"> \ 970 <menuitem name=\"About\" action=\"About\" /> \ 971 </menu> \ 972 </menubar> \ 973 </ui> \ 974"; 975 976static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager) 977{ 978 GtkActionGroup *action_group = gtk_action_group_new("Menu"); 979 GError *error = 0; 980 981 action_group = gtk_action_group_new("Menu"); 982 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0); 983 984 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0); 985 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error); 986 987 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager)); 988 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu"); 989} 990 991void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar, 992 GtkWidget *vbox, GtkUIManager *ui_manager) 993{ 994 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0); 995} 996 997static void init_ui(int *argc, char **argv[], struct gui *ui) 998{ 999 GtkSettings *settings; 1000 GtkUIManager *uimanager; 1001 GtkWidget *menu, *probe, *probe_frame, *probe_box; 1002 1003 memset(ui, 0, sizeof(*ui)); 1004 1005 /* Magical g*thread incantation, you just need this thread stuff. 1006 * Without it, the update that happens in gfio_update_thread_status 1007 * doesn't really happen in a timely fashion, you need expose events 1008 */ 1009 if (!g_thread_supported()) 1010 g_thread_init(NULL); 1011 gdk_threads_init(); 1012 1013 gtk_init(argc, argv); 1014 settings = gtk_settings_get_default(); 1015 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting"); 1016 g_type_init(); 1017 1018 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 1019 gtk_window_set_title(GTK_WINDOW(ui->window), "fio"); 1020 gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500); 1021 1022 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL); 1023 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL); 1024 1025 ui->vbox = gtk_vbox_new(FALSE, 0); 1026 gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox); 1027 1028 uimanager = gtk_ui_manager_new(); 1029 menu = get_menubar_menu(ui->window, uimanager); 1030 gfio_ui_setup(settings, menu, ui->vbox, uimanager); 1031 1032 /* 1033 * Set up alignments for widgets at the top of ui, 1034 * align top left, expand horizontally but not vertically 1035 */ 1036 ui->topalign = gtk_alignment_new(0, 0, 1, 0); 1037 ui->topvbox = gtk_vbox_new(FALSE, 3); 1038 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox); 1039 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0); 1040 1041 probe = gtk_frame_new("Job"); 1042 gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3); 1043 probe_frame = gtk_vbox_new(FALSE, 3); 1044 gtk_container_add(GTK_CONTAINER(probe), probe_frame); 1045 1046 probe_box = gtk_hbox_new(FALSE, 3); 1047 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3); 1048 ui->probe.hostname = new_info_label_in_frame(probe_box, "Host"); 1049 ui->probe.os = new_info_label_in_frame(probe_box, "OS"); 1050 ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture"); 1051 ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version"); 1052 1053 probe_box = gtk_hbox_new(FALSE, 3); 1054 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3); 1055 1056 ui->eta.name = new_info_label_in_frame(probe_box, "Name"); 1057 ui->eta.iotype = new_info_label_in_frame(probe_box, "IO"); 1058 ui->eta.ioengine = new_info_label_in_frame(probe_box, "IO Engine"); 1059 ui->eta.iodepth = new_info_label_in_frame(probe_box, "IO Depth"); 1060 ui->eta.jobs = new_info_label_in_frame(probe_box, "Jobs"); 1061 ui->eta.files = new_info_label_in_frame(probe_box, "Open files"); 1062 1063 probe_box = gtk_hbox_new(FALSE, 3); 1064 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3); 1065 ui->eta.read_bw = new_info_label_in_frame(probe_box, "Read BW"); 1066 ui->eta.read_iops = new_info_label_in_frame(probe_box, "IOPS"); 1067 ui->eta.write_bw = new_info_label_in_frame(probe_box, "Write BW"); 1068 ui->eta.write_iops = new_info_label_in_frame(probe_box, "IOPS"); 1069 1070 /* 1071 * Only add this if we have a commit rate 1072 */ 1073#if 0 1074 probe_box = gtk_hbox_new(FALSE, 3); 1075 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3); 1076 1077 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW"); 1078 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS"); 1079 1080 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW"); 1081 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS"); 1082#endif 1083 1084 /* 1085 * Add a text box for text op messages 1086 */ 1087 ui->textview = gtk_text_view_new(); 1088 ui->text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->textview)); 1089 gtk_text_buffer_set_text(ui->text, "", -1); 1090 gtk_text_view_set_editable(GTK_TEXT_VIEW(ui->textview), FALSE); 1091 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ui->textview), FALSE); 1092 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL); 1093 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window), 1094 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); 1095 gtk_container_add(GTK_CONTAINER(ui->scrolled_window), ui->textview); 1096 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window, 1097 TRUE, TRUE, 0); 1098 1099 /* 1100 * Set up alignments for widgets at the bottom of ui, 1101 * align bottom left, expand horizontally but not vertically 1102 */ 1103 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0); 1104 ui->buttonbox = gtk_hbox_new(FALSE, 0); 1105 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox); 1106 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign, 1107 FALSE, FALSE, 0); 1108 1109 add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist)); 1110 1111 /* 1112 * Set up thread status progress bar 1113 */ 1114 ui->thread_status_pb = gtk_progress_bar_new(); 1115 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0); 1116 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections"); 1117 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb); 1118 1119 1120 gtk_widget_show_all(ui->window); 1121} 1122 1123int main(int argc, char *argv[], char *envp[]) 1124{ 1125 if (initialize_fio(envp)) 1126 return 1; 1127 if (fio_init_options()) 1128 return 1; 1129 1130 init_ui(&argc, &argv, &ui); 1131 1132 gdk_threads_enter(); 1133 gtk_main(); 1134 gdk_threads_leave(); 1135 return 0; 1136} 1137