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