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