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