Fio 3.12
[fio.git] / gclient.c
CommitLineData
2ef39e09 1#include <stdlib.h>
1252d8f2
JA
2#include <string.h>
3
4#include <glib.h>
5#include <cairo.h>
6#include <gtk/gtk.h>
7
8#include "fio.h"
9#include "gfio.h"
10#include "ghelpers.h"
11#include "goptions.h"
12#include "gerror.h"
13#include "graph.h"
14#include "gclient.h"
bf3f7027 15#include "printing.h"
0f38bbef 16#include "lib/pow2.h"
1252d8f2
JA
17
18static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
19 struct group_run_stats *rs);
20
21static gboolean results_window_delete(GtkWidget *w, gpointer data)
22{
23 struct gui_entry *ge = (struct gui_entry *) data;
24
25 gtk_widget_destroy(w);
26 ge->results_window = NULL;
27 ge->results_notebook = NULL;
28 return TRUE;
29}
30
31static void results_close(GtkWidget *w, gpointer *data)
32{
33 struct gui_entry *ge = (struct gui_entry *) data;
34
35 gtk_widget_destroy(ge->results_window);
36}
37
bf3f7027
SC
38static void results_print(GtkWidget *w, gpointer *data)
39{
40 struct gui_entry *ge = (struct gui_entry *) data;
41
42 gfio_print_results(ge);
43}
44
1252d8f2
JA
45static GtkActionEntry results_menu_items[] = {
46 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
47 { "GraphMenuAction", GTK_STOCK_FILE, "Graph", NULL, NULL, NULL},
bf3f7027 48 { "PrintFile", GTK_STOCK_PRINT, "Print", "<Control>P", NULL, G_CALLBACK(results_print) },
1252d8f2
JA
49 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(results_close) },
50};
b920b29b 51static gint results_nmenu_items = ARRAY_SIZE(results_menu_items);
1252d8f2
JA
52
53static const gchar *results_ui_string = " \
54 <ui> \
55 <menubar name=\"MainMenu\"> \
56 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
bf3f7027 57 <menuitem name=\"Print\" action=\"PrintFile\" /> \
1252d8f2
JA
58 <menuitem name=\"Close\" action=\"CloseFile\" /> \
59 </menu> \
60 <menu name=\"GraphMenu\" action=\"GraphMenuAction\"> \
61 </menu>\
62 </menubar> \
63 </ui> \
64";
65
66static GtkWidget *get_results_menubar(GtkWidget *window, struct gui_entry *ge)
67{
68 GtkActionGroup *action_group;
69 GtkWidget *widget;
70 GError *error = 0;
71
72 ge->results_uimanager = gtk_ui_manager_new();
73
74 action_group = gtk_action_group_new("ResultsMenu");
75 gtk_action_group_add_actions(action_group, results_menu_items, results_nmenu_items, ge);
76
77 gtk_ui_manager_insert_action_group(ge->results_uimanager, action_group, 0);
78 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ge->results_uimanager), results_ui_string, -1, &error);
79
80 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ge->results_uimanager));
81
82 widget = gtk_ui_manager_get_widget(ge->results_uimanager, "/MainMenu");
83 return widget;
84}
85
86static GtkWidget *get_results_window(struct gui_entry *ge)
87{
88 GtkWidget *win, *notebook, *vbox;
89
90 if (ge->results_window)
91 return ge->results_notebook;
92
93 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
94 gtk_window_set_title(GTK_WINDOW(win), "Results");
95 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
96 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
97 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
98
99 vbox = gtk_vbox_new(FALSE, 0);
100 gtk_container_add(GTK_CONTAINER(win), vbox);
101
102 ge->results_menu = get_results_menubar(win, ge);
103 gtk_box_pack_start(GTK_BOX(vbox), ge->results_menu, FALSE, FALSE, 0);
104
105 notebook = gtk_notebook_new();
106 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
107 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
108 gtk_container_add(GTK_CONTAINER(vbox), notebook);
109
110 ge->results_window = win;
111 ge->results_notebook = notebook;
112 return ge->results_notebook;
113}
114
115static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
116{
117 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
118 struct gfio_client *gc = client->client_data;
119 struct gui_entry *ge = gc->ge;
120 struct gui *ui = ge->ui;
121 GtkTreeIter iter;
122 struct tm *tm;
123 time_t sec;
38e7d820 124 char tmp[64], timebuf[96];
1252d8f2
JA
125
126 sec = p->log_sec;
127 tm = localtime(&sec);
128 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1fae4855 129 sprintf(timebuf, "%s.%03ld", tmp, (long) p->log_usec / 1000);
1252d8f2
JA
130
131 gdk_threads_enter();
132
133 gtk_list_store_append(ui->log_model, &iter);
134 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
135 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
3ad04399 136 gtk_list_store_set(ui->log_model, &iter, 2, log_get_level(p->level), -1);
1252d8f2
JA
137 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
138
139 if (p->level == FIO_LOG_ERR)
140 gfio_view_log(ui);
141
142 gdk_threads_leave();
143}
144
145static void disk_util_destroy(GtkWidget *w, gpointer data)
146{
147 struct gui_entry *ge = (struct gui_entry *) data;
148
149 ge->disk_util_vbox = NULL;
150 gtk_widget_destroy(w);
151}
152
153static GtkWidget *gfio_disk_util_get_vbox(struct gui_entry *ge)
154{
155 GtkWidget *vbox, *box, *scroll, *res_notebook;
156
157 if (ge->disk_util_vbox)
158 return ge->disk_util_vbox;
159
160 scroll = get_scrolled_window(5);
161 vbox = gtk_vbox_new(FALSE, 3);
162 box = gtk_hbox_new(FALSE, 0);
163 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
164
165 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
166 res_notebook = get_results_window(ge);
167
168 gtk_notebook_append_page(GTK_NOTEBOOK(res_notebook), scroll, gtk_label_new("Disk utilization"));
169 ge->disk_util_vbox = box;
170 g_signal_connect(vbox, "destroy", G_CALLBACK(disk_util_destroy), ge);
171
172 return ge->disk_util_vbox;
173}
174
175static int __gfio_disk_util_show(GtkWidget *res_notebook,
176 struct gfio_client *gc, struct cmd_du_pdu *p)
177{
178 GtkWidget *box, *frame, *entry, *vbox, *util_vbox;
179 struct gui_entry *ge = gc->ge;
180 double util;
181 char tmp[16];
182
183 util_vbox = gfio_disk_util_get_vbox(ge);
184
185 vbox = gtk_vbox_new(FALSE, 3);
186 gtk_container_add(GTK_CONTAINER(util_vbox), vbox);
187
188 frame = gtk_frame_new((char *) p->dus.name);
189 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
190
191 box = gtk_vbox_new(FALSE, 3);
192 gtk_container_add(GTK_CONTAINER(frame), box);
193
194 frame = gtk_frame_new("Read");
195 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
196 vbox = gtk_hbox_new(TRUE, 3);
197 gtk_container_add(GTK_CONTAINER(frame), vbox);
198 entry = new_info_entry_in_frame(vbox, "IOs");
57e2bbcb 199 entry_set_int_value(entry, p->dus.s.ios[0]);
1252d8f2 200 entry = new_info_entry_in_frame(vbox, "Merges");
57e2bbcb 201 entry_set_int_value(entry, p->dus.s.merges[0]);
1252d8f2 202 entry = new_info_entry_in_frame(vbox, "Sectors");
57e2bbcb 203 entry_set_int_value(entry, p->dus.s.sectors[0]);
1252d8f2 204 entry = new_info_entry_in_frame(vbox, "Ticks");
57e2bbcb 205 entry_set_int_value(entry, p->dus.s.ticks[0]);
1252d8f2
JA
206
207 frame = gtk_frame_new("Write");
208 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
209 vbox = gtk_hbox_new(TRUE, 3);
210 gtk_container_add(GTK_CONTAINER(frame), vbox);
211 entry = new_info_entry_in_frame(vbox, "IOs");
57e2bbcb 212 entry_set_int_value(entry, p->dus.s.ios[1]);
1252d8f2 213 entry = new_info_entry_in_frame(vbox, "Merges");
57e2bbcb 214 entry_set_int_value(entry, p->dus.s.merges[1]);
1252d8f2 215 entry = new_info_entry_in_frame(vbox, "Sectors");
57e2bbcb 216 entry_set_int_value(entry, p->dus.s.sectors[1]);
1252d8f2 217 entry = new_info_entry_in_frame(vbox, "Ticks");
57e2bbcb 218 entry_set_int_value(entry, p->dus.s.ticks[1]);
1252d8f2
JA
219
220 frame = gtk_frame_new("Shared");
221 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
222 vbox = gtk_hbox_new(TRUE, 3);
223 gtk_container_add(GTK_CONTAINER(frame), vbox);
224 entry = new_info_entry_in_frame(vbox, "IO ticks");
57e2bbcb 225 entry_set_int_value(entry, p->dus.s.io_ticks);
1252d8f2 226 entry = new_info_entry_in_frame(vbox, "Time in queue");
57e2bbcb 227 entry_set_int_value(entry, p->dus.s.time_in_queue);
1252d8f2
JA
228
229 util = 0.0;
57e2bbcb
DG
230 if (p->dus.s.msec)
231 util = (double) 100 * p->dus.s.io_ticks / (double) p->dus.s.msec;
1252d8f2
JA
232 if (util > 100.0)
233 util = 100.0;
234
235 sprintf(tmp, "%3.2f%%", util);
236 entry = new_info_entry_in_frame(vbox, "Disk utilization");
237 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
238
239 gtk_widget_show_all(ge->results_window);
240 return 0;
241}
242
243static int gfio_disk_util_show(struct gfio_client *gc)
244{
245 struct gui_entry *ge = gc->ge;
246 GtkWidget *res_notebook;
247 int i;
248
249 if (!gc->nr_du)
250 return 1;
251
252 res_notebook = get_results_window(ge);
253
254 for (i = 0; i < gc->nr_du; i++) {
255 struct cmd_du_pdu *p = &gc->du[i];
256
257 __gfio_disk_util_show(res_notebook, gc, p);
258 }
259
260 gtk_widget_show_all(ge->results_window);
261 return 0;
262}
263
264static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
265{
266 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
267 struct gfio_client *gc = client->client_data;
268 struct gui_entry *ge = gc->ge;
269 unsigned int nr = gc->nr_du;
270
271 gc->du = realloc(gc->du, (nr + 1) * sizeof(struct cmd_du_pdu));
272 memcpy(&gc->du[nr], p, sizeof(*p));
273 gc->nr_du++;
274
275 gdk_threads_enter();
276 if (ge->results_window)
277 __gfio_disk_util_show(ge->results_notebook, gc, p);
278 else
279 gfio_disk_util_show(gc);
280 gdk_threads_leave();
281}
282
1252d8f2
JA
283static int sum_stat_nr;
284
285static void gfio_thread_status_op(struct fio_client *client,
286 struct fio_net_cmd *cmd)
287{
288 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
289
290 gfio_display_ts(client, &p->ts, &p->rs);
291
292 if (sum_stat_clients == 1)
293 return;
294
fd595830 295 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr == 1);
1252d8f2
JA
296 sum_group_stats(&client_gs, &p->rs);
297
298 client_ts.members++;
299 client_ts.thread_number = p->ts.thread_number;
300 client_ts.groupid = p->ts.groupid;
18aa1998 301 client_ts.sig_figs = p->ts.sig_figs;
1252d8f2
JA
302
303 if (++sum_stat_nr == sum_stat_clients) {
304 strcpy(client_ts.name, "All clients");
305 gfio_display_ts(client, &client_ts, &client_gs);
306 }
307}
308
309static void gfio_group_stats_op(struct fio_client *client,
310 struct fio_net_cmd *cmd)
311{
312 /* We're ignoring group stats for now */
313}
314
315static void gfio_update_thread_status(struct gui_entry *ge,
316 char *status_message, double perc)
317{
318 static char message[100];
319 const char *m = message;
320
321 strncpy(message, status_message, sizeof(message) - 1);
322 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
323 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
324 gtk_widget_queue_draw(ge->ui->window);
325}
326
327static void gfio_update_thread_status_all(struct gui *ui, char *status_message,
328 double perc)
329{
330 static char message[100];
331 const char *m = message;
332
333 strncpy(message, status_message, sizeof(message) - 1);
334 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
335 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
336 gtk_widget_queue_draw(ui->window);
337}
338
339/*
340 * Client specific ETA
341 */
342static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
343{
344 struct gfio_client *gc = client->client_data;
345 struct gui_entry *ge = gc->ge;
346 static int eta_good;
347 char eta_str[128];
348 char output[256];
349 char tmp[32];
350 double perc = 0.0;
351 int i2p = 0;
352
353 gdk_threads_enter();
354
355 eta_str[0] = '\0';
356 output[0] = '\0';
357
358 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
359 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
360 eta_to_str(eta_str, je->eta_sec);
361 }
362
363 sprintf(tmp, "%u", je->nr_running);
364 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
365 sprintf(tmp, "%u", je->files_open);
366 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
367
1252d8f2 368 if (je->eta_sec != INT_MAX && je->nr_running) {
5442cfb0
JA
369 char *iops_str[DDIR_RWDIR_CNT];
370 char *rate_str[DDIR_RWDIR_CNT];
d694a6a7
RE
371 char *rate_alt[DDIR_RWDIR_CNT];
372 char tmp[128];
5442cfb0 373 int i;
1252d8f2
JA
374
375 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
376 strcpy(output, "-.-% done");
377 else {
378 eta_good = 1;
379 perc *= 100.0;
380 sprintf(output, "%3.1f%% done", perc);
381 }
382
e883cb35
JF
383 iops_str[0] = num2str(je->iops[0], je->sig_figs, 1, 0, N2S_PERSEC);
384 iops_str[1] = num2str(je->iops[1], je->sig_figs, 1, 0, N2S_PERSEC);
385 iops_str[2] = num2str(je->iops[2], je->sig_figs, 1, 0, N2S_PERSEC);
1252d8f2 386
e883cb35
JF
387 rate_str[0] = num2str(je->rate[0], je->sig_figs, 10, i2p, N2S_BYTEPERSEC);
388 rate_alt[0] = num2str(je->rate[0], je->sig_figs, 10, !i2p, N2S_BYTEPERSEC);
d694a6a7
RE
389 snprintf(tmp, sizeof(tmp), "%s (%s)", rate_str[0], rate_alt[0]);
390 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), tmp);
1252d8f2 391 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
d694a6a7 392
e883cb35
JF
393 rate_str[1] = num2str(je->rate[1], je->sig_figs, 10, i2p, N2S_BYTEPERSEC);
394 rate_alt[1] = num2str(je->rate[1], je->sig_figs, 10, !i2p, N2S_BYTEPERSEC);
d694a6a7
RE
395 snprintf(tmp, sizeof(tmp), "%s (%s)", rate_str[1], rate_alt[1]);
396 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), tmp);
1252d8f2 397 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
d694a6a7 398
e883cb35
JF
399 rate_str[2] = num2str(je->rate[2], je->sig_figs, 10, i2p, N2S_BYTEPERSEC);
400 rate_alt[2] = num2str(je->rate[2], je->sig_figs, 10, !i2p, N2S_BYTEPERSEC);
d694a6a7
RE
401 snprintf(tmp, sizeof(tmp), "%s (%s)", rate_str[2], rate_alt[2]);
402 gtk_entry_set_text(GTK_ENTRY(ge->eta.trim_bw), tmp);
5442cfb0 403 gtk_entry_set_text(GTK_ENTRY(ge->eta.trim_iops), iops_str[2]);
1252d8f2 404
8dfd6071
JA
405 graph_add_xy_data(ge->graphs.iops_graph, ge->graphs.read_iops, je->elapsed_sec, je->iops[0], iops_str[0]);
406 graph_add_xy_data(ge->graphs.iops_graph, ge->graphs.write_iops, je->elapsed_sec, je->iops[1], iops_str[1]);
5442cfb0 407 graph_add_xy_data(ge->graphs.iops_graph, ge->graphs.trim_iops, je->elapsed_sec, je->iops[2], iops_str[2]);
8dfd6071
JA
408 graph_add_xy_data(ge->graphs.bandwidth_graph, ge->graphs.read_bw, je->elapsed_sec, je->rate[0], rate_str[0]);
409 graph_add_xy_data(ge->graphs.bandwidth_graph, ge->graphs.write_bw, je->elapsed_sec, je->rate[1], rate_str[1]);
5442cfb0 410 graph_add_xy_data(ge->graphs.bandwidth_graph, ge->graphs.trim_bw, je->elapsed_sec, je->rate[2], rate_str[2]);
1252d8f2 411
5442cfb0
JA
412 for (i = 0; i < DDIR_RWDIR_CNT; i++) {
413 free(rate_str[i]);
d694a6a7 414 free(rate_alt[i]);
5442cfb0
JA
415 free(iops_str[i]);
416 }
1252d8f2
JA
417 }
418
419 if (eta_str[0]) {
420 char *dst = output + strlen(output);
421
422 sprintf(dst, " - %s", eta_str);
423 }
3c3ed070 424
1252d8f2
JA
425 gfio_update_thread_status(ge, output, perc);
426 gdk_threads_leave();
427}
428
429/*
430 * Update ETA in main window for all clients
431 */
432static void gfio_update_all_eta(struct jobs_eta *je)
433{
434 struct gui *ui = &main_ui;
435 static int eta_good;
436 char eta_str[128];
437 char output[256];
438 double perc = 0.0;
b78ca651 439 int i, i2p = 0;
1252d8f2
JA
440
441 gdk_threads_enter();
442
443 eta_str[0] = '\0';
444 output[0] = '\0';
445
446 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
447 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
448 eta_to_str(eta_str, je->eta_sec);
449 }
450
1252d8f2
JA
451 entry_set_int_value(ui->eta.jobs, je->nr_running);
452
453 if (je->eta_sec != INT_MAX && je->nr_running) {
d694a6a7
RE
454 char *iops_str[DDIR_RWDIR_CNT];
455 char *rate_str[DDIR_RWDIR_CNT];
456 char *rate_alt[DDIR_RWDIR_CNT];
457 char tmp[128];
1252d8f2
JA
458
459 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
460 strcpy(output, "-.-% done");
461 else {
462 eta_good = 1;
463 perc *= 100.0;
464 sprintf(output, "%3.1f%% done", perc);
465 }
466
e883cb35
JF
467 iops_str[0] = num2str(je->iops[0], je->sig_figs, 1, 0, N2S_PERSEC);
468 iops_str[1] = num2str(je->iops[1], je->sig_figs, 1, 0, N2S_PERSEC);
469 iops_str[2] = num2str(je->iops[2], je->sig_figs, 1, 0, N2S_PERSEC);
1252d8f2 470
e883cb35
JF
471 rate_str[0] = num2str(je->rate[0], je->sig_figs, 10, i2p, N2S_BYTEPERSEC);
472 rate_alt[0] = num2str(je->rate[0], je->sig_figs, 10, !i2p, N2S_BYTEPERSEC);
d694a6a7
RE
473 snprintf(tmp, sizeof(tmp), "%s (%s)", rate_str[0], rate_alt[0]);
474 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), tmp);
1252d8f2 475 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
d694a6a7 476
e883cb35
JF
477 rate_str[1] = num2str(je->rate[1], je->sig_figs, 10, i2p, N2S_BYTEPERSEC);
478 rate_alt[1] = num2str(je->rate[1], je->sig_figs, 10, !i2p, N2S_BYTEPERSEC);
d694a6a7
RE
479 snprintf(tmp, sizeof(tmp), "%s (%s)", rate_str[1], rate_alt[1]);
480 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), tmp);
1252d8f2 481 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
d694a6a7 482
e883cb35
JF
483 rate_str[2] = num2str(je->rate[2], je->sig_figs, 10, i2p, N2S_BYTEPERSEC);
484 rate_alt[2] = num2str(je->rate[2], je->sig_figs, 10, !i2p, N2S_BYTEPERSEC);
d694a6a7
RE
485 snprintf(tmp, sizeof(tmp), "%s (%s)", rate_str[2], rate_alt[2]);
486 gtk_entry_set_text(GTK_ENTRY(ui->eta.trim_bw), tmp);
b78ca651 487 gtk_entry_set_text(GTK_ENTRY(ui->eta.trim_iops), iops_str[2]);
1252d8f2 488
8dfd6071
JA
489 graph_add_xy_data(ui->graphs.iops_graph, ui->graphs.read_iops, je->elapsed_sec, je->iops[0], iops_str[0]);
490 graph_add_xy_data(ui->graphs.iops_graph, ui->graphs.write_iops, je->elapsed_sec, je->iops[1], iops_str[1]);
b78ca651 491 graph_add_xy_data(ui->graphs.iops_graph, ui->graphs.trim_iops, je->elapsed_sec, je->iops[2], iops_str[2]);
8dfd6071
JA
492 graph_add_xy_data(ui->graphs.bandwidth_graph, ui->graphs.read_bw, je->elapsed_sec, je->rate[0], rate_str[0]);
493 graph_add_xy_data(ui->graphs.bandwidth_graph, ui->graphs.write_bw, je->elapsed_sec, je->rate[1], rate_str[1]);
b78ca651 494 graph_add_xy_data(ui->graphs.bandwidth_graph, ui->graphs.trim_bw, je->elapsed_sec, je->rate[2], rate_str[2]);
1252d8f2 495
b78ca651
JA
496 for (i = 0; i < DDIR_RWDIR_CNT; i++) {
497 free(rate_str[i]);
d694a6a7 498 free(rate_alt[i]);
b78ca651
JA
499 free(iops_str[i]);
500 }
1252d8f2
JA
501 }
502
503 if (eta_str[0]) {
504 char *dst = output + strlen(output);
505
506 sprintf(dst, " - %s", eta_str);
507 }
3c3ed070 508
1252d8f2
JA
509 gfio_update_thread_status_all(ui, output, perc);
510 gdk_threads_leave();
511}
512
513static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
514{
aa79e6aa 515 struct cmd_probe_reply_pdu *probe = (struct cmd_probe_reply_pdu *) cmd->payload;
1252d8f2
JA
516 struct gfio_client *gc = client->client_data;
517 struct gui_entry *ge = gc->ge;
518 const char *os, *arch;
1252d8f2
JA
519
520 os = fio_get_os_string(probe->os);
521 if (!os)
522 os = "unknown";
523
524 arch = fio_get_arch_string(probe->arch);
525 if (!arch)
526 os = "unknown";
527
528 if (!client->name)
529 client->name = strdup((char *) probe->hostname);
530
d31e26d0
JA
531 gc->client_cpus = le32_to_cpu(probe->cpus);
532 gc->client_flags = le64_to_cpu(probe->flags);
533
1252d8f2
JA
534 gdk_threads_enter();
535
536 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
537 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
538 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
8029542b 539 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), (char *) probe->fio_version);
1252d8f2
JA
540
541 gfio_set_state(ge, GE_STATE_CONNECTED);
542
543 gdk_threads_leave();
544}
545
546static void gfio_quit_op(struct fio_client *client, struct fio_net_cmd *cmd)
547{
548 struct gfio_client *gc = client->client_data;
549
550 gdk_threads_enter();
551 gfio_set_state(gc->ge, GE_STATE_NEW);
552 gdk_threads_leave();
553}
554
cf3d8241
JA
555static struct thread_options *gfio_client_add_job(struct gfio_client *gc,
556 struct thread_options_pack *top)
557{
558 struct gfio_client_options *gco;
559
560 gco = calloc(1, sizeof(*gco));
561 convert_thread_options_to_cpu(&gco->o, top);
753e9e64 562 INIT_FLIST_HEAD(&gco->list);
cf3d8241 563 flist_add_tail(&gco->list, &gc->o_list);
753e9e64 564 gc->o_list_nr = 1;
cf3d8241
JA
565 return &gco->o;
566}
567
1252d8f2
JA
568static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
569{
570 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
571 struct gfio_client *gc = client->client_data;
1252d8f2 572 struct gui_entry *ge = gc->ge;
cf3d8241 573 struct thread_options *o;
1252d8f2
JA
574 char *c1, *c2, *c3, *c4;
575 char tmp[80];
d694a6a7 576 int i2p;
1252d8f2
JA
577
578 p->thread_number = le32_to_cpu(p->thread_number);
579 p->groupid = le32_to_cpu(p->groupid);
cf3d8241 580 o = gfio_client_add_job(gc, &p->top);
1252d8f2
JA
581
582 gdk_threads_enter();
583
f762cef9 584 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(ge->eta.names), (gchar *) o->name);
1252d8f2
JA
585 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
586
587 sprintf(tmp, "%s %s", o->odirect ? "direct" : "buffered", ddir_str(o->td_ddir));
588 multitext_add_entry(&ge->eta.iotype, tmp);
589
d694a6a7 590 i2p = is_power_of_2(o->kb_base);
e883cb35
JF
591 c1 = num2str(o->min_bs[DDIR_READ], o->sig_figs, 1, i2p, N2S_BYTE);
592 c2 = num2str(o->max_bs[DDIR_READ], o->sig_figs, 1, i2p, N2S_BYTE);
593 c3 = num2str(o->min_bs[DDIR_WRITE], o->sig_figs, 1, i2p, N2S_BYTE);
594 c4 = num2str(o->max_bs[DDIR_WRITE], o->sig_figs, 1, i2p, N2S_BYTE);
d694a6a7
RE
595
596 sprintf(tmp, "%s-%s,%s-%s", c1, c2, c3, c4);
1252d8f2
JA
597 free(c1);
598 free(c2);
599 free(c3);
600 free(c4);
601 multitext_add_entry(&ge->eta.bs, tmp);
602
603 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
604
605 sprintf(tmp, "%u", o->iodepth);
606 multitext_add_entry(&ge->eta.iodepth, tmp);
607
608 multitext_set_entry(&ge->eta.iotype, 0);
609 multitext_set_entry(&ge->eta.bs, 0);
610 multitext_set_entry(&ge->eta.ioengine, 0);
611 multitext_set_entry(&ge->eta.iodepth, 0);
612
613 gfio_set_state(ge, GE_STATE_JOB_SENT);
614
615 gdk_threads_leave();
616}
617
40c60516
JA
618static void gfio_update_job_op(struct fio_client *client,
619 struct fio_net_cmd *cmd)
620{
621 uint32_t *pdu_error = (uint32_t *) cmd->payload;
622 struct gfio_client *gc = client->client_data;
623
28db256b
JA
624 gc->update_job_status = le32_to_cpu(*pdu_error);
625 gc->update_job_done = 1;
40c60516
JA
626}
627
1252d8f2
JA
628static void gfio_client_timed_out(struct fio_client *client)
629{
630 struct gfio_client *gc = client->client_data;
631 char buf[256];
632
633 gdk_threads_enter();
634
635 gfio_set_state(gc->ge, GE_STATE_NEW);
636 clear_ge_ui_info(gc->ge);
637
638 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
639 gfio_report_info(gc->ge->ui, "Network timeout", buf);
640
641 gdk_threads_leave();
642}
643
36ed2f9a 644static void gfio_client_stop(struct fio_client *client)
1252d8f2
JA
645{
646 struct gfio_client *gc = client->client_data;
647
648 gdk_threads_enter();
649
650 gfio_set_state(gc->ge, GE_STATE_JOB_DONE);
651
652 if (gc->err_entry)
653 entry_set_int_value(gc->err_entry, client->error);
654
655 gdk_threads_leave();
656}
657
658static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd)
659{
660 struct gfio_client *gc = client->client_data;
661
662 gdk_threads_enter();
663 gfio_set_state(gc->ge, GE_STATE_JOB_STARTED);
664 gdk_threads_leave();
665}
666
667static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd)
668{
669 struct gfio_client *gc = client->client_data;
670
671 gdk_threads_enter();
672 gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING);
673 gdk_threads_leave();
674}
675
1252d8f2
JA
676static void gfio_add_total_depths_tree(GtkListStore *model,
677 struct thread_stat *ts, unsigned int len)
678{
679 double io_u_dist[FIO_IO_U_MAP_NR];
680 GtkTreeIter iter;
681 /* Bits 1-6, and 8 */
682 const int add_mask = 0x17e;
683 int i, j;
684
d79db122 685 stat_calc_dist(ts->io_u_map, ddir_rw_sum(ts->total_io_u), io_u_dist);
1252d8f2
JA
686
687 gtk_list_store_append(model, &iter);
688
689 gtk_list_store_set(model, &iter, 0, "Total", -1);
690
691 for (i = 1, j = 0; i < len; i++) {
692 char fbuf[32];
693
694 if (!(add_mask & (1UL << (i - 1))))
695 sprintf(fbuf, "0.0%%");
696 else {
697 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
698 j++;
699 }
700
701 gtk_list_store_set(model, &iter, i, fbuf, -1);
702 }
703
704}
705
706static void gfio_add_end_results(struct gfio_client *gc, struct thread_stat *ts,
707 struct group_run_stats *rs)
708{
709 unsigned int nr = gc->nr_results;
710
711 gc->results = realloc(gc->results, (nr + 1) * sizeof(struct end_results));
712 memcpy(&gc->results[nr].ts, ts, sizeof(*ts));
713 memcpy(&gc->results[nr].gs, rs, sizeof(*rs));
714 gc->nr_results++;
715}
716
717static void gfio_add_sc_depths_tree(GtkListStore *model,
718 struct thread_stat *ts, unsigned int len,
719 int submit)
720{
721 double io_u_dist[FIO_IO_U_MAP_NR];
722 GtkTreeIter iter;
723 /* Bits 0, and 3-8 */
724 const int add_mask = 0x1f9;
725 int i, j;
726
727 if (submit)
728 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
729 else
730 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
731
732 gtk_list_store_append(model, &iter);
733
734 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
735
736 for (i = 1, j = 0; i < len; i++) {
737 char fbuf[32];
738
739 if (!(add_mask & (1UL << (i - 1))))
740 sprintf(fbuf, "0.0%%");
741 else {
742 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
743 j++;
744 }
745
746 gtk_list_store_set(model, &iter, i, fbuf, -1);
747 }
748
749}
750
751static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
752{
753 GtkWidget *frame, *box, *tree_view = NULL;
754 GtkTreeSelection *selection;
755 GtkListStore *model;
1252d8f2
JA
756 int i;
757 const char *labels[] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
05775438 758 const int nr_labels = ARRAY_SIZE(labels);
1fae4855 759 GType types[nr_labels];
1252d8f2
JA
760
761 frame = gtk_frame_new("IO depths");
762 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
763
764 box = gtk_hbox_new(FALSE, 3);
765 gtk_container_add(GTK_CONTAINER(frame), box);
766
767 for (i = 0; i < nr_labels; i++)
768 types[i] = G_TYPE_STRING;
769
770 model = gtk_list_store_newv(nr_labels, types);
771
772 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
773 gtk_widget_set_can_focus(tree_view, FALSE);
774
775 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
776 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
777
778 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
779 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
780
781 for (i = 0; i < nr_labels; i++)
782 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
783
784 gfio_add_total_depths_tree(model, ts, nr_labels);
785 gfio_add_sc_depths_tree(model, ts, nr_labels, 1);
786 gfio_add_sc_depths_tree(model, ts, nr_labels, 0);
787
f5e653e6 788 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, TRUE, 3);
1252d8f2
JA
789}
790
791static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
792{
793 GtkWidget *box, *frame, *entry;
794 double usr_cpu, sys_cpu;
795 unsigned long runtime;
796 char tmp[32];
797
798 runtime = ts->total_run_time;
799 if (runtime) {
800 double runt = (double) runtime;
801
802 usr_cpu = (double) ts->usr_time * 100 / runt;
803 sys_cpu = (double) ts->sys_time * 100 / runt;
804 } else {
805 usr_cpu = 0;
806 sys_cpu = 0;
807 }
808
809 frame = gtk_frame_new("OS resources");
810 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
811
812 box = gtk_hbox_new(FALSE, 3);
813 gtk_container_add(GTK_CONTAINER(frame), box);
814
815 entry = new_info_entry_in_frame(box, "User CPU");
816 sprintf(tmp, "%3.2f%%", usr_cpu);
817 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
818 entry = new_info_entry_in_frame(box, "System CPU");
819 sprintf(tmp, "%3.2f%%", sys_cpu);
820 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
821 entry = new_info_entry_in_frame(box, "Context switches");
822 entry_set_int_value(entry, ts->ctx);
823 entry = new_info_entry_in_frame(box, "Major faults");
824 entry_set_int_value(entry, ts->majf);
825 entry = new_info_entry_in_frame(box, "Minor faults");
826 entry_set_int_value(entry, ts->minf);
827}
828
829static GtkWidget *gfio_output_lat_buckets(double *lat, const char **labels,
830 int num)
831{
832 GtkWidget *tree_view;
833 GtkTreeSelection *selection;
834 GtkListStore *model;
835 GtkTreeIter iter;
836 GType *types;
837 int i;
838
839 types = malloc(num * sizeof(GType));
840
841 for (i = 0; i < num; i++)
842 types[i] = G_TYPE_STRING;
843
844 model = gtk_list_store_newv(num, types);
845 free(types);
846 types = NULL;
847
848 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
849 gtk_widget_set_can_focus(tree_view, FALSE);
850
851 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
852 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
853
854 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
855 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
856
857 for (i = 0; i < num; i++)
858 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
859
860 gtk_list_store_append(model, &iter);
861
862 for (i = 0; i < num; i++) {
863 char fbuf[32];
864
865 if (lat[i] <= 0.0)
866 sprintf(fbuf, "0.00");
867 else
868 sprintf(fbuf, "%3.2f%%", lat[i]);
869
870 gtk_list_store_set(model, &iter, i, fbuf, -1);
871 }
872
873 return tree_view;
874}
875
876static struct graph *setup_lat_bucket_graph(const char *title, double *lat,
877 const char **labels,
878 unsigned int len,
879 double xdim, double ydim)
880{
881 struct graph *g;
882 int i;
883
884 g = graph_new(xdim, ydim, gfio_graph_font);
885 graph_title(g, title);
886 graph_x_title(g, "Buckets");
887 graph_y_title(g, "Percent");
888
889 for (i = 0; i < len; i++) {
8dfd6071
JA
890 graph_label_t l;
891
892 l = graph_add_label(g, labels[i]);
893 graph_add_data(g, l, lat[i]);
1252d8f2
JA
894 }
895
896 return g;
897}
898
899static int on_expose_lat_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
900{
901 struct graph *g = p;
902 cairo_t *cr;
903
cd0d2db8 904 cr = gdk_cairo_create(gtk_widget_get_window(w));
1252d8f2
JA
905#if 0
906 if (graph_has_tooltips(g)) {
907 g_object_set(w, "has-tooltip", TRUE, NULL);
908 g_signal_connect(w, "query-tooltip", G_CALLBACK(clat_graph_tooltip), g);
909 }
910#endif
911 cairo_set_source_rgb(cr, 0, 0, 0);
912 bar_graph_draw(g, cr);
913 cairo_destroy(cr);
914
915 return FALSE;
916}
917
918static gint on_config_lat_drawing_area(GtkWidget *w, GdkEventConfigure *event,
919 gpointer data)
920{
cd0d2db8
JA
921 guint width = gtk_widget_get_allocated_width(w);
922 guint height = gtk_widget_get_allocated_height(w);
1252d8f2
JA
923 struct graph *g = data;
924
cd0d2db8
JA
925 graph_set_size(g, width, height);
926 graph_set_size(g, width, height);
1252d8f2
JA
927 graph_set_position(g, 0, 0);
928 return TRUE;
929}
930
931static void gfio_show_latency_buckets(struct gfio_client *gc, GtkWidget *vbox,
932 struct thread_stat *ts)
933{
fea76a0c
VF
934 double io_u_lat[FIO_IO_U_LAT_N_NR + FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR];
935 const char *ranges[] = { "2ns", "4ns", "10ns", "20ns", "50ns", "100ns",
936 "250ns", "500ns", "750ns", "1000ns", "2us",
937 "4us", "10us", "20us", "50us", "100us",
2b27ad33
RE
938 "250us", "500us", "750us", "1ms", "2ms",
939 "4ms", "10ms", "20ms", "50ms", "100ms",
940 "250ms", "500ms", "750ms", "1s", "2s", ">= 2s" };
1252d8f2
JA
941 int start, end, i;
942 const int total = FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR;
943 GtkWidget *frame, *tree_view, *hbox, *completion_vbox, *drawing_area;
944 struct gui_entry *ge = gc->ge;
945
fea76a0c
VF
946 stat_calc_lat_n(ts, io_u_lat);
947 stat_calc_lat_u(ts, &io_u_lat[FIO_IO_U_LAT_N_NR]);
948 stat_calc_lat_m(ts, &io_u_lat[FIO_IO_U_LAT_N_NR + FIO_IO_U_LAT_U_NR]);
1252d8f2
JA
949
950 /*
951 * Found out which first bucket has entries, and which last bucket
952 */
953 start = end = -1U;
954 for (i = 0; i < total; i++) {
955 if (io_u_lat[i] == 0.00)
956 continue;
957
958 if (start == -1U)
959 start = i;
960 end = i;
961 }
962
963 /*
964 * No entries...
965 */
966 if (start == -1U)
967 return;
3c3ed070 968
1252d8f2 969 tree_view = gfio_output_lat_buckets(&io_u_lat[start], &ranges[start], end - start + 1);
2b27ad33 970 ge->lat_bucket_graph = setup_lat_bucket_graph("Latency buckets", &io_u_lat[start], &ranges[start], end - start + 1, 700.0, 300.0);
1252d8f2
JA
971
972 frame = gtk_frame_new("Latency buckets");
973 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
974
975 completion_vbox = gtk_vbox_new(FALSE, 3);
976 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
977 hbox = gtk_hbox_new(FALSE, 3);
978 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
979
980 drawing_area = gtk_drawing_area_new();
981 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
982 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &gfio_color_white);
983 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
b6ab6a31 984 g_signal_connect(G_OBJECT(drawing_area), GFIO_DRAW_EVENT, G_CALLBACK(on_expose_lat_drawing_area), ge->lat_bucket_graph);
3c3ed070 985 g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->lat_bucket_graph);
1252d8f2 986
b4d669e6 987 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, TRUE, 3);
1252d8f2
JA
988}
989
fea76a0c
VF
990static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long long min,
991 unsigned long long max, double mean, double dev)
1252d8f2 992{
fea76a0c 993 const char *base = "(nsec)";
1252d8f2
JA
994 GtkWidget *hbox, *label, *frame;
995 char *minp, *maxp;
996 char tmp[64];
997
fea76a0c 998 if (nsec_to_msec(&min, &max, &mean, &dev))
1252d8f2 999 base = "(msec)";
fea76a0c
VF
1000 else if (nsec_to_usec(&min, &max, &mean, &dev))
1001 base = "(usec)";
1252d8f2 1002
d694a6a7
RE
1003 minp = num2str(min, 6, 1, 0, N2S_NONE);
1004 maxp = num2str(max, 6, 1, 0, N2S_NONE);
1252d8f2
JA
1005
1006 sprintf(tmp, "%s %s", name, base);
1007 frame = gtk_frame_new(tmp);
1008 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1009
1010 hbox = gtk_hbox_new(FALSE, 3);
1011 gtk_container_add(GTK_CONTAINER(frame), hbox);
1012
1013 label = new_info_label_in_frame(hbox, "Minimum");
1014 gtk_label_set_text(GTK_LABEL(label), minp);
1015 label = new_info_label_in_frame(hbox, "Maximum");
1016 gtk_label_set_text(GTK_LABEL(label), maxp);
1017 label = new_info_label_in_frame(hbox, "Average");
1018 sprintf(tmp, "%5.02f", mean);
1019 gtk_label_set_text(GTK_LABEL(label), tmp);
1020 label = new_info_label_in_frame(hbox, "Standard deviation");
1021 sprintf(tmp, "%5.02f", dev);
1022 gtk_label_set_text(GTK_LABEL(label), tmp);
1023
1024 free(minp);
1025 free(maxp);
1026}
1027
fea76a0c 1028static GtkWidget *gfio_output_clat_percentiles(unsigned long long *ovals,
1252d8f2
JA
1029 fio_fp64_t *plist,
1030 unsigned int len,
1031 const char *base,
1032 unsigned int scale)
1033{
1034 GType types[FIO_IO_U_LIST_MAX_LEN];
1035 GtkWidget *tree_view;
1036 GtkTreeSelection *selection;
1037 GtkListStore *model;
1038 GtkTreeIter iter;
fea76a0c 1039 int i, j;
1252d8f2
JA
1040
1041 for (i = 0; i < len; i++)
fea76a0c 1042 types[i] = G_TYPE_ULONG;
1252d8f2
JA
1043
1044 model = gtk_list_store_newv(len, types);
1045
1046 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
1047 gtk_widget_set_can_focus(tree_view, FALSE);
1048
1049 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
1050 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
1051
1052 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
1053 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
1054
1055 for (i = 0; i < len; i++) {
1056 char fbuf[8];
1057
1058 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
1059 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
1060 }
1061
1062 gtk_list_store_append(model, &iter);
1063
1064 for (i = 0; i < len; i++) {
fea76a0c 1065 for (j = 0; j < scale; j++)
1252d8f2 1066 ovals[i] = (ovals[i] + 999) / 1000;
fea76a0c 1067 gtk_list_store_set(model, &iter, i, (unsigned long) ovals[i], -1);
1252d8f2
JA
1068 }
1069
1070 return tree_view;
1071}
1072
fea76a0c 1073static struct graph *setup_clat_graph(char *title, unsigned long long *ovals,
1252d8f2
JA
1074 fio_fp64_t *plist,
1075 unsigned int len,
1076 double xdim, double ydim)
1077{
1078 struct graph *g;
1079 int i;
1080
1081 g = graph_new(xdim, ydim, gfio_graph_font);
1082 graph_title(g, title);
1083 graph_x_title(g, "Percentile");
1084 graph_y_title(g, "Time");
1085
1086 for (i = 0; i < len; i++) {
8dfd6071 1087 graph_label_t l;
1252d8f2
JA
1088 char fbuf[8];
1089
1090 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
8dfd6071
JA
1091 l = graph_add_label(g, fbuf);
1092 graph_add_data(g, l, (double) ovals[i]);
1252d8f2
JA
1093 }
1094
1095 return g;
1096}
1097
1098static void gfio_show_clat_percentiles(struct gfio_client *gc,
1099 GtkWidget *vbox, struct thread_stat *ts,
1100 int ddir)
1101{
6cc0e5aa 1102 uint64_t *io_u_plat = ts->io_u_plat[ddir];
d7bb1620 1103 unsigned long long nr = ts->clat_stat[ddir].samples;
1252d8f2 1104 fio_fp64_t *plist = ts->percentile_list;
fea76a0c
VF
1105 unsigned int len, scale_down;
1106 unsigned long long *ovals, minv, maxv;
1252d8f2
JA
1107 const char *base;
1108 GtkWidget *tree_view, *frame, *hbox, *drawing_area, *completion_vbox;
1109 struct gui_entry *ge = gc->ge;
1110 char tmp[64];
1111
1112 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
1113 if (!len)
1114 goto out;
1115
1116 /*
fea76a0c
VF
1117 * We default to nsecs, but if the value range is such that we
1118 * should scale down to usecs or msecs, do that.
1252d8f2 1119 */
fea76a0c
VF
1120 if (minv > 2000000 && maxv > 99999999ULL) {
1121 scale_down = 2;
1252d8f2 1122 base = "msec";
fea76a0c
VF
1123 } else if (minv > 2000 && maxv > 99999) {
1124 scale_down = 1;
1252d8f2 1125 base = "usec";
fea76a0c
VF
1126 } else {
1127 scale_down = 0;
1128 base = "nsec";
1129 }
1252d8f2 1130
b599759b
JA
1131 if (ts->clat_percentiles)
1132 sprintf(tmp, "Completion percentiles (%s)", base);
1133 else
1134 sprintf(tmp, "Latency percentiles (%s)", base);
1135
1252d8f2
JA
1136 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
1137 ge->clat_graph = setup_clat_graph(tmp, ovals, plist, len, 700.0, 300.0);
1138
1139 frame = gtk_frame_new(tmp);
1140 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1141
1142 completion_vbox = gtk_vbox_new(FALSE, 3);
1143 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
1144 hbox = gtk_hbox_new(FALSE, 3);
1145 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
1146 drawing_area = gtk_drawing_area_new();
1147 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
1148 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &gfio_color_white);
1149 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
b6ab6a31 1150 g_signal_connect(G_OBJECT(drawing_area), GFIO_DRAW_EVENT, G_CALLBACK(on_expose_lat_drawing_area), ge->clat_graph);
1252d8f2
JA
1151 g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->clat_graph);
1152
bf0f6cc7 1153 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, TRUE, 3);
1252d8f2
JA
1154out:
1155 if (ovals)
1156 free(ovals);
1157}
1158
1159#define GFIO_CLAT 1
1160#define GFIO_SLAT 2
1161#define GFIO_LAT 4
1162
1163static void gfio_show_ddir_status(struct gfio_client *gc, GtkWidget *mbox,
1164 struct group_run_stats *rs,
1165 struct thread_stat *ts, int ddir)
1166{
b78ca651 1167 const char *ddir_label[3] = { "Read", "Write", "Trim" };
1252d8f2 1168 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
fea76a0c
VF
1169 unsigned long long min[3], max[3];
1170 unsigned long runt;
1252d8f2
JA
1171 unsigned long long bw, iops;
1172 unsigned int flags = 0;
1173 double mean[3], dev[3];
d694a6a7
RE
1174 char *io_p, *io_palt, *bw_p, *bw_palt, *iops_p;
1175 char tmp[128];
1252d8f2
JA
1176 int i2p;
1177
1178 if (!ts->runtime[ddir])
1179 return;
1180
1181 i2p = is_power_of_2(rs->kb_base);
1182 runt = ts->runtime[ddir];
1183
1184 bw = (1000 * ts->io_bytes[ddir]) / runt;
1252d8f2
JA
1185
1186 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
e883cb35 1187 iops_p = num2str(iops, ts->sig_figs, 1, 0, N2S_PERSEC);
1252d8f2
JA
1188
1189 box = gtk_hbox_new(FALSE, 3);
1190 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
1191
1192 frame = gtk_frame_new(ddir_label[ddir]);
f614437e 1193 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 5);
1252d8f2
JA
1194
1195 main_vbox = gtk_vbox_new(FALSE, 3);
1196 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
1197
1198 box = gtk_hbox_new(FALSE, 3);
1199 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
1200
1201 label = new_info_label_in_frame(box, "IO");
e883cb35
JF
1202 io_p = num2str(ts->io_bytes[ddir], ts->sig_figs, 1, i2p, N2S_BYTE);
1203 io_palt = num2str(ts->io_bytes[ddir], ts->sig_figs, 1, !i2p, N2S_BYTE);
d694a6a7
RE
1204 snprintf(tmp, sizeof(tmp), "%s (%s)", io_p, io_palt);
1205 gtk_label_set_text(GTK_LABEL(label), tmp);
1206
1252d8f2 1207 label = new_info_label_in_frame(box, "Bandwidth");
e883cb35
JF
1208 bw_p = num2str(bw, ts->sig_figs, 1, i2p, ts->unit_base);
1209 bw_palt = num2str(bw, ts->sig_figs, 1, !i2p, ts->unit_base);
d694a6a7
RE
1210 snprintf(tmp, sizeof(tmp), "%s (%s)", bw_p, bw_palt);
1211 gtk_label_set_text(GTK_LABEL(label), tmp);
1212
1252d8f2
JA
1213 label = new_info_label_in_frame(box, "IOPS");
1214 gtk_label_set_text(GTK_LABEL(label), iops_p);
1215 label = new_info_label_in_frame(box, "Runtime (msec)");
1216 label_set_int_value(label, ts->runtime[ddir]);
1217
1218 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
1219 double p_of_agg = 100.0;
d694a6a7 1220 const char *bw_str = "KiB/s";
1252d8f2
JA
1221 char tmp[32];
1222
1223 if (rs->agg[ddir]) {
1224 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
1225 if (p_of_agg > 100.0)
1226 p_of_agg = 100.0;
1227 }
1228
d694a6a7
RE
1229 if (mean[0] > 1073741824.9) {
1230 min[0] /= 1048576.0;
1231 max[0] /= 1048576.0;
1232 mean[0] /= 1048576.0;
1233 dev[0] /= 1048576.0;
1234 bw_str = "GiB/s";
1252d8f2
JA
1235 }
1236
d694a6a7
RE
1237 if (mean[0] > 1047575.9) {
1238 min[0] /= 1024.0;
1239 max[0] /= 1024.0;
1240 mean[0] /= 1024.0;
1241 dev[0] /= 1024.0;
1242 bw_str = "MiB/s";
1243 }
1252d8f2
JA
1244 sprintf(tmp, "Bandwidth (%s)", bw_str);
1245 frame = gtk_frame_new(tmp);
1246 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
1247
1248 box = gtk_hbox_new(FALSE, 3);
1249 gtk_container_add(GTK_CONTAINER(frame), box);
1250
1251 label = new_info_label_in_frame(box, "Minimum");
1252 label_set_int_value(label, min[0]);
1253 label = new_info_label_in_frame(box, "Maximum");
1254 label_set_int_value(label, max[0]);
1255 label = new_info_label_in_frame(box, "Percentage of jobs");
1256 sprintf(tmp, "%3.2f%%", p_of_agg);
1257 gtk_label_set_text(GTK_LABEL(label), tmp);
1258 label = new_info_label_in_frame(box, "Average");
1259 sprintf(tmp, "%5.02f", mean[0]);
1260 gtk_label_set_text(GTK_LABEL(label), tmp);
1261 label = new_info_label_in_frame(box, "Standard deviation");
1262 sprintf(tmp, "%5.02f", dev[0]);
1263 gtk_label_set_text(GTK_LABEL(label), tmp);
1264 }
1265
1266 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
1267 flags |= GFIO_SLAT;
1268 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
1269 flags |= GFIO_CLAT;
1270 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
1271 flags |= GFIO_LAT;
1272
1273 if (flags) {
1274 frame = gtk_frame_new("Latency");
1275 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
1276
1277 vbox = gtk_vbox_new(FALSE, 3);
1278 gtk_container_add(GTK_CONTAINER(frame), vbox);
1279
1280 if (flags & GFIO_SLAT)
1281 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
1282 if (flags & GFIO_CLAT)
1283 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
1284 if (flags & GFIO_LAT)
1285 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
1286 }
1287
1288 if (ts->clat_percentiles)
1289 gfio_show_clat_percentiles(gc, main_vbox, ts, ddir);
1290
1291 free(io_p);
1292 free(bw_p);
d694a6a7
RE
1293 free(io_palt);
1294 free(bw_palt);
1252d8f2
JA
1295 free(iops_p);
1296}
1297
1298static void __gfio_display_end_results(GtkWidget *win, struct gfio_client *gc,
1299 struct thread_stat *ts,
1300 struct group_run_stats *rs)
1301{
1302 GtkWidget *box, *vbox, *entry, *scroll;
b78ca651 1303 int i;
1252d8f2
JA
1304
1305 scroll = gtk_scrolled_window_new(NULL, NULL);
1306 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1307 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1308
1309 vbox = gtk_vbox_new(FALSE, 3);
1310
1311 box = gtk_hbox_new(FALSE, 0);
1312 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1313
1314 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1315
1316 gtk_notebook_append_page(GTK_NOTEBOOK(win), scroll, gtk_label_new(ts->name));
1317
1318 entry = new_info_entry_in_frame(box, "Name");
1319 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1320 if (strlen(ts->description)) {
1321 entry = new_info_entry_in_frame(box, "Description");
1322 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1323 }
1324 entry = new_info_entry_in_frame(box, "Group ID");
1325 entry_set_int_value(entry, ts->groupid);
1326 entry = new_info_entry_in_frame(box, "Jobs");
1327 entry_set_int_value(entry, ts->members);
1328 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
1329 entry_set_int_value(entry, ts->error);
1330 entry = new_info_entry_in_frame(box, "PID");
1331 entry_set_int_value(entry, ts->pid);
1332
b78ca651
JA
1333 for (i = 0; i < DDIR_RWDIR_CNT; i++) {
1334 if (ts->io_bytes[i])
1335 gfio_show_ddir_status(gc, vbox, rs, ts, i);
1336 }
1252d8f2
JA
1337
1338 gfio_show_latency_buckets(gc, vbox, ts);
1339 gfio_show_cpu_usage(vbox, ts);
1340 gfio_show_io_depths(vbox, ts);
1341}
1342
1343void gfio_display_end_results(struct gfio_client *gc)
1344{
1345 struct gui_entry *ge = gc->ge;
1346 GtkWidget *res_notebook;
1347 int i;
1348
1349 res_notebook = get_results_window(ge);
1350
1351 for (i = 0; i < gc->nr_results; i++) {
1352 struct end_results *e = &gc->results[i];
1353
1354 __gfio_display_end_results(res_notebook, gc, &e->ts, &e->gs);
1355 }
1356
1357 if (gfio_disk_util_show(gc))
1358 gtk_widget_show_all(ge->results_window);
1359}
1360
1361static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
1362 struct group_run_stats *rs)
1363{
1364 struct gfio_client *gc = client->client_data;
1365 struct gui_entry *ge = gc->ge;
1366
1367 gfio_add_end_results(gc, ts, rs);
1368
1369 gdk_threads_enter();
1370 if (ge->results_window)
1371 __gfio_display_end_results(ge->results_notebook, gc, ts, rs);
1372 else
1373 gfio_display_end_results(gc);
1374 gdk_threads_leave();
1375}
1376
1377static void gfio_client_removed(struct fio_client *client)
1378{
1379 struct gfio_client *gc = client->client_data;
1380
1381 assert(gc->client == client);
1382 fio_put_client(gc->client);
1383 gc->client = NULL;
1384}
1385
1386struct client_ops gfio_client_ops = {
1387 .text = gfio_text_op,
1388 .disk_util = gfio_disk_util_op,
1389 .thread_status = gfio_thread_status_op,
1390 .group_stats = gfio_group_stats_op,
1391 .jobs_eta = gfio_update_client_eta,
1392 .eta = gfio_update_all_eta,
1393 .probe = gfio_probe_op,
1394 .quit = gfio_quit_op,
1395 .add_job = gfio_add_job_op,
40c60516 1396 .update_job = gfio_update_job_op,
1252d8f2
JA
1397 .timed_out = gfio_client_timed_out,
1398 .stop = gfio_client_stop,
1399 .start = gfio_client_start,
1400 .job_start = gfio_client_job_start,
1252d8f2
JA
1401 .removed = gfio_client_removed,
1402 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
1403 .stay_connected = 1,
1404 .client_type = FIO_CLIENT_TYPE_GUI,
1405};