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