gfio.c revision f9d40b4814d88ca1ca13a05cf45b6f36cfb2ba55
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	GtkWidget *results_notebook;
92	GtkWidget *results_window;
93	GtkTextBuffer *text;
94	struct probe_widget probe;
95	struct eta_widget eta;
96	int connected;
97	pthread_t t;
98
99	struct fio_client *client;
100	int nr_job_files;
101	char **job_files;
102} ui;
103
104static void clear_ui_info(struct gui *ui)
105{
106	gtk_label_set_text(GTK_LABEL(ui->probe.hostname), "");
107	gtk_label_set_text(GTK_LABEL(ui->probe.os), "");
108	gtk_label_set_text(GTK_LABEL(ui->probe.arch), "");
109	gtk_label_set_text(GTK_LABEL(ui->probe.fio_ver), "");
110	gtk_entry_set_text(GTK_ENTRY(ui->eta.name), "");
111	gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), "");
112	gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), "");
113	gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), "");
114	gtk_entry_set_text(GTK_ENTRY(ui->eta.jobs), "");
115	gtk_entry_set_text(GTK_ENTRY(ui->eta.files), "");
116	gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), "");
117	gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), "");
118	gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), "");
119	gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), "");
120}
121
122static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
123{
124	GtkWidget *entry, *frame;
125
126	frame = gtk_frame_new(label);
127	entry = gtk_entry_new();
128	gtk_entry_set_editable(GTK_ENTRY(entry), 0);
129	gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
130	gtk_container_add(GTK_CONTAINER(frame), entry);
131
132	return entry;
133}
134
135static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
136{
137	GtkWidget *label_widget;
138	GtkWidget *frame;
139
140	frame = gtk_frame_new(label);
141	label_widget = gtk_label_new(NULL);
142	gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
143	gtk_container_add(GTK_CONTAINER(frame), label_widget);
144
145	return label_widget;
146}
147
148static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
149{
150	GtkWidget *button, *box;
151
152	box = gtk_hbox_new(FALSE, 3);
153	gtk_container_add(GTK_CONTAINER(hbox), box);
154
155	button = gtk_spin_button_new_with_range(min, max, 1.0);
156	gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
157
158	gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
159	gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
160
161	return button;
162}
163
164static void gfio_set_connected(struct gui *ui, int connected)
165{
166	if (connected) {
167		gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
168		ui->connected = 1;
169		gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Disconnect");
170	} else {
171		ui->connected = 0;
172		gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Connect");
173		gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
174	}
175}
176
177static void label_set_int_value(GtkWidget *entry, unsigned int val)
178{
179	char tmp[80];
180
181	sprintf(tmp, "%u", val);
182	gtk_label_set_text(GTK_LABEL(entry), tmp);
183}
184
185static void entry_set_int_value(GtkWidget *entry, unsigned int val)
186{
187	char tmp[80];
188
189	sprintf(tmp, "%u", val);
190	gtk_entry_set_text(GTK_ENTRY(entry), tmp);
191}
192
193#define ALIGN_LEFT 1
194#define ALIGN_RIGHT 2
195#define INVISIBLE 4
196#define UNSORTABLE 8
197
198GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
199{
200	GtkCellRenderer *renderer;
201	GtkTreeViewColumn *col;
202	double xalign = 0.0; /* left as default */
203	PangoAlignment align;
204	gboolean visible;
205
206	align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
207		(flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
208		PANGO_ALIGN_CENTER;
209	visible = !(flags & INVISIBLE);
210
211	renderer = gtk_cell_renderer_text_new();
212	col = gtk_tree_view_column_new();
213
214	gtk_tree_view_column_set_title(col, title);
215	if (!(flags & UNSORTABLE))
216		gtk_tree_view_column_set_sort_column_id(col, index);
217	gtk_tree_view_column_set_resizable(col, TRUE);
218	gtk_tree_view_column_pack_start(col, renderer, TRUE);
219	gtk_tree_view_column_add_attribute(col, renderer, "text", index);
220	gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
221	switch (align) {
222	case PANGO_ALIGN_LEFT:
223		xalign = 0.0;
224		break;
225	case PANGO_ALIGN_CENTER:
226		xalign = 0.5;
227		break;
228	case PANGO_ALIGN_RIGHT:
229		xalign = 1.0;
230		break;
231	}
232	gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
233	gtk_tree_view_column_set_visible(col, visible);
234	gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
235	return col;
236}
237
238static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
239					       fio_fp64_t *plist,
240					       unsigned int len,
241					       const char *base,
242					       unsigned int scale)
243{
244	GType types[FIO_IO_U_LIST_MAX_LEN];
245	GtkWidget *tree_view;
246	GtkTreeSelection *selection;
247	GtkListStore *model;
248	GtkTreeIter iter;
249	int i;
250
251	for (i = 0; i < len; i++)
252		types[i] = G_TYPE_INT;
253
254	model = gtk_list_store_newv(len, types);
255
256	tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
257	gtk_widget_set_can_focus(tree_view, FALSE);
258
259	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
260	gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
261
262	for (i = 0; i < len; i++) {
263		char fbuf[8];
264
265		sprintf(fbuf, "%2.2f%%", plist[i].u.f);
266		tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
267	}
268
269	gtk_list_store_append(model, &iter);
270
271	for (i = 0; i < len; i++)
272		gtk_list_store_set(model, &iter, i, ovals[i], -1);
273
274	return tree_view;
275}
276
277static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
278				       int ddir)
279{
280	unsigned int *io_u_plat = ts->io_u_plat[ddir];
281	unsigned long nr = ts->clat_stat[ddir].samples;
282	fio_fp64_t *plist = ts->percentile_list;
283	unsigned int *ovals, len, minv, maxv, scale_down;
284	const char *base;
285	GtkWidget *tree_view, *frame, *hbox;
286	char tmp[64];
287
288	len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
289	if (!len)
290		goto out;
291
292	/*
293	 * We default to usecs, but if the value range is such that we
294	 * should scale down to msecs, do that.
295	 */
296	if (minv > 2000 && maxv > 99999) {
297		scale_down = 1;
298		base = "msec";
299	} else {
300		scale_down = 0;
301		base = "usec";
302	}
303
304	tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
305
306	sprintf(tmp, "Completion percentiles (%s)", base);
307	frame = gtk_frame_new(tmp);
308	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
309
310	hbox = gtk_hbox_new(FALSE, 3);
311	gtk_container_add(GTK_CONTAINER(frame), hbox);
312
313	gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
314out:
315	if (ovals)
316		free(ovals);
317}
318
319static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
320			  unsigned long max, double mean, double dev)
321{
322	const char *base = "(usec)";
323	GtkWidget *hbox, *label, *frame;
324	char *minp, *maxp;
325	char tmp[64];
326
327	if (!usec_to_msec(&min, &max, &mean, &dev))
328		base = "(msec)";
329
330	minp = num2str(min, 6, 1, 0);
331	maxp = num2str(max, 6, 1, 0);
332
333	sprintf(tmp, "%s %s", name, base);
334	frame = gtk_frame_new(tmp);
335	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
336
337	hbox = gtk_hbox_new(FALSE, 3);
338	gtk_container_add(GTK_CONTAINER(frame), hbox);
339
340	label = new_info_label_in_frame(hbox, "Minimum");
341	gtk_label_set_text(GTK_LABEL(label), minp);
342	label = new_info_label_in_frame(hbox, "Maximum");
343	gtk_label_set_text(GTK_LABEL(label), maxp);
344	label = new_info_label_in_frame(hbox, "Average");
345	sprintf(tmp, "%5.02f", mean);
346	gtk_label_set_text(GTK_LABEL(label), tmp);
347	label = new_info_label_in_frame(hbox, "Standard deviation");
348	sprintf(tmp, "%5.02f", dev);
349	gtk_label_set_text(GTK_LABEL(label), tmp);
350
351	free(minp);
352	free(maxp);
353
354}
355
356#define GFIO_CLAT	1
357#define GFIO_SLAT	2
358#define GFIO_LAT	4
359
360static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
361				  struct thread_stat *ts, int ddir)
362{
363	const char *ddir_label[2] = { "Read", "Write" };
364	GtkWidget *frame, *label, *box, *vbox, *main_vbox;
365	unsigned long min, max, runt;
366	unsigned long long bw, iops;
367	unsigned int flags = 0;
368	double mean, dev;
369	char *io_p, *bw_p, *iops_p;
370	int i2p;
371
372	if (!ts->runtime[ddir])
373		return;
374
375	i2p = is_power_of_2(rs->kb_base);
376	runt = ts->runtime[ddir];
377
378	bw = (1000 * ts->io_bytes[ddir]) / runt;
379	io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
380	bw_p = num2str(bw, 6, 1, i2p);
381
382	iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
383	iops_p = num2str(iops, 6, 1, 0);
384
385	box = gtk_hbox_new(FALSE, 3);
386	gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
387
388	frame = gtk_frame_new(ddir_label[ddir]);
389	gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
390
391	main_vbox = gtk_vbox_new(FALSE, 3);
392	gtk_container_add(GTK_CONTAINER(frame), main_vbox);
393
394	box = gtk_hbox_new(FALSE, 3);
395	gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
396
397	label = new_info_label_in_frame(box, "IO");
398	gtk_label_set_text(GTK_LABEL(label), io_p);
399	label = new_info_label_in_frame(box, "Bandwidth");
400	gtk_label_set_text(GTK_LABEL(label), bw_p);
401	label = new_info_label_in_frame(box, "IOPS");
402	gtk_label_set_text(GTK_LABEL(label), iops_p);
403	label = new_info_label_in_frame(box, "Runtime (msec)");
404	label_set_int_value(label, ts->runtime[ddir]);
405
406	if (calc_lat(&ts->bw_stat[ddir], &min, &max, &mean, &dev)) {
407		double p_of_agg = 100.0;
408		const char *bw_str = "KB";
409		char tmp[32];
410
411		if (rs->agg[ddir]) {
412			p_of_agg = mean * 100 / (double) rs->agg[ddir];
413			if (p_of_agg > 100.0)
414				p_of_agg = 100.0;
415		}
416
417		if (mean > 999999.9) {
418			min /= 1000.0;
419			max /= 1000.0;
420			mean /= 1000.0;
421			dev /= 1000.0;
422			bw_str = "MB";
423		}
424
425		sprintf(tmp, "Bandwidth (%s)", bw_str);
426		frame = gtk_frame_new(tmp);
427		gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
428
429		box = gtk_hbox_new(FALSE, 3);
430		gtk_container_add(GTK_CONTAINER(frame), box);
431
432		label = new_info_label_in_frame(box, "Minimum");
433		label_set_int_value(label, min);
434		label = new_info_label_in_frame(box, "Maximum");
435		label_set_int_value(label, max);
436		label = new_info_label_in_frame(box, "Percentage of jobs");
437		sprintf(tmp, "%3.2f%%", p_of_agg);
438		gtk_label_set_text(GTK_LABEL(label), tmp);
439		label = new_info_label_in_frame(box, "Average");
440		sprintf(tmp, "%5.02f", mean);
441		gtk_label_set_text(GTK_LABEL(label), tmp);
442		label = new_info_label_in_frame(box, "Standard deviation");
443		sprintf(tmp, "%5.02f", dev);
444		gtk_label_set_text(GTK_LABEL(label), tmp);
445	}
446
447	if (calc_lat(&ts->slat_stat[ddir], &min, &max, &mean, &dev))
448		flags |= GFIO_SLAT;
449	if (calc_lat(&ts->clat_stat[ddir], &min, &max, &mean, &dev))
450		flags |= GFIO_CLAT;
451	if (calc_lat(&ts->lat_stat[ddir], &min, &max, &mean, &dev))
452		flags |= GFIO_LAT;
453
454	if (flags) {
455		frame = gtk_frame_new("Latency");
456		gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
457
458		vbox = gtk_vbox_new(FALSE, 3);
459		gtk_container_add(GTK_CONTAINER(frame), vbox);
460
461		if (flags & GFIO_SLAT)
462			gfio_show_lat(vbox, "Submission latency", min, max, mean, dev);
463		if (flags & GFIO_CLAT)
464			gfio_show_lat(vbox, "Completion latency", min, max, mean, dev);
465		if (flags & GFIO_LAT)
466			gfio_show_lat(vbox, "Total latency", min, max, mean, dev);
467	}
468
469	if (ts->clat_percentiles)
470		gfio_show_clat_percentiles(main_vbox, ts, ddir);
471
472
473	free(io_p);
474	free(bw_p);
475	free(iops_p);
476}
477
478static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
479					  const char **labels)
480{
481	GtkWidget *tree_view;
482	GtkTreeSelection *selection;
483	GtkListStore *model;
484	GtkTreeIter iter;
485	GType *types;
486	int i, skipped;
487
488	/*
489	 * Check if all are empty, in which case don't bother
490	 */
491	for (i = 0, skipped = 0; i < num; i++)
492		if (lat[i] <= 0.0)
493			skipped++;
494
495	if (skipped == num)
496		return NULL;
497
498	types = malloc(num * sizeof(GType));
499
500	for (i = 0; i < num; i++)
501		types[i] = G_TYPE_STRING;
502
503	model = gtk_list_store_newv(num, types);
504	free(types);
505	types = NULL;
506
507	tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
508	gtk_widget_set_can_focus(tree_view, FALSE);
509
510	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
511	gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
512
513	for (i = 0; i < num; i++)
514		tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
515
516	gtk_list_store_append(model, &iter);
517
518	for (i = 0; i < num; i++) {
519		char fbuf[32];
520
521		if (lat[i] <= 0.0)
522			sprintf(fbuf, "0.00");
523		else
524			sprintf(fbuf, "%3.2f%%", lat[i]);
525
526		gtk_list_store_set(model, &iter, i, fbuf, -1);
527	}
528
529	return tree_view;
530}
531
532static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
533{
534	GtkWidget *box, *frame, *tree_view;
535	double io_u_lat_u[FIO_IO_U_LAT_U_NR];
536	double io_u_lat_m[FIO_IO_U_LAT_M_NR];
537	const char *uranges[] = { "2", "4", "10", "20", "50", "100",
538				  "250", "500", "750", "1000", };
539	const char *mranges[] = { "2", "4", "10", "20", "50", "100",
540				  "250", "500", "750", "1000", "2000",
541				  ">= 2000", };
542
543	stat_calc_lat_u(ts, io_u_lat_u);
544	stat_calc_lat_m(ts, io_u_lat_m);
545
546	tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
547	if (tree_view) {
548		frame = gtk_frame_new("Latency buckets (usec)");
549		gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
550
551		box = gtk_hbox_new(FALSE, 3);
552		gtk_container_add(GTK_CONTAINER(frame), box);
553		gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
554	}
555
556	tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
557	if (tree_view) {
558		frame = gtk_frame_new("Latency buckets (msec)");
559		gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
560
561		box = gtk_hbox_new(FALSE, 3);
562		gtk_container_add(GTK_CONTAINER(frame), box);
563		gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
564	}
565}
566
567static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
568{
569	GtkWidget *box, *frame, *entry;
570	double usr_cpu, sys_cpu;
571	unsigned long runtime;
572	char tmp[32];
573
574	runtime = ts->total_run_time;
575	if (runtime) {
576		double runt = (double) runtime;
577
578		usr_cpu = (double) ts->usr_time * 100 / runt;
579		sys_cpu = (double) ts->sys_time * 100 / runt;
580	} else {
581		usr_cpu = 0;
582		sys_cpu = 0;
583	}
584
585	frame = gtk_frame_new("OS resources");
586	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
587
588	box = gtk_hbox_new(FALSE, 3);
589	gtk_container_add(GTK_CONTAINER(frame), box);
590
591	entry = new_info_entry_in_frame(box, "User CPU");
592	sprintf(tmp, "%3.2f%%", usr_cpu);
593	gtk_entry_set_text(GTK_ENTRY(entry), tmp);
594	entry = new_info_entry_in_frame(box, "System CPU");
595	sprintf(tmp, "%3.2f%%", sys_cpu);
596	gtk_entry_set_text(GTK_ENTRY(entry), tmp);
597	entry = new_info_entry_in_frame(box, "Context switches");
598	entry_set_int_value(entry, ts->ctx);
599	entry = new_info_entry_in_frame(box, "Major faults");
600	entry_set_int_value(entry, ts->majf);
601	entry = new_info_entry_in_frame(box, "Minor faults");
602	entry_set_int_value(entry, ts->minf);
603}
604static void gfio_add_sc_depths_tree(GtkListStore *model,
605				    struct thread_stat *ts, unsigned int len,
606				    int submit)
607{
608	double io_u_dist[FIO_IO_U_MAP_NR];
609	GtkTreeIter iter;
610	/* Bits 0, and 3-8 */
611	const int add_mask = 0x1f9;
612	int i, j;
613
614	if (submit)
615		stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
616	else
617		stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
618
619	gtk_list_store_append(model, &iter);
620
621	gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
622
623	for (i = 1, j = 0; i < len; i++) {
624		char fbuf[32];
625
626		if (!(add_mask & (1UL << (i - 1))))
627			sprintf(fbuf, "0.0%%");
628		else {
629			sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
630			j++;
631		}
632
633		gtk_list_store_set(model, &iter, i, fbuf, -1);
634	}
635
636}
637
638static void gfio_add_total_depths_tree(GtkListStore *model,
639				       struct thread_stat *ts, unsigned int len)
640{
641	double io_u_dist[FIO_IO_U_MAP_NR];
642	GtkTreeIter iter;
643	/* Bits 1-6, and 8 */
644	const int add_mask = 0x17e;
645	int i, j;
646
647	stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
648
649	gtk_list_store_append(model, &iter);
650
651	gtk_list_store_set(model, &iter, 0, "Total", -1);
652
653	for (i = 1, j = 0; i < len; i++) {
654		char fbuf[32];
655
656		if (!(add_mask & (1UL << (i - 1))))
657			sprintf(fbuf, "0.0%%");
658		else {
659			sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
660			j++;
661		}
662
663		gtk_list_store_set(model, &iter, i, fbuf, -1);
664	}
665
666}
667
668static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
669{
670	GtkWidget *frame, *box, *tree_view;
671	GtkTreeSelection *selection;
672	GtkListStore *model;
673	GType types[FIO_IO_U_MAP_NR + 1];
674	int i;
675#define NR_LABELS	10
676	const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
677
678	frame = gtk_frame_new("IO depths");
679	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
680
681	box = gtk_hbox_new(FALSE, 3);
682	gtk_container_add(GTK_CONTAINER(frame), box);
683
684	for (i = 0; i < NR_LABELS; i++)
685		types[i] = G_TYPE_STRING;
686
687	model = gtk_list_store_newv(NR_LABELS, types);
688
689	tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
690	gtk_widget_set_can_focus(tree_view, FALSE);
691
692	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
693	gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
694
695	for (i = 0; i < NR_LABELS; i++)
696		tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
697
698	gfio_add_total_depths_tree(model, ts, NR_LABELS);
699	gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
700	gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
701
702	gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
703}
704
705static gboolean results_window_delete(GtkWidget *w, gpointer data)
706{
707	struct gui *ui = (struct gui *) data;
708
709	gtk_widget_destroy(w);
710	ui->results_window = NULL;
711	ui->results_notebook = NULL;
712	return TRUE;
713}
714
715static GtkWidget *get_results_window(struct gui *ui)
716{
717	GtkWidget *win, *notebook;
718
719	if (ui->results_window)
720		return ui->results_notebook;
721
722	win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
723	gtk_window_set_title(GTK_WINDOW(win), "Results");
724	g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ui);
725	g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ui);
726
727	notebook = gtk_notebook_new();
728	gtk_container_add(GTK_CONTAINER(win), notebook);
729
730	ui->results_window = win;
731	ui->results_notebook = notebook;
732	return ui->results_notebook;
733}
734
735static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
736			    struct group_run_stats *rs)
737{
738	GtkWidget *res_win, *box, *vbox, *entry;
739	struct gui *ui = client->client_data;
740
741	gdk_threads_enter();
742
743	res_win = get_results_window(ui);
744
745	vbox = gtk_vbox_new(FALSE, 3);
746
747	box = gtk_hbox_new(TRUE, 3);
748	gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
749
750	gtk_notebook_append_page(GTK_NOTEBOOK(res_win), vbox, gtk_label_new(ts->name));
751
752	entry = new_info_entry_in_frame(box, "Name");
753	gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
754	if (strlen(ts->description)) {
755		entry = new_info_entry_in_frame(box, "Description");
756		gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
757	}
758	entry = new_info_entry_in_frame(box, "Group ID");
759	entry_set_int_value(entry, ts->groupid);
760	entry = new_info_entry_in_frame(box, "Jobs");
761	entry_set_int_value(entry, ts->members);
762	entry = new_info_entry_in_frame(box, "Error");
763	entry_set_int_value(entry, ts->error);
764	entry = new_info_entry_in_frame(box, "PID");
765	entry_set_int_value(entry, ts->pid);
766
767	if (ts->io_bytes[DDIR_READ])
768		gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
769	if (ts->io_bytes[DDIR_WRITE])
770		gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
771
772	gfio_show_latency_buckets(vbox, ts);
773	gfio_show_cpu_usage(vbox, ts);
774	gfio_show_io_depths(vbox, ts);
775
776	gtk_widget_show_all(ui->results_window);
777	gdk_threads_leave();
778}
779
780static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
781{
782#if 0
783	GtkTextBuffer *buffer;
784	GtkTextIter end;
785
786	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui.textview));
787	gdk_threads_enter();
788	gtk_text_buffer_get_end_iter(buffer, &end);
789	gtk_text_buffer_insert(buffer, &end, buf, -1);
790	gdk_threads_leave();
791	gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(ui.textview),
792					&end, 0.0, FALSE, 0.0,0.0);
793#else
794	fio_client_ops.text_op(client, cmd);
795#endif
796}
797
798static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
799{
800	gdk_threads_enter();
801	printf("gfio_disk_util_op called\n");
802	fio_client_ops.disk_util(client, cmd);
803	gdk_threads_leave();
804}
805
806extern int sum_stat_clients;
807extern struct thread_stat client_ts;
808extern struct group_run_stats client_gs;
809
810static int sum_stat_nr;
811
812static void gfio_thread_status_op(struct fio_client *client,
813				  struct fio_net_cmd *cmd)
814{
815	struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
816
817	gfio_display_ts(client, &p->ts, &p->rs);
818
819	if (sum_stat_clients == 1)
820		return;
821
822	sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
823	sum_group_stats(&client_gs, &p->rs);
824
825	client_ts.members++;
826	client_ts.groupid = p->ts.groupid;
827
828	if (++sum_stat_nr == sum_stat_clients) {
829		strcpy(client_ts.name, "All clients");
830		gfio_display_ts(client, &client_ts, &client_gs);
831	}
832}
833
834static void gfio_group_stats_op(struct fio_client *client,
835				struct fio_net_cmd *cmd)
836{
837	gdk_threads_enter();
838	printf("gfio_group_stats_op called\n");
839	fio_client_ops.group_stats(client, cmd);
840	gdk_threads_leave();
841}
842
843static void gfio_update_eta(struct jobs_eta *je)
844{
845	static int eta_good;
846	char eta_str[128];
847	char output[256];
848	char tmp[32];
849	double perc = 0.0;
850	int i2p = 0;
851
852	gdk_threads_enter();
853
854	eta_str[0] = '\0';
855	output[0] = '\0';
856
857	if (je->eta_sec != INT_MAX && je->elapsed_sec) {
858		perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
859		eta_to_str(eta_str, je->eta_sec);
860	}
861
862	sprintf(tmp, "%u", je->nr_running);
863	gtk_entry_set_text(GTK_ENTRY(ui.eta.jobs), tmp);
864	sprintf(tmp, "%u", je->files_open);
865	gtk_entry_set_text(GTK_ENTRY(ui.eta.files), tmp);
866
867#if 0
868	if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
869	if (je->m_rate || je->t_rate) {
870		char *tr, *mr;
871
872		mr = num2str(je->m_rate, 4, 0, i2p);
873		tr = num2str(je->t_rate, 4, 0, i2p);
874		gtk_entry_set_text(GTK_ENTRY(ui.eta);
875		p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
876		free(tr);
877		free(mr);
878	} else if (je->m_iops || je->t_iops)
879		p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
880
881	gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_bw), "---");
882	gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_iops), "---");
883	gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_bw), "---");
884	gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_iops), "---");
885#endif
886
887	if (je->eta_sec != INT_MAX && je->nr_running) {
888		char *iops_str[2];
889		char *rate_str[2];
890
891		if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
892			strcpy(output, "-.-% done");
893		else {
894			eta_good = 1;
895			perc *= 100.0;
896			sprintf(output, "%3.1f%% done", perc);
897		}
898
899		rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
900		rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
901
902		iops_str[0] = num2str(je->iops[0], 4, 1, 0);
903		iops_str[1] = num2str(je->iops[1], 4, 1, 0);
904
905		gtk_entry_set_text(GTK_ENTRY(ui.eta.read_bw), rate_str[0]);
906		gtk_entry_set_text(GTK_ENTRY(ui.eta.read_iops), iops_str[0]);
907		gtk_entry_set_text(GTK_ENTRY(ui.eta.write_bw), rate_str[1]);
908		gtk_entry_set_text(GTK_ENTRY(ui.eta.write_iops), iops_str[1]);
909
910		free(rate_str[0]);
911		free(rate_str[1]);
912		free(iops_str[0]);
913		free(iops_str[1]);
914	}
915
916	if (eta_str[0]) {
917		char *dst = output + strlen(output);
918
919		sprintf(dst, " - %s", eta_str);
920	}
921
922	gfio_update_thread_status(output, perc);
923	gdk_threads_leave();
924}
925
926static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
927{
928	struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
929	const char *os, *arch;
930	char buf[64];
931
932	os = fio_get_os_string(probe->os);
933	if (!os)
934		os = "unknown";
935
936	arch = fio_get_arch_string(probe->arch);
937	if (!arch)
938		os = "unknown";
939
940	if (!client->name)
941		client->name = strdup((char *) probe->hostname);
942
943	gdk_threads_enter();
944
945	gtk_label_set_text(GTK_LABEL(ui.probe.hostname), (char *) probe->hostname);
946	gtk_label_set_text(GTK_LABEL(ui.probe.os), os);
947	gtk_label_set_text(GTK_LABEL(ui.probe.arch), arch);
948	sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
949	gtk_label_set_text(GTK_LABEL(ui.probe.fio_ver), buf);
950
951	gdk_threads_leave();
952}
953
954static void gfio_update_thread_status(char *status_message, double perc)
955{
956	static char message[100];
957	const char *m = message;
958
959	strncpy(message, status_message, sizeof(message) - 1);
960	gtk_progress_bar_set_text(
961		GTK_PROGRESS_BAR(ui.thread_status_pb), m);
962	gtk_progress_bar_set_fraction(
963		GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0);
964	gtk_widget_queue_draw(ui.window);
965}
966
967static void gfio_quit_op(struct fio_client *client)
968{
969	struct gui *ui = client->client_data;
970
971	gdk_threads_enter();
972	gfio_set_connected(ui, 0);
973	gdk_threads_leave();
974}
975
976static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
977{
978	struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
979	struct gui *ui = client->client_data;
980	char tmp[8];
981	int i;
982
983	p->iodepth		= le32_to_cpu(p->iodepth);
984	p->rw			= le32_to_cpu(p->rw);
985
986	for (i = 0; i < 2; i++) {
987		p->min_bs[i] 	= le32_to_cpu(p->min_bs[i]);
988		p->max_bs[i]	= le32_to_cpu(p->max_bs[i]);
989	}
990
991	p->numjobs		= le32_to_cpu(p->numjobs);
992	p->group_reporting	= le32_to_cpu(p->group_reporting);
993
994	gdk_threads_enter();
995
996	gtk_entry_set_text(GTK_ENTRY(ui->eta.name), (gchar *) p->jobname);
997	gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), ddir_str(p->rw));
998	gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), (gchar *) p->ioengine);
999
1000	sprintf(tmp, "%u", p->iodepth);
1001	gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), tmp);
1002
1003	gdk_threads_leave();
1004}
1005
1006static void gfio_client_timed_out(struct fio_client *client)
1007{
1008	struct gui *ui = client->client_data;
1009	GtkWidget *dialog, *label, *content;
1010	char buf[256];
1011
1012	gdk_threads_enter();
1013
1014	gfio_set_connected(ui, 0);
1015	clear_ui_info(ui);
1016
1017	sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1018
1019	dialog = gtk_dialog_new_with_buttons("Timed out!",
1020			GTK_WINDOW(ui->window),
1021			GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1022			GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1023
1024	content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1025	label = gtk_label_new((const gchar *) buf);
1026	gtk_container_add(GTK_CONTAINER(content), label);
1027	gtk_widget_show_all(dialog);
1028	gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1029
1030	gtk_dialog_run(GTK_DIALOG(dialog));
1031	gtk_widget_destroy(dialog);
1032
1033	gdk_threads_leave();
1034}
1035
1036struct client_ops gfio_client_ops = {
1037	.text_op		= gfio_text_op,
1038	.disk_util		= gfio_disk_util_op,
1039	.thread_status		= gfio_thread_status_op,
1040	.group_stats		= gfio_group_stats_op,
1041	.eta			= gfio_update_eta,
1042	.probe			= gfio_probe_op,
1043	.quit			= gfio_quit_op,
1044	.add_job		= gfio_add_job_op,
1045	.timed_out		= gfio_client_timed_out,
1046	.stay_connected		= 1,
1047};
1048
1049static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1050                __attribute__((unused)) gpointer data)
1051{
1052        gtk_main_quit();
1053}
1054
1055static void *job_thread(void *arg)
1056{
1057	fio_handle_clients(&gfio_client_ops);
1058	return NULL;
1059}
1060
1061static int send_job_files(struct gui *ui)
1062{
1063	int i, ret = 0;
1064
1065	for (i = 0; i < ui->nr_job_files; i++) {
1066		ret = fio_clients_send_ini(ui->job_files[i]);
1067		if (ret)
1068			break;
1069
1070		free(ui->job_files[i]);
1071		ui->job_files[i] = NULL;
1072	}
1073	while (i < ui->nr_job_files) {
1074		free(ui->job_files[i]);
1075		ui->job_files[i] = NULL;
1076		i++;
1077	}
1078
1079	return ret;
1080}
1081
1082static void start_job_thread(struct gui *ui)
1083{
1084	if (send_job_files(ui)) {
1085		printf("Yeah, I didn't really like those options too much.\n");
1086		gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
1087		return;
1088	}
1089}
1090
1091static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1092                gpointer data)
1093{
1094	struct gui *ui = data;
1095
1096	gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
1097	start_job_thread(ui);
1098}
1099
1100static void file_open(GtkWidget *w, gpointer data);
1101
1102static void connect_clicked(GtkWidget *widget, gpointer data)
1103{
1104	struct gui *ui = data;
1105
1106	if (!ui->connected) {
1107		if (!ui->nr_job_files)
1108			file_open(widget, data);
1109		gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
1110		fio_clients_connect();
1111		pthread_create(&ui->t, NULL, job_thread, NULL);
1112		gfio_set_connected(ui, 1);
1113	} else {
1114		fio_clients_terminate();
1115		gfio_set_connected(ui, 0);
1116		clear_ui_info(ui);
1117	}
1118}
1119
1120static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
1121			struct button_spec *buttonspec)
1122{
1123	ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext);
1124	g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui);
1125	gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
1126	gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
1127	gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
1128}
1129
1130static void add_buttons(struct gui *ui,
1131				struct button_spec *buttonlist,
1132				int nbuttons)
1133{
1134	int i;
1135
1136	for (i = 0; i < nbuttons; i++)
1137		add_button(ui, i, ui->buttonbox, &buttonlist[i]);
1138}
1139
1140static void on_info_bar_response(GtkWidget *widget, gint response,
1141                                 gpointer data)
1142{
1143	if (response == GTK_RESPONSE_OK) {
1144		gtk_widget_destroy(widget);
1145		ui.error_info_bar = NULL;
1146	}
1147}
1148
1149void report_error(GError *error)
1150{
1151	if (ui.error_info_bar == NULL) {
1152		ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1153		                                               GTK_RESPONSE_OK,
1154		                                               NULL);
1155		g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1156		gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar),
1157		                              GTK_MESSAGE_ERROR);
1158
1159		ui.error_label = gtk_label_new(error->message);
1160		GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar));
1161		gtk_container_add(GTK_CONTAINER(container), ui.error_label);
1162
1163		gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0);
1164		gtk_widget_show_all(ui.vbox);
1165	} else {
1166		char buffer[256];
1167		snprintf(buffer, sizeof(buffer), "Failed to open file.");
1168		gtk_label_set(GTK_LABEL(ui.error_label), buffer);
1169	}
1170}
1171
1172static int get_connection_details(char **host, int *port, int *type,
1173				  int *server_start)
1174{
1175	GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo;
1176	GtkWidget *button;
1177	char *typeentry;
1178
1179	dialog = gtk_dialog_new_with_buttons("Connection details",
1180			GTK_WINDOW(ui.window),
1181			GTK_DIALOG_DESTROY_WITH_PARENT,
1182			GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1183			GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1184
1185	frame = gtk_frame_new("Hostname / socket name");
1186	vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1187	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1188
1189	box = gtk_vbox_new(FALSE, 6);
1190	gtk_container_add(GTK_CONTAINER(frame), box);
1191
1192	hbox = gtk_hbox_new(TRUE, 10);
1193	gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1194	hentry = gtk_entry_new();
1195	gtk_entry_set_text(GTK_ENTRY(hentry), "localhost");
1196	gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 0);
1197
1198	frame = gtk_frame_new("Port");
1199	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1200	box = gtk_vbox_new(FALSE, 10);
1201	gtk_container_add(GTK_CONTAINER(frame), box);
1202
1203	hbox = gtk_hbox_new(TRUE, 4);
1204	gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1205	pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1206
1207	frame = gtk_frame_new("Type");
1208	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1209	box = gtk_vbox_new(FALSE, 10);
1210	gtk_container_add(GTK_CONTAINER(frame), box);
1211
1212	hbox = gtk_hbox_new(TRUE, 4);
1213	gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1214
1215	combo = gtk_combo_box_text_new();
1216	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv4");
1217	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv6");
1218	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "local socket");
1219	gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
1220
1221	gtk_container_add(GTK_CONTAINER(hbox), combo);
1222
1223	frame = gtk_frame_new("Options");
1224	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1225	box = gtk_vbox_new(FALSE, 10);
1226	gtk_container_add(GTK_CONTAINER(frame), box);
1227
1228	hbox = gtk_hbox_new(TRUE, 4);
1229	gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1230
1231	button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1232	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1);
1233	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.");
1234	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6);
1235
1236	gtk_widget_show_all(dialog);
1237
1238	if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1239		gtk_widget_destroy(dialog);
1240		return 1;
1241	}
1242
1243	*host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry)));
1244	*port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1245
1246	typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
1247	if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1248		*type = Fio_client_ipv4;
1249	else if (!strncmp(typeentry, "IPv6", 4))
1250		*type = Fio_client_ipv6;
1251	else
1252		*type = Fio_client_socket;
1253	g_free(typeentry);
1254
1255	*server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
1256
1257	gtk_widget_destroy(dialog);
1258	return 0;
1259}
1260
1261static void file_open(GtkWidget *w, gpointer data)
1262{
1263	GtkWidget *dialog;
1264	GSList *filenames, *fn_glist;
1265	GtkFileFilter *filter;
1266	char *host;
1267	int port, type, server_start;
1268
1269	dialog = gtk_file_chooser_dialog_new("Open File",
1270		GTK_WINDOW(ui.window),
1271		GTK_FILE_CHOOSER_ACTION_OPEN,
1272		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1273		GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1274		NULL);
1275	gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1276
1277	filter = gtk_file_filter_new();
1278	gtk_file_filter_add_pattern(filter, "*.fio");
1279	gtk_file_filter_add_pattern(filter, "*.job");
1280	gtk_file_filter_add_mime_type(filter, "text/fio");
1281	gtk_file_filter_set_name(filter, "Fio job file");
1282	gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1283
1284	if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1285		gtk_widget_destroy(dialog);
1286		return;
1287	}
1288
1289	fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1290
1291	gtk_widget_destroy(dialog);
1292
1293	if (get_connection_details(&host, &port, &type, &server_start))
1294		goto err;
1295
1296	filenames = fn_glist;
1297	while (filenames != NULL) {
1298		ui.job_files = realloc(ui.job_files, (ui.nr_job_files + 1) * sizeof(char *));
1299		ui.job_files[ui.nr_job_files] = strdup(filenames->data);
1300		ui.nr_job_files++;
1301
1302		ui.client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1303		if (!ui.client) {
1304			GError *error;
1305
1306			error = g_error_new(g_quark_from_string("fio"), 1,
1307					"Failed to add client %s", host);
1308			report_error(error);
1309			g_error_free(error);
1310		}
1311		ui.client->client_data = &ui;
1312
1313		g_free(filenames->data);
1314		filenames = g_slist_next(filenames);
1315	}
1316	free(host);
1317err:
1318	g_slist_free(fn_glist);
1319}
1320
1321static void file_save(GtkWidget *w, gpointer data)
1322{
1323	GtkWidget *dialog;
1324
1325	dialog = gtk_file_chooser_dialog_new("Save File",
1326		GTK_WINDOW(ui.window),
1327		GTK_FILE_CHOOSER_ACTION_SAVE,
1328		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1329		GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1330		NULL);
1331
1332	gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1333	gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1334
1335	if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1336		char *filename;
1337
1338		filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1339		// save_job_file(filename);
1340		g_free(filename);
1341	}
1342	gtk_widget_destroy(dialog);
1343}
1344
1345static void preferences(GtkWidget *w, gpointer data)
1346{
1347	GtkWidget *dialog, *frame, *box, **buttons;
1348	int i;
1349
1350	dialog = gtk_dialog_new_with_buttons("Preferences",
1351		GTK_WINDOW(ui.window),
1352		GTK_DIALOG_DESTROY_WITH_PARENT,
1353		GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1354		GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1355		NULL);
1356
1357	frame = gtk_frame_new("Debug logging");
1358	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1359	box = gtk_hbox_new(FALSE, 6);
1360	gtk_container_add(GTK_CONTAINER(frame), box);
1361
1362	buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1363
1364	for (i = 0; i < FD_DEBUG_MAX; i++) {
1365		buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
1366		gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
1367		gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1368	}
1369
1370	gtk_widget_show_all(dialog);
1371
1372	if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1373		gtk_widget_destroy(dialog);
1374		return;
1375	}
1376
1377	for (i = 0; i < FD_DEBUG_MAX; i++) {
1378		int set;
1379
1380		set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1381		if (set)
1382			fio_debug |= (1UL << i);
1383	}
1384
1385	gtk_widget_destroy(dialog);
1386}
1387
1388static void about_dialog(GtkWidget *w, gpointer data)
1389{
1390	gtk_show_about_dialog(NULL,
1391		"program-name", "gfio",
1392		"comments", "Gtk2 UI for fio",
1393		"license", "GPLv2",
1394		"version", fio_version_string,
1395		"copyright", "Jens Axboe <axboe@kernel.dk> 2012",
1396		"logo-icon-name", "fio",
1397		/* Must be last: */
1398		NULL, NULL,
1399		NULL);
1400}
1401
1402static GtkActionEntry menu_items[] = {
1403	{ "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1404	{ "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
1405	{ "OpenFile", GTK_STOCK_OPEN, NULL,   "<Control>O", NULL, G_CALLBACK(file_open) },
1406	{ "SaveFile", GTK_STOCK_SAVE, NULL,   "<Control>S", NULL, G_CALLBACK(file_save) },
1407	{ "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
1408	{ "Quit", GTK_STOCK_QUIT, NULL,   "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1409	{ "About", GTK_STOCK_ABOUT, NULL,  NULL, NULL, G_CALLBACK(about_dialog) },
1410};
1411static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
1412
1413static const gchar *ui_string = " \
1414	<ui> \
1415		<menubar name=\"MainMenu\"> \
1416			<menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1417				<menuitem name=\"Open\" action=\"OpenFile\" /> \
1418				<menuitem name=\"Save\" action=\"SaveFile\" /> \
1419				<separator name=\"Separator\"/> \
1420				<menuitem name=\"Preferences\" action=\"Preferences\" /> \
1421				<separator name=\"Separator2\"/> \
1422				<menuitem name=\"Quit\" action=\"Quit\" /> \
1423			</menu> \
1424			<menu name=\"Help\" action=\"HelpMenuAction\"> \
1425				<menuitem name=\"About\" action=\"About\" /> \
1426			</menu> \
1427		</menubar> \
1428	</ui> \
1429";
1430
1431static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager)
1432{
1433	GtkActionGroup *action_group = gtk_action_group_new("Menu");
1434	GError *error = 0;
1435
1436	action_group = gtk_action_group_new("Menu");
1437	gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0);
1438
1439	gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1440	gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1441
1442	gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
1443	return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1444}
1445
1446void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1447                   GtkWidget *vbox, GtkUIManager *ui_manager)
1448{
1449        gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
1450}
1451
1452static void init_ui(int *argc, char **argv[], struct gui *ui)
1453{
1454	GtkSettings *settings;
1455	GtkUIManager *uimanager;
1456	GtkWidget *menu, *probe, *probe_frame, *probe_box;
1457
1458	memset(ui, 0, sizeof(*ui));
1459
1460	/* Magical g*thread incantation, you just need this thread stuff.
1461	 * Without it, the update that happens in gfio_update_thread_status
1462	 * doesn't really happen in a timely fashion, you need expose events
1463	 */
1464	if (!g_thread_supported())
1465		g_thread_init(NULL);
1466	gdk_threads_init();
1467
1468	gtk_init(argc, argv);
1469	settings = gtk_settings_get_default();
1470	gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1471	g_type_init();
1472
1473	ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1474        gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1475	gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500);
1476
1477	g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
1478	g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
1479
1480	ui->vbox = gtk_vbox_new(FALSE, 0);
1481	gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
1482
1483	uimanager = gtk_ui_manager_new();
1484	menu = get_menubar_menu(ui->window, uimanager);
1485	gfio_ui_setup(settings, menu, ui->vbox, uimanager);
1486
1487	/*
1488	 * Set up alignments for widgets at the top of ui,
1489	 * align top left, expand horizontally but not vertically
1490	 */
1491	ui->topalign = gtk_alignment_new(0, 0, 1, 0);
1492	ui->topvbox = gtk_vbox_new(FALSE, 3);
1493	gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
1494	gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
1495
1496	probe = gtk_frame_new("Job");
1497	gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3);
1498	probe_frame = gtk_vbox_new(FALSE, 3);
1499	gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1500
1501	probe_box = gtk_hbox_new(FALSE, 3);
1502	gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1503	ui->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1504	ui->probe.os = new_info_label_in_frame(probe_box, "OS");
1505	ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1506	ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1507
1508	probe_box = gtk_hbox_new(FALSE, 3);
1509	gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1510
1511	ui->eta.name = new_info_entry_in_frame(probe_box, "Name");
1512	ui->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
1513	ui->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
1514	ui->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
1515	ui->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1516	ui->eta.files = new_info_entry_in_frame(probe_box, "Open files");
1517
1518	probe_box = gtk_hbox_new(FALSE, 3);
1519	gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1520	ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1521	ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1522	ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1523	ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1524
1525	/*
1526	 * Only add this if we have a commit rate
1527	 */
1528#if 0
1529	probe_box = gtk_hbox_new(FALSE, 3);
1530	gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1531
1532	ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1533	ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1534
1535	ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1536	ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1537#endif
1538
1539	/*
1540	 * Add a text box for text op messages
1541	 */
1542	ui->textview = gtk_text_view_new();
1543	ui->text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->textview));
1544	gtk_text_buffer_set_text(ui->text, "", -1);
1545	gtk_text_view_set_editable(GTK_TEXT_VIEW(ui->textview), FALSE);
1546	gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ui->textview), FALSE);
1547	ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1548	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
1549					GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1550	gtk_container_add(GTK_CONTAINER(ui->scrolled_window), ui->textview);
1551	gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
1552			TRUE, TRUE, 0);
1553
1554	/*
1555	 * Set up alignments for widgets at the bottom of ui,
1556	 * align bottom left, expand horizontally but not vertically
1557	 */
1558	ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
1559	ui->buttonbox = gtk_hbox_new(FALSE, 0);
1560	gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
1561	gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
1562					FALSE, FALSE, 0);
1563
1564	add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
1565
1566	/*
1567	 * Set up thread status progress bar
1568	 */
1569	ui->thread_status_pb = gtk_progress_bar_new();
1570	gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
1571	gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
1572	gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1573
1574
1575	gtk_widget_show_all(ui->window);
1576}
1577
1578int main(int argc, char *argv[], char *envp[])
1579{
1580	if (initialize_fio(envp))
1581		return 1;
1582	if (fio_init_options())
1583		return 1;
1584
1585	init_ui(&argc, &argv, &ui);
1586
1587	gdk_threads_enter();
1588	gtk_main();
1589	gdk_threads_leave();
1590	return 0;
1591}
1592