gfio.c revision e61ca217bc489cdacebc5302583128c4d73dd4e7
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 31#define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0]))) 32 33typedef void (*clickfunction)(GtkWidget *widget, gpointer data); 34 35static void quit_clicked(GtkWidget *widget, gpointer data); 36static void start_job_clicked(GtkWidget *widget, gpointer data); 37 38static struct button_spec { 39 const char *buttontext; 40 clickfunction f; 41 const char *tooltiptext; 42} buttonspeclist[] = { 43#define START_JOB_BUTTON 0 44 { "Start Job", 45 start_job_clicked, 46 "Send current fio job to fio server to be executed" }, 47#define QUIT_BUTTON 1 48 { "Quit", quit_clicked, "Quit gfio" }, 49}; 50 51struct gui { 52 int argc; 53 char **argv; 54 GtkWidget *window; 55 GtkWidget *vbox; 56 GtkWidget *topvbox; 57 GtkWidget *topalign; 58 GtkWidget *bottomalign; 59 GtkWidget *thread_status_pb; 60 GtkWidget *buttonbox; 61 GtkWidget *button[ARRAYSIZE(buttonspeclist)]; 62 GtkWidget *hostname_hbox; 63 GtkWidget *hostname_label; 64 GtkWidget *hostname_entry; 65 GtkWidget *port_label; 66 GtkWidget *port_entry; 67 GtkWidget *hostname_combo_box; /* ipv4, ipv6 or socket */ 68 GtkWidget *jobfile_hbox; 69 GtkWidget *jobfile_label; 70 GtkWidget *jobfile_entry; 71 GtkWidget *scrolled_window; 72 GtkWidget *textview; 73 GtkTextBuffer *text; 74 pthread_t t; 75} ui; 76 77static void gfio_text_op(struct fio_client *client, 78 FILE *f, __u16 pdu_len, const char *buf) 79{ 80 GtkTextBuffer *buffer; 81 GtkTextIter end; 82 83 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui.textview)); 84 gdk_threads_enter(); 85 gtk_text_buffer_get_end_iter(buffer, &end); 86 gtk_text_buffer_insert(buffer, &end, buf, -1); 87 gdk_threads_leave(); 88 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(ui.textview), 89 &end, 0.0, FALSE, 0.0,0.0); 90} 91 92static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd) 93{ 94 printf("gfio_disk_util_op called\n"); 95 fio_client_ops.disk_util(client, cmd); 96} 97 98static void gfio_thread_status_op(struct fio_net_cmd *cmd) 99{ 100 printf("gfio_thread_status_op called\n"); 101 fio_client_ops.thread_status(cmd); 102} 103 104static void gfio_group_stats_op(struct fio_net_cmd *cmd) 105{ 106 printf("gfio_group_stats_op called\n"); 107 fio_client_ops.group_stats(cmd); 108} 109 110static void gfio_eta_op(struct fio_client *client, struct fio_net_cmd *cmd) 111{ 112 fio_client_ops.eta(client, cmd); 113} 114 115static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd) 116{ 117 printf("gfio_probe_op called\n"); 118 fio_client_ops.probe(client, cmd); 119} 120 121static void gfio_update_thread_status(char *status_message, double perc) 122{ 123 static char message[100]; 124 const char *m = message; 125 126 strncpy(message, status_message, sizeof(message) - 1); 127 gtk_progress_bar_set_text( 128 GTK_PROGRESS_BAR(ui.thread_status_pb), m); 129 gtk_progress_bar_set_fraction( 130 GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0); 131 gdk_threads_enter(); 132 gtk_widget_queue_draw(ui.window); 133 gdk_threads_leave(); 134} 135 136struct client_ops gfio_client_ops = { 137 gfio_text_op, 138 gfio_disk_util_op, 139 gfio_thread_status_op, 140 gfio_group_stats_op, 141 gfio_eta_op, 142 gfio_probe_op, 143 gfio_update_thread_status, 144}; 145 146static void quit_clicked(__attribute__((unused)) GtkWidget *widget, 147 __attribute__((unused)) gpointer data) 148{ 149 gtk_main_quit(); 150} 151 152static void add_arg(char **argv, int index, const char *value) 153{ 154 argv[index] = malloc(strlen(value) + 1); 155 strcpy(argv[index], value); 156} 157 158static void free_args(int argc, char **argv) 159{ 160 int i; 161 162 for (i = 0; i < argc; i++) 163 free(argv[i]); 164 free(argv); 165} 166 167static void *job_thread(void *arg) 168{ 169 struct gui *ui = arg; 170 171 fio_handle_clients(&gfio_client_ops); 172 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1); 173 free_args(ui->argc, ui->argv); 174 return NULL; 175} 176 177static void construct_options(struct gui *ui, int *argc, char ***argv) 178{ 179 const char *hostname, *hostname_type, *port, *jobfile; 180 char newarg[200]; 181 182 hostname_type = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(ui->hostname_combo_box)->entry)); 183 hostname = gtk_entry_get_text(GTK_ENTRY(ui->hostname_entry)); 184 port = gtk_entry_get_text(GTK_ENTRY(ui->port_entry)); 185 jobfile = gtk_entry_get_text(GTK_ENTRY(ui->jobfile_entry)); 186 187 *argc = 3; 188 *argv = malloc(*argc * sizeof(**argv)); 189 add_arg(*argv, 0, "gfio"); 190 snprintf(newarg, sizeof(newarg) - 1, "--client=%s", hostname); 191 add_arg(*argv, 1, newarg); 192 add_arg(*argv, 2, jobfile); 193} 194 195static void start_job_thread(pthread_t *t, struct gui *ui) 196{ 197 construct_options(ui, &ui->argc, &ui->argv); 198 if (parse_options(ui->argc, ui->argv)) { 199 printf("Yeah, I didn't really like those options too much.\n"); 200 free_args(ui->argc, ui->argv); 201 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1); 202 return; 203 } 204 pthread_create(t, NULL, job_thread, ui); 205} 206 207static void start_job_clicked(__attribute__((unused)) GtkWidget *widget, 208 gpointer data) 209{ 210 struct gui *ui = data; 211 212 printf("Start job button was clicked.\n"); 213 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0); 214 start_job_thread(&ui->t, ui); 215} 216 217static void add_button(struct gui *ui, int i, GtkWidget *buttonbox, 218 struct button_spec *buttonspec) 219{ 220 ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext); 221 g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui); 222 gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], TRUE, TRUE, 0); 223 gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext); 224} 225 226static void add_buttons(struct gui *ui, 227 struct button_spec *buttonlist, 228 int nbuttons) 229{ 230 int i; 231 232 for (i = 0; i < nbuttons; i++) 233 add_button(ui, i, ui->buttonbox, &buttonlist[i]); 234} 235 236static void init_ui(int *argc, char **argv[], struct gui *ui) 237{ 238 GList *hostname_type_list = NULL; 239 char portnum[20]; 240 241 /* Magical g*thread incantation, you just need this thread stuff. 242 * Without it, the update that happens in gfio_update_thread_status 243 * doesn't really happen in a timely fashion, you need expose events 244 */ 245 if (!g_thread_supported ()) 246 g_thread_init(NULL); 247 gdk_threads_init(); 248 249 gtk_init(argc, argv); 250 251 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 252 gtk_window_set_title(GTK_WINDOW(ui->window), "fio"); 253 gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500); 254 255 g_signal_connect(ui->window, "delete-event", G_CALLBACK (quit_clicked), NULL); 256 g_signal_connect(ui->window, "destroy", G_CALLBACK (quit_clicked), NULL); 257 258 ui->vbox = gtk_vbox_new(FALSE, 0); 259 gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox); 260 261 /* 262 * Set up alignments for widgets at the top of ui, 263 * align top left, expand horizontally but not vertically 264 */ 265 ui->topalign = gtk_alignment_new(0, 0, 1, 0); 266 ui->topvbox = gtk_vbox_new(FALSE, 0); 267 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox); 268 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0); 269 270 /* 271 * Set up hostname label + entry, port label + entry, 272 */ 273 ui->hostname_hbox = gtk_hbox_new(FALSE, 0); 274 ui->hostname_label = gtk_label_new("Host:"); 275 ui->hostname_entry = gtk_entry_new(); 276 gtk_entry_set_text(GTK_ENTRY(ui->hostname_entry), "localhost"); 277 ui->port_label = gtk_label_new("Port:"); 278 ui->port_entry = gtk_entry_new(); 279 snprintf(portnum, sizeof(portnum) - 1, "%d", FIO_NET_PORT); 280 gtk_entry_set_text(GTK_ENTRY(ui->port_entry), (gchar *) portnum); 281 282 /* 283 * Set up combo box for address type 284 */ 285 ui->hostname_combo_box = gtk_combo_new(); 286 gtk_entry_set_text(GTK_ENTRY (GTK_COMBO(ui->hostname_combo_box)->entry), "IPv4"); 287 hostname_type_list = g_list_append(hostname_type_list, (gpointer) "IPv4"); 288 hostname_type_list = g_list_append(hostname_type_list, (gpointer) "local socket"); 289 hostname_type_list = g_list_append(hostname_type_list, (gpointer) "IPv6"); 290 gtk_combo_set_popdown_strings (GTK_COMBO (ui->hostname_combo_box), hostname_type_list); 291 g_list_free(hostname_type_list); 292 293 gtk_container_add(GTK_CONTAINER (ui->hostname_hbox), ui->hostname_label); 294 gtk_container_add(GTK_CONTAINER (ui->hostname_hbox), ui->hostname_entry); 295 gtk_container_add(GTK_CONTAINER (ui->hostname_hbox), ui->port_label); 296 gtk_container_add(GTK_CONTAINER (ui->hostname_hbox), ui->port_entry); 297 gtk_container_add(GTK_CONTAINER (ui->hostname_hbox), ui->hostname_combo_box); 298 gtk_container_add(GTK_CONTAINER (ui->topvbox), ui->hostname_hbox); 299 300 /* 301 * Set up jobfile text entry (temporary until gui really works) 302 */ 303 ui->jobfile_hbox = gtk_hbox_new(FALSE, 0); 304 ui->jobfile_label = gtk_label_new("Job file:"); 305 ui->jobfile_entry = gtk_entry_new(); 306 gtk_container_add(GTK_CONTAINER (ui->jobfile_hbox), ui->jobfile_label); 307 gtk_container_add(GTK_CONTAINER (ui->jobfile_hbox), ui->jobfile_entry); 308 gtk_container_add(GTK_CONTAINER (ui->topvbox), ui->jobfile_hbox); 309 310 /* 311 * Set up thread status progress bar 312 */ 313 ui->thread_status_pb = gtk_progress_bar_new(); 314 gtk_progress_bar_set_fraction( 315 GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0); 316 gtk_progress_bar_set_text( 317 GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running"); 318 gtk_container_add(GTK_CONTAINER (ui->topvbox), ui->thread_status_pb); 319 320 /* 321 * Add a text box for text op messages 322 */ 323 ui->textview = gtk_text_view_new(); 324 ui->text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->textview)); 325 gtk_text_buffer_set_text(ui->text, "", -1); 326 gtk_text_view_set_editable(GTK_TEXT_VIEW(ui->textview), FALSE); 327 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ui->textview), FALSE); 328 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL); 329 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window), 330 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); 331 gtk_container_add(GTK_CONTAINER(ui->scrolled_window), ui->textview); 332 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window, 333 TRUE, TRUE, 0); 334 335 /* 336 * Set up alignments for widgets at the bottom of ui, 337 * align bottom left, expand horizontally but not vertically 338 */ 339 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0); 340 ui->buttonbox = gtk_hbox_new(FALSE, 0); 341 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox); 342 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign, 343 FALSE, FALSE, 0); 344 345 add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist)); 346 gtk_widget_show_all(ui->window); 347} 348 349int main(int argc, char *argv[], char *envp[]) 350{ 351 if (initialize_fio(envp)) 352 return 1; 353 354 init_ui(&argc, &argv, &ui); 355 356 gdk_threads_enter(); 357 gtk_main(); 358 gdk_threads_leave(); 359 return 0; 360} 361