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