Merge branch 'master' of https://github.com/celestinechen/fio
[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};
59f94d26 51static gint results_nmenu_items = FIO_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
016869be 295 sum_thread_stats(&client_ts, &p->ts);
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
36833fb0 321 snprintf(message, sizeof(message), "%s", status_message);
1252d8f2
JA
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
5b215853 333 snprintf(message, sizeof(message), "%s", status_message);
1252d8f2
JA
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 555static struct thread_options *gfio_client_add_job(struct gfio_client *gc,
7f812e87 556 struct thread_options_pack *top, size_t top_sz)
cf3d8241
JA
557{
558 struct gfio_client_options *gco;
559
560 gco = calloc(1, sizeof(*gco));
7f812e87
LG
561 if (convert_thread_options_to_cpu(&gco->o, top, top_sz)) {
562 dprint(FD_NET, "client: failed parsing add_job command\n");
563 return NULL;
564 }
753e9e64 565 INIT_FLIST_HEAD(&gco->list);
cf3d8241 566 flist_add_tail(&gco->list, &gc->o_list);
753e9e64 567 gc->o_list_nr = 1;
cf3d8241
JA
568 return &gco->o;
569}
570
1252d8f2
JA
571static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
572{
573 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
574 struct gfio_client *gc = client->client_data;
1252d8f2 575 struct gui_entry *ge = gc->ge;
cf3d8241 576 struct thread_options *o;
1252d8f2
JA
577 char *c1, *c2, *c3, *c4;
578 char tmp[80];
d694a6a7 579 int i2p;
1252d8f2
JA
580
581 p->thread_number = le32_to_cpu(p->thread_number);
582 p->groupid = le32_to_cpu(p->groupid);
7f812e87
LG
583 o = gfio_client_add_job(gc, &p->top,
584 cmd->pdu_len - offsetof(struct cmd_add_job_pdu, top));
585 if (o == NULL)
586 return;
1252d8f2
JA
587
588 gdk_threads_enter();
589
f762cef9 590 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(ge->eta.names), (gchar *) o->name);
1252d8f2
JA
591 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
592
593 sprintf(tmp, "%s %s", o->odirect ? "direct" : "buffered", ddir_str(o->td_ddir));
594 multitext_add_entry(&ge->eta.iotype, tmp);
595
d694a6a7 596 i2p = is_power_of_2(o->kb_base);
e883cb35
JF
597 c1 = num2str(o->min_bs[DDIR_READ], o->sig_figs, 1, i2p, N2S_BYTE);
598 c2 = num2str(o->max_bs[DDIR_READ], o->sig_figs, 1, i2p, N2S_BYTE);
599 c3 = num2str(o->min_bs[DDIR_WRITE], o->sig_figs, 1, i2p, N2S_BYTE);
600 c4 = num2str(o->max_bs[DDIR_WRITE], o->sig_figs, 1, i2p, N2S_BYTE);
d694a6a7
RE
601
602 sprintf(tmp, "%s-%s,%s-%s", c1, c2, c3, c4);
1252d8f2
JA
603 free(c1);
604 free(c2);
605 free(c3);
606 free(c4);
607 multitext_add_entry(&ge->eta.bs, tmp);
608
609 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
610
611 sprintf(tmp, "%u", o->iodepth);
612 multitext_add_entry(&ge->eta.iodepth, tmp);
613
614 multitext_set_entry(&ge->eta.iotype, 0);
615 multitext_set_entry(&ge->eta.bs, 0);
616 multitext_set_entry(&ge->eta.ioengine, 0);
617 multitext_set_entry(&ge->eta.iodepth, 0);
618
619 gfio_set_state(ge, GE_STATE_JOB_SENT);
620
621 gdk_threads_leave();
622}
623
40c60516
JA
624static void gfio_update_job_op(struct fio_client *client,
625 struct fio_net_cmd *cmd)
626{
627 uint32_t *pdu_error = (uint32_t *) cmd->payload;
628 struct gfio_client *gc = client->client_data;
629
28db256b
JA
630 gc->update_job_status = le32_to_cpu(*pdu_error);
631 gc->update_job_done = 1;
40c60516
JA
632}
633
1252d8f2
JA
634static void gfio_client_timed_out(struct fio_client *client)
635{
636 struct gfio_client *gc = client->client_data;
637 char buf[256];
638
639 gdk_threads_enter();
640
641 gfio_set_state(gc->ge, GE_STATE_NEW);
642 clear_ge_ui_info(gc->ge);
643
644 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
645 gfio_report_info(gc->ge->ui, "Network timeout", buf);
646
647 gdk_threads_leave();
648}
649
36ed2f9a 650static void gfio_client_stop(struct fio_client *client)
1252d8f2
JA
651{
652 struct gfio_client *gc = client->client_data;
653
654 gdk_threads_enter();
655
656 gfio_set_state(gc->ge, GE_STATE_JOB_DONE);
657
658 if (gc->err_entry)
659 entry_set_int_value(gc->err_entry, client->error);
660
661 gdk_threads_leave();
662}
663
664static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd)
665{
666 struct gfio_client *gc = client->client_data;
667
668 gdk_threads_enter();
669 gfio_set_state(gc->ge, GE_STATE_JOB_STARTED);
670 gdk_threads_leave();
671}
672
673static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd)
674{
675 struct gfio_client *gc = client->client_data;
676
677 gdk_threads_enter();
678 gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING);
679 gdk_threads_leave();
680}
681
1252d8f2
JA
682static void gfio_add_total_depths_tree(GtkListStore *model,
683 struct thread_stat *ts, unsigned int len)
684{
685 double io_u_dist[FIO_IO_U_MAP_NR];
686 GtkTreeIter iter;
687 /* Bits 1-6, and 8 */
688 const int add_mask = 0x17e;
689 int i, j;
690
d79db122 691 stat_calc_dist(ts->io_u_map, ddir_rw_sum(ts->total_io_u), io_u_dist);
1252d8f2
JA
692
693 gtk_list_store_append(model, &iter);
694
695 gtk_list_store_set(model, &iter, 0, "Total", -1);
696
697 for (i = 1, j = 0; i < len; i++) {
698 char fbuf[32];
699
700 if (!(add_mask & (1UL << (i - 1))))
701 sprintf(fbuf, "0.0%%");
702 else {
703 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
704 j++;
705 }
706
707 gtk_list_store_set(model, &iter, i, fbuf, -1);
708 }
709
710}
711
712static void gfio_add_end_results(struct gfio_client *gc, struct thread_stat *ts,
713 struct group_run_stats *rs)
714{
715 unsigned int nr = gc->nr_results;
716
717 gc->results = realloc(gc->results, (nr + 1) * sizeof(struct end_results));
718 memcpy(&gc->results[nr].ts, ts, sizeof(*ts));
719 memcpy(&gc->results[nr].gs, rs, sizeof(*rs));
720 gc->nr_results++;
721}
722
723static void gfio_add_sc_depths_tree(GtkListStore *model,
724 struct thread_stat *ts, unsigned int len,
725 int submit)
726{
727 double io_u_dist[FIO_IO_U_MAP_NR];
728 GtkTreeIter iter;
729 /* Bits 0, and 3-8 */
730 const int add_mask = 0x1f9;
731 int i, j;
732
733 if (submit)
734 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
735 else
736 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
737
738 gtk_list_store_append(model, &iter);
739
740 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
741
742 for (i = 1, j = 0; i < len; i++) {
743 char fbuf[32];
744
745 if (!(add_mask & (1UL << (i - 1))))
746 sprintf(fbuf, "0.0%%");
747 else {
748 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
749 j++;
750 }
751
752 gtk_list_store_set(model, &iter, i, fbuf, -1);
753 }
754
755}
756
757static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
758{
759 GtkWidget *frame, *box, *tree_view = NULL;
760 GtkTreeSelection *selection;
761 GtkListStore *model;
1252d8f2
JA
762 int i;
763 const char *labels[] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
59f94d26 764 const int nr_labels = FIO_ARRAY_SIZE(labels);
1fae4855 765 GType types[nr_labels];
1252d8f2
JA
766
767 frame = gtk_frame_new("IO depths");
768 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
769
770 box = gtk_hbox_new(FALSE, 3);
771 gtk_container_add(GTK_CONTAINER(frame), box);
772
773 for (i = 0; i < nr_labels; i++)
774 types[i] = G_TYPE_STRING;
775
776 model = gtk_list_store_newv(nr_labels, types);
777
778 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
779 gtk_widget_set_can_focus(tree_view, FALSE);
780
781 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
782 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
783
784 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
785 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
786
787 for (i = 0; i < nr_labels; i++)
788 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
789
790 gfio_add_total_depths_tree(model, ts, nr_labels);
791 gfio_add_sc_depths_tree(model, ts, nr_labels, 1);
792 gfio_add_sc_depths_tree(model, ts, nr_labels, 0);
793
f5e653e6 794 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, TRUE, 3);
1252d8f2
JA
795}
796
797static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
798{
799 GtkWidget *box, *frame, *entry;
800 double usr_cpu, sys_cpu;
801 unsigned long runtime;
802 char tmp[32];
803
804 runtime = ts->total_run_time;
805 if (runtime) {
806 double runt = (double) runtime;
807
808 usr_cpu = (double) ts->usr_time * 100 / runt;
809 sys_cpu = (double) ts->sys_time * 100 / runt;
810 } else {
811 usr_cpu = 0;
812 sys_cpu = 0;
813 }
814
815 frame = gtk_frame_new("OS resources");
816 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
817
818 box = gtk_hbox_new(FALSE, 3);
819 gtk_container_add(GTK_CONTAINER(frame), box);
820
821 entry = new_info_entry_in_frame(box, "User CPU");
822 sprintf(tmp, "%3.2f%%", usr_cpu);
823 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
824 entry = new_info_entry_in_frame(box, "System CPU");
825 sprintf(tmp, "%3.2f%%", sys_cpu);
826 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
827 entry = new_info_entry_in_frame(box, "Context switches");
828 entry_set_int_value(entry, ts->ctx);
829 entry = new_info_entry_in_frame(box, "Major faults");
830 entry_set_int_value(entry, ts->majf);
831 entry = new_info_entry_in_frame(box, "Minor faults");
832 entry_set_int_value(entry, ts->minf);
833}
834
835static GtkWidget *gfio_output_lat_buckets(double *lat, const char **labels,
836 int num)
837{
838 GtkWidget *tree_view;
839 GtkTreeSelection *selection;
840 GtkListStore *model;
841 GtkTreeIter iter;
842 GType *types;
843 int i;
844
845 types = malloc(num * sizeof(GType));
846
847 for (i = 0; i < num; i++)
848 types[i] = G_TYPE_STRING;
849
850 model = gtk_list_store_newv(num, types);
851 free(types);
852 types = NULL;
853
854 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
855 gtk_widget_set_can_focus(tree_view, FALSE);
856
857 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
858 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
859
860 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
861 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
862
863 for (i = 0; i < num; i++)
864 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
865
866 gtk_list_store_append(model, &iter);
867
868 for (i = 0; i < num; i++) {
869 char fbuf[32];
870
871 if (lat[i] <= 0.0)
872 sprintf(fbuf, "0.00");
873 else
874 sprintf(fbuf, "%3.2f%%", lat[i]);
875
876 gtk_list_store_set(model, &iter, i, fbuf, -1);
877 }
878
879 return tree_view;
880}
881
882static struct graph *setup_lat_bucket_graph(const char *title, double *lat,
883 const char **labels,
884 unsigned int len,
885 double xdim, double ydim)
886{
887 struct graph *g;
888 int i;
889
890 g = graph_new(xdim, ydim, gfio_graph_font);
891 graph_title(g, title);
892 graph_x_title(g, "Buckets");
893 graph_y_title(g, "Percent");
894
895 for (i = 0; i < len; i++) {
8dfd6071
JA
896 graph_label_t l;
897
898 l = graph_add_label(g, labels[i]);
899 graph_add_data(g, l, lat[i]);
1252d8f2
JA
900 }
901
902 return g;
903}
904
905static int on_expose_lat_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
906{
907 struct graph *g = p;
908 cairo_t *cr;
909
cd0d2db8 910 cr = gdk_cairo_create(gtk_widget_get_window(w));
1252d8f2
JA
911#if 0
912 if (graph_has_tooltips(g)) {
913 g_object_set(w, "has-tooltip", TRUE, NULL);
914 g_signal_connect(w, "query-tooltip", G_CALLBACK(clat_graph_tooltip), g);
915 }
916#endif
917 cairo_set_source_rgb(cr, 0, 0, 0);
918 bar_graph_draw(g, cr);
919 cairo_destroy(cr);
920
921 return FALSE;
922}
923
924static gint on_config_lat_drawing_area(GtkWidget *w, GdkEventConfigure *event,
925 gpointer data)
926{
cd0d2db8
JA
927 guint width = gtk_widget_get_allocated_width(w);
928 guint height = gtk_widget_get_allocated_height(w);
1252d8f2
JA
929 struct graph *g = data;
930
cd0d2db8
JA
931 graph_set_size(g, width, height);
932 graph_set_size(g, width, height);
1252d8f2
JA
933 graph_set_position(g, 0, 0);
934 return TRUE;
935}
936
937static void gfio_show_latency_buckets(struct gfio_client *gc, GtkWidget *vbox,
938 struct thread_stat *ts)
939{
fea76a0c
VF
940 double io_u_lat[FIO_IO_U_LAT_N_NR + FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR];
941 const char *ranges[] = { "2ns", "4ns", "10ns", "20ns", "50ns", "100ns",
942 "250ns", "500ns", "750ns", "1000ns", "2us",
943 "4us", "10us", "20us", "50us", "100us",
2b27ad33
RE
944 "250us", "500us", "750us", "1ms", "2ms",
945 "4ms", "10ms", "20ms", "50ms", "100ms",
946 "250ms", "500ms", "750ms", "1s", "2s", ">= 2s" };
1252d8f2
JA
947 int start, end, i;
948 const int total = FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR;
949 GtkWidget *frame, *tree_view, *hbox, *completion_vbox, *drawing_area;
950 struct gui_entry *ge = gc->ge;
951
fea76a0c
VF
952 stat_calc_lat_n(ts, io_u_lat);
953 stat_calc_lat_u(ts, &io_u_lat[FIO_IO_U_LAT_N_NR]);
954 stat_calc_lat_m(ts, &io_u_lat[FIO_IO_U_LAT_N_NR + FIO_IO_U_LAT_U_NR]);
1252d8f2
JA
955
956 /*
957 * Found out which first bucket has entries, and which last bucket
958 */
959 start = end = -1U;
960 for (i = 0; i < total; i++) {
961 if (io_u_lat[i] == 0.00)
962 continue;
963
964 if (start == -1U)
965 start = i;
966 end = i;
967 }
968
969 /*
970 * No entries...
971 */
972 if (start == -1U)
973 return;
3c3ed070 974
1252d8f2 975 tree_view = gfio_output_lat_buckets(&io_u_lat[start], &ranges[start], end - start + 1);
2b27ad33 976 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
977
978 frame = gtk_frame_new("Latency buckets");
979 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
980
981 completion_vbox = gtk_vbox_new(FALSE, 3);
982 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
983 hbox = gtk_hbox_new(FALSE, 3);
984 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
985
986 drawing_area = gtk_drawing_area_new();
987 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
988 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &gfio_color_white);
989 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
b6ab6a31 990 g_signal_connect(G_OBJECT(drawing_area), GFIO_DRAW_EVENT, G_CALLBACK(on_expose_lat_drawing_area), ge->lat_bucket_graph);
3c3ed070 991 g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->lat_bucket_graph);
1252d8f2 992
b4d669e6 993 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, TRUE, 3);
1252d8f2
JA
994}
995
fea76a0c
VF
996static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long long min,
997 unsigned long long max, double mean, double dev)
1252d8f2 998{
fea76a0c 999 const char *base = "(nsec)";
1252d8f2
JA
1000 GtkWidget *hbox, *label, *frame;
1001 char *minp, *maxp;
1002 char tmp[64];
1003
fea76a0c 1004 if (nsec_to_msec(&min, &max, &mean, &dev))
1252d8f2 1005 base = "(msec)";
fea76a0c
VF
1006 else if (nsec_to_usec(&min, &max, &mean, &dev))
1007 base = "(usec)";
1252d8f2 1008
d694a6a7
RE
1009 minp = num2str(min, 6, 1, 0, N2S_NONE);
1010 maxp = num2str(max, 6, 1, 0, N2S_NONE);
1252d8f2
JA
1011
1012 sprintf(tmp, "%s %s", name, base);
1013 frame = gtk_frame_new(tmp);
1014 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1015
1016 hbox = gtk_hbox_new(FALSE, 3);
1017 gtk_container_add(GTK_CONTAINER(frame), hbox);
1018
1019 label = new_info_label_in_frame(hbox, "Minimum");
1020 gtk_label_set_text(GTK_LABEL(label), minp);
1021 label = new_info_label_in_frame(hbox, "Maximum");
1022 gtk_label_set_text(GTK_LABEL(label), maxp);
1023 label = new_info_label_in_frame(hbox, "Average");
1024 sprintf(tmp, "%5.02f", mean);
1025 gtk_label_set_text(GTK_LABEL(label), tmp);
1026 label = new_info_label_in_frame(hbox, "Standard deviation");
1027 sprintf(tmp, "%5.02f", dev);
1028 gtk_label_set_text(GTK_LABEL(label), tmp);
1029
1030 free(minp);
1031 free(maxp);
1032}
1033
fea76a0c 1034static GtkWidget *gfio_output_clat_percentiles(unsigned long long *ovals,
1252d8f2
JA
1035 fio_fp64_t *plist,
1036 unsigned int len,
1037 const char *base,
1038 unsigned int scale)
1039{
1040 GType types[FIO_IO_U_LIST_MAX_LEN];
1041 GtkWidget *tree_view;
1042 GtkTreeSelection *selection;
1043 GtkListStore *model;
1044 GtkTreeIter iter;
fea76a0c 1045 int i, j;
1252d8f2
JA
1046
1047 for (i = 0; i < len; i++)
fea76a0c 1048 types[i] = G_TYPE_ULONG;
1252d8f2
JA
1049
1050 model = gtk_list_store_newv(len, types);
1051
1052 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
1053 gtk_widget_set_can_focus(tree_view, FALSE);
1054
1055 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
1056 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
1057
1058 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
1059 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
1060
1061 for (i = 0; i < len; i++) {
1062 char fbuf[8];
1063
1064 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
1065 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
1066 }
1067
1068 gtk_list_store_append(model, &iter);
1069
1070 for (i = 0; i < len; i++) {
fea76a0c 1071 for (j = 0; j < scale; j++)
1252d8f2 1072 ovals[i] = (ovals[i] + 999) / 1000;
fea76a0c 1073 gtk_list_store_set(model, &iter, i, (unsigned long) ovals[i], -1);
1252d8f2
JA
1074 }
1075
1076 return tree_view;
1077}
1078
fea76a0c 1079static struct graph *setup_clat_graph(char *title, unsigned long long *ovals,
1252d8f2
JA
1080 fio_fp64_t *plist,
1081 unsigned int len,
1082 double xdim, double ydim)
1083{
1084 struct graph *g;
1085 int i;
1086
1087 g = graph_new(xdim, ydim, gfio_graph_font);
1088 graph_title(g, title);
1089 graph_x_title(g, "Percentile");
1090 graph_y_title(g, "Time");
1091
1092 for (i = 0; i < len; i++) {
8dfd6071 1093 graph_label_t l;
1252d8f2
JA
1094 char fbuf[8];
1095
1096 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
8dfd6071
JA
1097 l = graph_add_label(g, fbuf);
1098 graph_add_data(g, l, (double) ovals[i]);
1252d8f2
JA
1099 }
1100
1101 return g;
1102}
1103
1104static void gfio_show_clat_percentiles(struct gfio_client *gc,
1105 GtkWidget *vbox, struct thread_stat *ts,
3e306f32
VF
1106 int ddir, uint64_t *io_u_plat,
1107 unsigned long long nr, const char *type)
1252d8f2 1108{
1252d8f2 1109 fio_fp64_t *plist = ts->percentile_list;
fea76a0c
VF
1110 unsigned int len, scale_down;
1111 unsigned long long *ovals, minv, maxv;
1252d8f2
JA
1112 const char *base;
1113 GtkWidget *tree_view, *frame, *hbox, *drawing_area, *completion_vbox;
1114 struct gui_entry *ge = gc->ge;
1115 char tmp[64];
1116
1117 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
1118 if (!len)
1119 goto out;
1120
1121 /*
fea76a0c
VF
1122 * We default to nsecs, but if the value range is such that we
1123 * should scale down to usecs or msecs, do that.
1252d8f2 1124 */
fea76a0c
VF
1125 if (minv > 2000000 && maxv > 99999999ULL) {
1126 scale_down = 2;
1252d8f2 1127 base = "msec";
fea76a0c
VF
1128 } else if (minv > 2000 && maxv > 99999) {
1129 scale_down = 1;
1252d8f2 1130 base = "usec";
fea76a0c
VF
1131 } else {
1132 scale_down = 0;
1133 base = "nsec";
1134 }
1252d8f2 1135
3e306f32 1136 sprintf(tmp, "%s latency percentiles (%s)", type, base);
b599759b 1137
1252d8f2
JA
1138 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
1139 ge->clat_graph = setup_clat_graph(tmp, ovals, plist, len, 700.0, 300.0);
1140
1141 frame = gtk_frame_new(tmp);
1142 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1143
1144 completion_vbox = gtk_vbox_new(FALSE, 3);
1145 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
1146 hbox = gtk_hbox_new(FALSE, 3);
1147 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
1148 drawing_area = gtk_drawing_area_new();
1149 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
1150 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &gfio_color_white);
1151 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
b6ab6a31 1152 g_signal_connect(G_OBJECT(drawing_area), GFIO_DRAW_EVENT, G_CALLBACK(on_expose_lat_drawing_area), ge->clat_graph);
1252d8f2
JA
1153 g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->clat_graph);
1154
bf0f6cc7 1155 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, TRUE, 3);
1252d8f2
JA
1156out:
1157 if (ovals)
1158 free(ovals);
1159}
1160
1161#define GFIO_CLAT 1
1162#define GFIO_SLAT 2
1163#define GFIO_LAT 4
1164
1165static void gfio_show_ddir_status(struct gfio_client *gc, GtkWidget *mbox,
1166 struct group_run_stats *rs,
1167 struct thread_stat *ts, int ddir)
1168{
b78ca651 1169 const char *ddir_label[3] = { "Read", "Write", "Trim" };
1252d8f2 1170 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
c53048b3 1171 unsigned long long min[3], max[3];
fea76a0c 1172 unsigned long runt;
1252d8f2
JA
1173 unsigned long long bw, iops;
1174 unsigned int flags = 0;
c53048b3 1175 double mean[3], dev[3];
d694a6a7
RE
1176 char *io_p, *io_palt, *bw_p, *bw_palt, *iops_p;
1177 char tmp[128];
1252d8f2
JA
1178 int i2p;
1179
1180 if (!ts->runtime[ddir])
1181 return;
1182
1183 i2p = is_power_of_2(rs->kb_base);
1184 runt = ts->runtime[ddir];
1185
1186 bw = (1000 * ts->io_bytes[ddir]) / runt;
1252d8f2
JA
1187
1188 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
e883cb35 1189 iops_p = num2str(iops, ts->sig_figs, 1, 0, N2S_PERSEC);
1252d8f2
JA
1190
1191 box = gtk_hbox_new(FALSE, 3);
1192 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
1193
1194 frame = gtk_frame_new(ddir_label[ddir]);
f614437e 1195 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 5);
1252d8f2
JA
1196
1197 main_vbox = gtk_vbox_new(FALSE, 3);
1198 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
1199
1200 box = gtk_hbox_new(FALSE, 3);
1201 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
1202
1203 label = new_info_label_in_frame(box, "IO");
e883cb35
JF
1204 io_p = num2str(ts->io_bytes[ddir], ts->sig_figs, 1, i2p, N2S_BYTE);
1205 io_palt = num2str(ts->io_bytes[ddir], ts->sig_figs, 1, !i2p, N2S_BYTE);
d694a6a7
RE
1206 snprintf(tmp, sizeof(tmp), "%s (%s)", io_p, io_palt);
1207 gtk_label_set_text(GTK_LABEL(label), tmp);
1208
1252d8f2 1209 label = new_info_label_in_frame(box, "Bandwidth");
e883cb35
JF
1210 bw_p = num2str(bw, ts->sig_figs, 1, i2p, ts->unit_base);
1211 bw_palt = num2str(bw, ts->sig_figs, 1, !i2p, ts->unit_base);
d694a6a7
RE
1212 snprintf(tmp, sizeof(tmp), "%s (%s)", bw_p, bw_palt);
1213 gtk_label_set_text(GTK_LABEL(label), tmp);
1214
1252d8f2
JA
1215 label = new_info_label_in_frame(box, "IOPS");
1216 gtk_label_set_text(GTK_LABEL(label), iops_p);
1217 label = new_info_label_in_frame(box, "Runtime (msec)");
1218 label_set_int_value(label, ts->runtime[ddir]);
1219
1220 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
1221 double p_of_agg = 100.0;
d694a6a7 1222 const char *bw_str = "KiB/s";
1252d8f2
JA
1223 char tmp[32];
1224
1225 if (rs->agg[ddir]) {
1226 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
1227 if (p_of_agg > 100.0)
1228 p_of_agg = 100.0;
1229 }
1230
d694a6a7
RE
1231 if (mean[0] > 1073741824.9) {
1232 min[0] /= 1048576.0;
1233 max[0] /= 1048576.0;
1234 mean[0] /= 1048576.0;
1235 dev[0] /= 1048576.0;
1236 bw_str = "GiB/s";
1252d8f2
JA
1237 }
1238
d694a6a7
RE
1239 if (mean[0] > 1047575.9) {
1240 min[0] /= 1024.0;
1241 max[0] /= 1024.0;
1242 mean[0] /= 1024.0;
1243 dev[0] /= 1024.0;
1244 bw_str = "MiB/s";
1245 }
1252d8f2
JA
1246 sprintf(tmp, "Bandwidth (%s)", bw_str);
1247 frame = gtk_frame_new(tmp);
1248 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
1249
1250 box = gtk_hbox_new(FALSE, 3);
1251 gtk_container_add(GTK_CONTAINER(frame), box);
1252
1253 label = new_info_label_in_frame(box, "Minimum");
1254 label_set_int_value(label, min[0]);
1255 label = new_info_label_in_frame(box, "Maximum");
1256 label_set_int_value(label, max[0]);
1257 label = new_info_label_in_frame(box, "Percentage of jobs");
1258 sprintf(tmp, "%3.2f%%", p_of_agg);
1259 gtk_label_set_text(GTK_LABEL(label), tmp);
1260 label = new_info_label_in_frame(box, "Average");
1261 sprintf(tmp, "%5.02f", mean[0]);
1262 gtk_label_set_text(GTK_LABEL(label), tmp);
1263 label = new_info_label_in_frame(box, "Standard deviation");
1264 sprintf(tmp, "%5.02f", dev[0]);
1265 gtk_label_set_text(GTK_LABEL(label), tmp);
1266 }
1267
1268 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
1269 flags |= GFIO_SLAT;
1270 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
1271 flags |= GFIO_CLAT;
1272 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
1273 flags |= GFIO_LAT;
1274
1275 if (flags) {
1276 frame = gtk_frame_new("Latency");
1277 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
1278
1279 vbox = gtk_vbox_new(FALSE, 3);
1280 gtk_container_add(GTK_CONTAINER(frame), vbox);
1281
1282 if (flags & GFIO_SLAT)
1283 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
1284 if (flags & GFIO_CLAT)
1285 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
1286 if (flags & GFIO_LAT)
1287 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
1288 }
1289
3e306f32
VF
1290 if (ts->slat_percentiles && flags & GFIO_SLAT)
1291 gfio_show_clat_percentiles(gc, main_vbox, ts, ddir,
1292 ts->io_u_plat[FIO_SLAT][ddir],
1293 ts->slat_stat[ddir].samples,
1294 "Submission");
c53048b3 1295 if (ts->clat_percentiles && flags & GFIO_CLAT)
3e306f32
VF
1296 gfio_show_clat_percentiles(gc, main_vbox, ts, ddir,
1297 ts->io_u_plat[FIO_CLAT][ddir],
1298 ts->clat_stat[ddir].samples,
1299 "Completion");
c53048b3 1300 if (ts->lat_percentiles && flags & GFIO_LAT)
3e306f32
VF
1301 gfio_show_clat_percentiles(gc, main_vbox, ts, ddir,
1302 ts->io_u_plat[FIO_LAT][ddir],
1303 ts->lat_stat[ddir].samples,
1304 "Total");
1252d8f2
JA
1305
1306 free(io_p);
1307 free(bw_p);
d694a6a7
RE
1308 free(io_palt);
1309 free(bw_palt);
1252d8f2
JA
1310 free(iops_p);
1311}
1312
1313static void __gfio_display_end_results(GtkWidget *win, struct gfio_client *gc,
1314 struct thread_stat *ts,
1315 struct group_run_stats *rs)
1316{
1317 GtkWidget *box, *vbox, *entry, *scroll;
b78ca651 1318 int i;
1252d8f2
JA
1319
1320 scroll = gtk_scrolled_window_new(NULL, NULL);
1321 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1322 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1323
1324 vbox = gtk_vbox_new(FALSE, 3);
1325
1326 box = gtk_hbox_new(FALSE, 0);
1327 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1328
1329 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1330
1331 gtk_notebook_append_page(GTK_NOTEBOOK(win), scroll, gtk_label_new(ts->name));
1332
1333 entry = new_info_entry_in_frame(box, "Name");
1334 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1335 if (strlen(ts->description)) {
1336 entry = new_info_entry_in_frame(box, "Description");
1337 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1338 }
1339 entry = new_info_entry_in_frame(box, "Group ID");
1340 entry_set_int_value(entry, ts->groupid);
1341 entry = new_info_entry_in_frame(box, "Jobs");
1342 entry_set_int_value(entry, ts->members);
1343 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
1344 entry_set_int_value(entry, ts->error);
1345 entry = new_info_entry_in_frame(box, "PID");
1346 entry_set_int_value(entry, ts->pid);
1347
b78ca651
JA
1348 for (i = 0; i < DDIR_RWDIR_CNT; i++) {
1349 if (ts->io_bytes[i])
1350 gfio_show_ddir_status(gc, vbox, rs, ts, i);
1351 }
1252d8f2
JA
1352
1353 gfio_show_latency_buckets(gc, vbox, ts);
1354 gfio_show_cpu_usage(vbox, ts);
1355 gfio_show_io_depths(vbox, ts);
1356}
1357
1358void gfio_display_end_results(struct gfio_client *gc)
1359{
1360 struct gui_entry *ge = gc->ge;
1361 GtkWidget *res_notebook;
1362 int i;
1363
1364 res_notebook = get_results_window(ge);
1365
1366 for (i = 0; i < gc->nr_results; i++) {
1367 struct end_results *e = &gc->results[i];
1368
1369 __gfio_display_end_results(res_notebook, gc, &e->ts, &e->gs);
1370 }
1371
1372 if (gfio_disk_util_show(gc))
1373 gtk_widget_show_all(ge->results_window);
1374}
1375
1376static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
1377 struct group_run_stats *rs)
1378{
1379 struct gfio_client *gc = client->client_data;
1380 struct gui_entry *ge = gc->ge;
1381
1382 gfio_add_end_results(gc, ts, rs);
1383
1384 gdk_threads_enter();
1385 if (ge->results_window)
1386 __gfio_display_end_results(ge->results_notebook, gc, ts, rs);
1387 else
1388 gfio_display_end_results(gc);
1389 gdk_threads_leave();
1390}
1391
1392static void gfio_client_removed(struct fio_client *client)
1393{
1394 struct gfio_client *gc = client->client_data;
1395
1396 assert(gc->client == client);
1397 fio_put_client(gc->client);
1398 gc->client = NULL;
1399}
1400
1401struct client_ops gfio_client_ops = {
1402 .text = gfio_text_op,
1403 .disk_util = gfio_disk_util_op,
1404 .thread_status = gfio_thread_status_op,
1405 .group_stats = gfio_group_stats_op,
1406 .jobs_eta = gfio_update_client_eta,
1407 .eta = gfio_update_all_eta,
1408 .probe = gfio_probe_op,
1409 .quit = gfio_quit_op,
1410 .add_job = gfio_add_job_op,
40c60516 1411 .update_job = gfio_update_job_op,
1252d8f2
JA
1412 .timed_out = gfio_client_timed_out,
1413 .stop = gfio_client_stop,
1414 .start = gfio_client_start,
1415 .job_start = gfio_client_job_start,
1252d8f2
JA
1416 .removed = gfio_client_removed,
1417 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
1418 .stay_connected = 1,
1419 .client_type = FIO_CLIENT_TYPE_GUI,
1420};