1 /*- 2 * BSD LICENSE 3 * 4 * Copyright (c) Intel Corporation. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * * Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * * Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * * Neither the name of Intel Corporation nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include "spdk/stdinc.h" 35 #include "spdk/jsonrpc.h" 36 #include "spdk/rpc.h" 37 #include "spdk/event.h" 38 #include "spdk/util.h" 39 #include "spdk/env.h" 40 41 #if defined __has_include 42 #if __has_include(<ncurses/panel.h>) 43 #include <ncurses/ncurses.h> 44 #include <ncurses/panel.h> 45 #include <ncurses/menu.h> 46 #else 47 #include <ncurses.h> 48 #include <panel.h> 49 #include <menu.h> 50 #endif 51 #else 52 #include <ncurses.h> 53 #include <panel.h> 54 #include <menu.h> 55 #endif 56 57 #define RPC_MAX_THREADS 1024 58 #define RPC_MAX_POLLERS 1024 59 #define RPC_MAX_CORES 255 60 #define MAX_THREAD_NAME 128 61 #define MAX_POLLER_NAME 128 62 #define MAX_THREADS 4096 63 #define RR_MAX_VALUE 255 64 65 #define MAX_STRING_LEN 12289 /* 3x 4k monitors + 1 */ 66 #define TAB_WIN_HEIGHT 3 67 #define TAB_WIN_LOCATION_ROW 1 68 #define TABS_SPACING 2 69 #define TABS_LOCATION_ROW 4 70 #define TABS_LOCATION_COL 0 71 #define TABS_DATA_START_ROW 3 72 #define TABS_DATA_START_COL 2 73 #define TABS_COL_COUNT 10 74 #define MENU_WIN_HEIGHT 3 75 #define MENU_WIN_SPACING 4 76 #define MENU_WIN_LOCATION_COL 0 77 #define RR_WIN_WIDTH 32 78 #define RR_WIN_HEIGHT 5 79 #define MAX_THREAD_NAME_LEN 26 80 #define MAX_THREAD_COUNT_STR_LEN 14 81 #define MAX_POLLER_NAME_LEN 36 82 #define MAX_POLLER_COUNT_STR_LEN 16 83 #define MAX_POLLER_TYPE_STR_LEN 8 84 #define MAX_POLLER_IND_STR_LEN 8 85 #define MAX_CORE_MASK_STR_LEN 16 86 #define MAX_CORE_STR_LEN 6 87 #define MAX_CORE_FREQ_STR_LEN 18 88 #define MAX_TIME_STR_LEN 12 89 #define MAX_POLLER_RUN_COUNT 20 90 #define MAX_PERIOD_STR_LEN 12 91 #define WINDOW_HEADER 12 92 #define FROM_HEX 16 93 #define THREAD_WIN_WIDTH 69 94 #define THREAD_WIN_HEIGHT 9 95 #define THREAD_WIN_FIRST_COL 2 96 #define CORE_WIN_FIRST_COL 16 97 #define CORE_WIN_WIDTH 48 98 #define CORE_WIN_HEIGHT 11 99 #define POLLER_WIN_HEIGHT 8 100 #define POLLER_WIN_WIDTH 64 101 #define POLLER_WIN_FIRST_COL 14 102 #define FIRST_DATA_ROW 7 103 #define HELP_WIN_WIDTH 88 104 #define HELP_WIN_HEIGHT 22 105 106 enum tabs { 107 THREADS_TAB, 108 POLLERS_TAB, 109 CORES_TAB, 110 NUMBER_OF_TABS, 111 }; 112 113 enum spdk_poller_type { 114 SPDK_ACTIVE_POLLER, 115 SPDK_TIMED_POLLER, 116 SPDK_PAUSED_POLLER, 117 SPDK_POLLER_TYPES_COUNT, 118 }; 119 120 struct col_desc { 121 const char *name; 122 uint8_t name_len; 123 uint8_t max_data_string; 124 bool disabled; 125 }; 126 127 struct run_counter_history { 128 char *poller_name; 129 uint64_t thread_id; 130 uint64_t last_run_counter; 131 TAILQ_ENTRY(run_counter_history) link; 132 }; 133 134 struct core_info { 135 uint32_t core; 136 uint64_t threads_count; 137 uint64_t pollers_count; 138 uint64_t idle; 139 uint64_t last_idle; 140 uint64_t busy; 141 uint64_t last_busy; 142 uint32_t core_freq; 143 }; 144 145 uint8_t g_sleep_time = 1; 146 uint16_t g_selected_row; 147 uint16_t g_max_selected_row; 148 const char *poller_type_str[SPDK_POLLER_TYPES_COUNT] = {"Active", "Timed", "Paused"}; 149 const char *g_tab_title[NUMBER_OF_TABS] = {"[1] THREADS", "[2] POLLERS", "[3] CORES"}; 150 struct spdk_jsonrpc_client *g_rpc_client; 151 static TAILQ_HEAD(, run_counter_history) g_run_counter_history = TAILQ_HEAD_INITIALIZER( 152 g_run_counter_history); 153 struct core_info g_cores_history[RPC_MAX_CORES]; 154 WINDOW *g_menu_win, *g_tab_win[NUMBER_OF_TABS], *g_tabs[NUMBER_OF_TABS]; 155 PANEL *g_panels[NUMBER_OF_TABS]; 156 uint16_t g_max_row, g_max_col; 157 uint16_t g_data_win_size, g_max_data_rows; 158 uint32_t g_last_threads_count, g_last_pollers_count, g_last_cores_count; 159 uint8_t g_current_sort_col[NUMBER_OF_TABS] = {0, 0, 0}; 160 bool g_interval_data = true; 161 bool g_quit_app = false; 162 pthread_mutex_t g_thread_lock; 163 static struct col_desc g_col_desc[NUMBER_OF_TABS][TABS_COL_COUNT] = { 164 { {.name = "Thread name", .max_data_string = MAX_THREAD_NAME_LEN}, 165 {.name = "Core", .max_data_string = MAX_CORE_STR_LEN}, 166 {.name = "Active pollers", .max_data_string = MAX_POLLER_COUNT_STR_LEN}, 167 {.name = "Timed pollers", .max_data_string = MAX_POLLER_COUNT_STR_LEN}, 168 {.name = "Paused pollers", .max_data_string = MAX_POLLER_COUNT_STR_LEN}, 169 {.name = "Idle [us]", .max_data_string = MAX_TIME_STR_LEN}, 170 {.name = "Busy [us]", .max_data_string = MAX_TIME_STR_LEN}, 171 {.name = (char *)NULL} 172 }, 173 { {.name = "Poller name", .max_data_string = MAX_POLLER_NAME_LEN}, 174 {.name = "Type", .max_data_string = MAX_POLLER_TYPE_STR_LEN}, 175 {.name = "On thread", .max_data_string = MAX_THREAD_NAME_LEN}, 176 {.name = "Run count", .max_data_string = MAX_POLLER_RUN_COUNT}, 177 {.name = "Period [us]", .max_data_string = MAX_PERIOD_STR_LEN}, 178 {.name = "Status", .max_data_string = MAX_POLLER_IND_STR_LEN}, 179 {.name = (char *)NULL} 180 }, 181 { {.name = "Core", .max_data_string = MAX_CORE_STR_LEN}, 182 {.name = "Thread count", .max_data_string = MAX_THREAD_COUNT_STR_LEN}, 183 {.name = "Poller count", .max_data_string = MAX_POLLER_COUNT_STR_LEN}, 184 {.name = "Idle [us]", .max_data_string = MAX_TIME_STR_LEN}, 185 {.name = "Busy [us]", .max_data_string = MAX_TIME_STR_LEN}, 186 {.name = "Frequency [MHz]", .max_data_string = MAX_CORE_FREQ_STR_LEN}, 187 {.name = (char *)NULL} 188 } 189 }; 190 191 struct rpc_thread_info { 192 char *name; 193 uint64_t id; 194 int core_num; 195 char *cpumask; 196 uint64_t busy; 197 uint64_t last_busy; 198 uint64_t idle; 199 uint64_t last_idle; 200 uint64_t active_pollers_count; 201 uint64_t timed_pollers_count; 202 uint64_t paused_pollers_count; 203 }; 204 205 struct rpc_threads { 206 uint64_t threads_count; 207 struct rpc_thread_info thread_info[RPC_MAX_THREADS]; 208 }; 209 210 struct rpc_threads_stats { 211 uint64_t tick_rate; 212 struct rpc_threads threads; 213 }; 214 215 struct rpc_poller_info { 216 char *name; 217 char *state; 218 uint64_t run_count; 219 uint64_t busy_count; 220 uint64_t period_ticks; 221 enum spdk_poller_type type; 222 char thread_name[MAX_THREAD_NAME]; 223 uint64_t thread_id; 224 }; 225 226 struct rpc_pollers { 227 uint64_t pollers_count; 228 struct rpc_poller_info pollers[RPC_MAX_POLLERS]; 229 }; 230 231 struct rpc_poller_thread_info { 232 char *name; 233 uint64_t id; 234 struct rpc_pollers active_pollers; 235 struct rpc_pollers timed_pollers; 236 struct rpc_pollers paused_pollers; 237 }; 238 239 struct rpc_pollers_threads { 240 uint64_t threads_count; 241 struct rpc_poller_thread_info threads[RPC_MAX_THREADS]; 242 }; 243 244 struct rpc_pollers_stats { 245 uint64_t tick_rate; 246 struct rpc_pollers_threads pollers_threads; 247 }; 248 249 struct rpc_core_thread_info { 250 char *name; 251 uint64_t id; 252 char *cpumask; 253 uint64_t elapsed; 254 }; 255 256 struct rpc_core_threads { 257 uint64_t threads_count; 258 struct rpc_core_thread_info thread[RPC_MAX_THREADS]; 259 }; 260 261 struct rpc_core_info { 262 uint32_t lcore; 263 uint64_t busy; 264 uint64_t idle; 265 uint32_t core_freq; 266 struct rpc_core_threads threads; 267 }; 268 269 struct rpc_cores { 270 uint64_t cores_count; 271 struct rpc_core_info core[RPC_MAX_CORES]; 272 }; 273 274 struct rpc_cores_stats { 275 uint64_t tick_rate; 276 struct rpc_cores cores; 277 }; 278 279 struct rpc_threads_stats g_threads_stats; 280 struct rpc_pollers_stats g_pollers_stats; 281 struct rpc_cores_stats g_cores_stats; 282 283 static void 284 init_str_len(void) 285 { 286 int i, j; 287 288 for (i = 0; i < NUMBER_OF_TABS; i++) { 289 for (j = 0; g_col_desc[i][j].name != NULL; j++) { 290 g_col_desc[i][j].name_len = strlen(g_col_desc[i][j].name); 291 } 292 } 293 } 294 295 static void 296 free_rpc_threads_stats(struct rpc_threads_stats *req) 297 { 298 uint64_t i; 299 300 for (i = 0; i < req->threads.threads_count; i++) { 301 free(req->threads.thread_info[i].name); 302 req->threads.thread_info[i].name = NULL; 303 free(req->threads.thread_info[i].cpumask); 304 req->threads.thread_info[i].cpumask = NULL; 305 } 306 } 307 308 static const struct spdk_json_object_decoder rpc_thread_info_decoders[] = { 309 {"name", offsetof(struct rpc_thread_info, name), spdk_json_decode_string}, 310 {"id", offsetof(struct rpc_thread_info, id), spdk_json_decode_uint64}, 311 {"cpumask", offsetof(struct rpc_thread_info, cpumask), spdk_json_decode_string}, 312 {"busy", offsetof(struct rpc_thread_info, busy), spdk_json_decode_uint64}, 313 {"idle", offsetof(struct rpc_thread_info, idle), spdk_json_decode_uint64}, 314 {"active_pollers_count", offsetof(struct rpc_thread_info, active_pollers_count), spdk_json_decode_uint64}, 315 {"timed_pollers_count", offsetof(struct rpc_thread_info, timed_pollers_count), spdk_json_decode_uint64}, 316 {"paused_pollers_count", offsetof(struct rpc_thread_info, paused_pollers_count), spdk_json_decode_uint64}, 317 }; 318 319 static int 320 rpc_decode_threads_object(const struct spdk_json_val *val, void *out) 321 { 322 struct rpc_thread_info *info = out; 323 324 return spdk_json_decode_object(val, rpc_thread_info_decoders, 325 SPDK_COUNTOF(rpc_thread_info_decoders), info); 326 } 327 328 static int 329 rpc_decode_threads_array(const struct spdk_json_val *val, void *out) 330 { 331 struct rpc_threads *threads = out; 332 333 return spdk_json_decode_array(val, rpc_decode_threads_object, threads->thread_info, RPC_MAX_THREADS, 334 &threads->threads_count, sizeof(struct rpc_thread_info)); 335 } 336 337 static const struct spdk_json_object_decoder rpc_threads_stats_decoders[] = { 338 {"tick_rate", offsetof(struct rpc_threads_stats, tick_rate), spdk_json_decode_uint64}, 339 {"threads", offsetof(struct rpc_threads_stats, threads), rpc_decode_threads_array}, 340 }; 341 342 static void 343 free_rpc_poller(struct rpc_poller_info *poller) 344 { 345 free(poller->name); 346 poller->name = NULL; 347 free(poller->state); 348 poller->state = NULL; 349 } 350 351 static void 352 free_rpc_pollers_stats(struct rpc_pollers_stats *req) 353 { 354 struct rpc_poller_thread_info *thread; 355 uint64_t i, j; 356 357 for (i = 0; i < req->pollers_threads.threads_count; i++) { 358 thread = &req->pollers_threads.threads[i]; 359 360 for (j = 0; j < thread->active_pollers.pollers_count; j++) { 361 free_rpc_poller(&thread->active_pollers.pollers[j]); 362 } 363 364 for (j = 0; j < thread->timed_pollers.pollers_count; j++) { 365 free_rpc_poller(&thread->timed_pollers.pollers[j]); 366 } 367 368 for (j = 0; j < thread->paused_pollers.pollers_count; j++) { 369 free_rpc_poller(&thread->paused_pollers.pollers[j]); 370 } 371 372 free(thread->name); 373 thread->name = NULL; 374 } 375 } 376 377 static void 378 free_rpc_cores_stats(struct rpc_cores_stats *req) 379 { 380 struct rpc_core_info *core; 381 struct rpc_core_thread_info *thread; 382 uint64_t i, j; 383 384 for (i = 0; i < req->cores.cores_count; i++) { 385 core = &req->cores.core[i]; 386 387 for (j = 0; j < core->threads.threads_count; j++) { 388 thread = &core->threads.thread[j]; 389 390 free(thread->name); 391 free(thread->cpumask); 392 } 393 } 394 } 395 396 static const struct spdk_json_object_decoder rpc_pollers_decoders[] = { 397 {"name", offsetof(struct rpc_poller_info, name), spdk_json_decode_string}, 398 {"state", offsetof(struct rpc_poller_info, state), spdk_json_decode_string}, 399 {"run_count", offsetof(struct rpc_poller_info, run_count), spdk_json_decode_uint64}, 400 {"busy_count", offsetof(struct rpc_poller_info, busy_count), spdk_json_decode_uint64}, 401 {"period_ticks", offsetof(struct rpc_poller_info, period_ticks), spdk_json_decode_uint64, true}, 402 }; 403 404 static int 405 rpc_decode_pollers_object(const struct spdk_json_val *val, void *out) 406 { 407 struct rpc_poller_info *info = out; 408 409 return spdk_json_decode_object(val, rpc_pollers_decoders, SPDK_COUNTOF(rpc_pollers_decoders), info); 410 } 411 412 static int 413 rpc_decode_pollers_array(const struct spdk_json_val *val, void *out) 414 { 415 struct rpc_pollers *pollers = out; 416 417 return spdk_json_decode_array(val, rpc_decode_pollers_object, pollers->pollers, RPC_MAX_THREADS, 418 &pollers->pollers_count, sizeof(struct rpc_poller_info)); 419 } 420 421 static const struct spdk_json_object_decoder rpc_pollers_threads_decoders[] = { 422 {"name", offsetof(struct rpc_poller_thread_info, name), spdk_json_decode_string}, 423 {"id", offsetof(struct rpc_poller_thread_info, id), spdk_json_decode_uint64}, 424 {"active_pollers", offsetof(struct rpc_poller_thread_info, active_pollers), rpc_decode_pollers_array}, 425 {"timed_pollers", offsetof(struct rpc_poller_thread_info, timed_pollers), rpc_decode_pollers_array}, 426 {"paused_pollers", offsetof(struct rpc_poller_thread_info, paused_pollers), rpc_decode_pollers_array}, 427 }; 428 429 static int 430 rpc_decode_pollers_threads_object(const struct spdk_json_val *val, void *out) 431 { 432 struct rpc_poller_thread_info *info = out; 433 434 return spdk_json_decode_object(val, rpc_pollers_threads_decoders, 435 SPDK_COUNTOF(rpc_pollers_threads_decoders), info); 436 } 437 438 static int 439 rpc_decode_pollers_threads_array(const struct spdk_json_val *val, void *out) 440 { 441 struct rpc_pollers_threads *pollers_threads = out; 442 443 return spdk_json_decode_array(val, rpc_decode_pollers_threads_object, pollers_threads->threads, 444 RPC_MAX_THREADS, &pollers_threads->threads_count, sizeof(struct rpc_poller_thread_info)); 445 } 446 447 static const struct spdk_json_object_decoder rpc_pollers_stats_decoders[] = { 448 {"tick_rate", offsetof(struct rpc_pollers_stats, tick_rate), spdk_json_decode_uint64}, 449 {"threads", offsetof(struct rpc_pollers_stats, pollers_threads), rpc_decode_pollers_threads_array}, 450 }; 451 452 static const struct spdk_json_object_decoder rpc_core_thread_info_decoders[] = { 453 {"name", offsetof(struct rpc_core_thread_info, name), spdk_json_decode_string}, 454 {"id", offsetof(struct rpc_core_thread_info, id), spdk_json_decode_uint64}, 455 {"cpumask", offsetof(struct rpc_core_thread_info, cpumask), spdk_json_decode_string}, 456 {"elapsed", offsetof(struct rpc_core_thread_info, elapsed), spdk_json_decode_uint64}, 457 }; 458 459 static int 460 rpc_decode_core_threads_object(const struct spdk_json_val *val, void *out) 461 { 462 struct rpc_core_thread_info *info = out; 463 464 return spdk_json_decode_object(val, rpc_core_thread_info_decoders, 465 SPDK_COUNTOF(rpc_core_thread_info_decoders), info); 466 } 467 468 static int 469 rpc_decode_cores_lw_threads(const struct spdk_json_val *val, void *out) 470 { 471 struct rpc_core_threads *threads = out; 472 473 return spdk_json_decode_array(val, rpc_decode_core_threads_object, threads->thread, RPC_MAX_THREADS, 474 &threads->threads_count, sizeof(struct rpc_core_thread_info)); 475 } 476 477 static const struct spdk_json_object_decoder rpc_core_info_decoders[] = { 478 {"lcore", offsetof(struct rpc_core_info, lcore), spdk_json_decode_uint32}, 479 {"busy", offsetof(struct rpc_core_info, busy), spdk_json_decode_uint64}, 480 {"idle", offsetof(struct rpc_core_info, idle), spdk_json_decode_uint64}, 481 {"core_freq", offsetof(struct rpc_core_info, core_freq), spdk_json_decode_uint32, true}, 482 {"lw_threads", offsetof(struct rpc_core_info, threads), rpc_decode_cores_lw_threads}, 483 }; 484 485 static int 486 rpc_decode_core_object(const struct spdk_json_val *val, void *out) 487 { 488 struct rpc_core_info *info = out; 489 490 return spdk_json_decode_object(val, rpc_core_info_decoders, 491 SPDK_COUNTOF(rpc_core_info_decoders), info); 492 } 493 494 static int 495 rpc_decode_cores_array(const struct spdk_json_val *val, void *out) 496 { 497 struct rpc_cores *cores = out; 498 499 return spdk_json_decode_array(val, rpc_decode_core_object, cores->core, 500 RPC_MAX_THREADS, &cores->cores_count, sizeof(struct rpc_core_info)); 501 } 502 503 static const struct spdk_json_object_decoder rpc_cores_stats_decoders[] = { 504 {"tick_rate", offsetof(struct rpc_cores_stats, tick_rate), spdk_json_decode_uint64}, 505 {"reactors", offsetof(struct rpc_cores_stats, cores), rpc_decode_cores_array}, 506 }; 507 508 509 static int 510 rpc_send_req(char *rpc_name, struct spdk_jsonrpc_client_response **resp) 511 { 512 struct spdk_jsonrpc_client_response *json_resp = NULL; 513 struct spdk_json_write_ctx *w; 514 struct spdk_jsonrpc_client_request *request; 515 int rc; 516 517 request = spdk_jsonrpc_client_create_request(); 518 if (request == NULL) { 519 return -ENOMEM; 520 } 521 522 w = spdk_jsonrpc_begin_request(request, 1, rpc_name); 523 spdk_jsonrpc_end_request(request, w); 524 spdk_jsonrpc_client_send_request(g_rpc_client, request); 525 526 do { 527 rc = spdk_jsonrpc_client_poll(g_rpc_client, 1); 528 } while (rc == 0 || rc == -ENOTCONN); 529 530 if (rc <= 0) { 531 return -1; 532 } 533 534 json_resp = spdk_jsonrpc_client_get_response(g_rpc_client); 535 if (json_resp == NULL) { 536 return -1; 537 } 538 539 /* Check for error response */ 540 if (json_resp->error != NULL) { 541 return -1; 542 } 543 544 assert(json_resp->result); 545 546 *resp = json_resp; 547 548 return 0; 549 } 550 551 552 static int 553 sort_threads(const void *p1, const void *p2) 554 { 555 const struct rpc_thread_info thread_info1 = *(struct rpc_thread_info *)p1; 556 const struct rpc_thread_info thread_info2 = *(struct rpc_thread_info *)p2; 557 uint64_t count1, count2; 558 559 switch (g_current_sort_col[THREADS_TAB]) { 560 case 0: /* Sort by name */ 561 return strcmp(thread_info1.name, thread_info2.name); 562 case 1: /* Sort by core */ 563 count2 = thread_info1.core_num; 564 count1 = thread_info2.core_num; 565 break; 566 case 2: /* Sort by active pollers number */ 567 count1 = thread_info1.active_pollers_count; 568 count2 = thread_info2.active_pollers_count; 569 break; 570 case 3: /* Sort by timed pollers number */ 571 count1 = thread_info1.timed_pollers_count; 572 count2 = thread_info2.timed_pollers_count; 573 break; 574 case 4: /* Sort by paused pollers number */ 575 count1 = thread_info1.paused_pollers_count; 576 count2 = thread_info2.paused_pollers_count; 577 break; 578 case 5: /* Sort by idle time */ 579 count1 = thread_info1.idle - thread_info1.last_idle; 580 count2 = thread_info2.idle - thread_info2.last_idle; 581 break; 582 case 6: /* Sort by busy time */ 583 count1 = thread_info1.busy - thread_info1.last_busy; 584 count2 = thread_info2.busy - thread_info2.last_busy; 585 break; 586 default: 587 return 0; 588 } 589 590 if (count2 > count1) { 591 return 1; 592 } else if (count2 < count1) { 593 return -1; 594 } else { 595 return 0; 596 } 597 } 598 599 static void 600 store_last_run_counter(const char *poller_name, uint64_t thread_id, uint64_t last_run_counter) 601 { 602 struct run_counter_history *history; 603 604 TAILQ_FOREACH(history, &g_run_counter_history, link) { 605 if (!strcmp(history->poller_name, poller_name) && history->thread_id == thread_id) { 606 history->last_run_counter = last_run_counter; 607 return; 608 } 609 } 610 611 history = calloc(1, sizeof(*history)); 612 if (history == NULL) { 613 fprintf(stderr, "Unable to allocate a history object in store_last_run_counter.\n"); 614 return; 615 } 616 history->poller_name = strdup(poller_name); 617 if (!history->poller_name) { 618 fprintf(stderr, "Unable to allocate poller_name of a history object in store_last_run_counter.\n"); 619 free(history); 620 return; 621 } 622 history->thread_id = thread_id; 623 history->last_run_counter = last_run_counter; 624 625 TAILQ_INSERT_TAIL(&g_run_counter_history, history, link); 626 } 627 628 static int 629 get_thread_data(void) 630 { 631 struct spdk_jsonrpc_client_response *json_resp = NULL; 632 struct rpc_threads_stats threads_stats; 633 struct rpc_thread_info *thread_info; 634 struct rpc_core_info *core_info; 635 uint64_t i, j, k; 636 int rc = 0; 637 638 rc = rpc_send_req("thread_get_stats", &json_resp); 639 if (rc) { 640 return rc; 641 } 642 643 /* Decode json */ 644 memset(&threads_stats, 0, sizeof(threads_stats)); 645 if (spdk_json_decode_object(json_resp->result, rpc_threads_stats_decoders, 646 SPDK_COUNTOF(rpc_threads_stats_decoders), &threads_stats)) { 647 rc = -EINVAL; 648 goto end; 649 } 650 651 pthread_mutex_lock(&g_thread_lock); 652 653 /* This is to free allocated char arrays with old thread names */ 654 free_rpc_threads_stats(&g_threads_stats); 655 656 for (i = 0; i < threads_stats.threads.threads_count; i++) { 657 for (j = 0; j < g_threads_stats.threads.threads_count; j++) { 658 if (g_threads_stats.threads.thread_info[j].id == threads_stats.threads.thread_info[i].id) { 659 threads_stats.threads.thread_info[i].last_busy = g_threads_stats.threads.thread_info[j].busy; 660 threads_stats.threads.thread_info[i].last_idle = g_threads_stats.threads.thread_info[j].idle; 661 } 662 } 663 } 664 memcpy(&g_threads_stats, &threads_stats, sizeof(struct rpc_threads_stats)); 665 qsort(&g_threads_stats.threads.thread_info, threads_stats.threads.threads_count, 666 sizeof(g_threads_stats.threads.thread_info[0]), sort_threads); 667 668 for (i = 0; i < g_threads_stats.threads.threads_count; i++) { 669 g_threads_stats.threads.thread_info[i].core_num = -1; 670 } 671 672 for (i = 0; i < g_cores_stats.cores.cores_count; i++) { 673 core_info = &g_cores_stats.cores.core[i]; 674 675 for (j = 0; j < core_info->threads.threads_count; j++) { 676 for (k = 0; k < g_threads_stats.threads.threads_count; k++) { 677 /* For each thread on current core: check if it's ID also exists 678 * in g_thread_info data structure. If it does then assign current 679 * core's number to that thread, otherwise application state is inconsistent 680 * (e.g. scheduler is moving threads between cores). */ 681 thread_info = &g_threads_stats.threads.thread_info[k]; 682 if (thread_info->id == core_info->threads.thread[j].id) { 683 thread_info->core_num = core_info->lcore; 684 break; 685 } 686 } 687 } 688 } 689 690 pthread_mutex_unlock(&g_thread_lock); 691 692 end: 693 spdk_jsonrpc_client_free_response(json_resp); 694 return rc; 695 } 696 697 static int 698 get_pollers_data(void) 699 { 700 struct spdk_jsonrpc_client_response *json_resp = NULL; 701 int rc = 0; 702 struct rpc_pollers *pollers; 703 struct rpc_poller_info *poller; 704 uint64_t pollers_count = 0; 705 uint64_t i, j; 706 707 rc = rpc_send_req("thread_get_pollers", &json_resp); 708 if (rc) { 709 return rc; 710 } 711 712 pthread_mutex_lock(&g_thread_lock); 713 714 /* Save last run counter of each poller before updating g_pollers_stats */ 715 for (i = 0; i < g_pollers_stats.pollers_threads.threads_count; i++) { 716 pollers = &g_pollers_stats.pollers_threads.threads[i].active_pollers; 717 for (j = 0; j < pollers->pollers_count; j++) { 718 poller = &pollers->pollers[j]; 719 store_last_run_counter(poller->name, poller->thread_id, poller->run_count); 720 } 721 pollers_count += pollers->pollers_count; 722 723 pollers = &g_pollers_stats.pollers_threads.threads[i].timed_pollers; 724 for (j = 0; j < pollers->pollers_count; j++) { 725 poller = &pollers->pollers[j]; 726 store_last_run_counter(poller->name, poller->thread_id, poller->run_count); 727 } 728 pollers_count += pollers->pollers_count; 729 730 pollers = &g_pollers_stats.pollers_threads.threads[i].paused_pollers; 731 for (j = 0; j < pollers->pollers_count; j++) { 732 poller = &pollers->pollers[j]; 733 store_last_run_counter(poller->name, poller->thread_id, poller->run_count); 734 } 735 pollers_count += pollers->pollers_count; 736 } 737 g_last_pollers_count = pollers_count; 738 739 /* Free old pollers values before allocating memory for new ones */ 740 free_rpc_pollers_stats(&g_pollers_stats); 741 742 /* Decode json */ 743 memset(&g_pollers_stats, 0, sizeof(g_pollers_stats)); 744 if (spdk_json_decode_object(json_resp->result, rpc_pollers_stats_decoders, 745 SPDK_COUNTOF(rpc_pollers_stats_decoders), &g_pollers_stats)) { 746 rc = -EINVAL; 747 } 748 749 pthread_mutex_unlock(&g_thread_lock); 750 spdk_jsonrpc_client_free_response(json_resp); 751 return rc; 752 } 753 754 static int 755 get_cores_data(void) 756 { 757 struct spdk_jsonrpc_client_response *json_resp = NULL; 758 struct rpc_core_info *core_info; 759 uint64_t i, j, k; 760 int rc = 0; 761 762 rc = rpc_send_req("framework_get_reactors", &json_resp); 763 if (rc) { 764 return rc; 765 } 766 767 pthread_mutex_lock(&g_thread_lock); 768 769 /* Free old cores values before allocating memory for new ones */ 770 free_rpc_cores_stats(&g_cores_stats); 771 772 /* Decode json */ 773 memset(&g_cores_stats, 0, sizeof(g_cores_stats)); 774 if (spdk_json_decode_object(json_resp->result, rpc_cores_stats_decoders, 775 SPDK_COUNTOF(rpc_cores_stats_decoders), &g_cores_stats)) { 776 rc = -EINVAL; 777 goto end; 778 } 779 780 for (i = 0; i < g_cores_stats.cores.cores_count; i++) { 781 core_info = &g_cores_stats.cores.core[i]; 782 783 for (j = 0; j < core_info->threads.threads_count; j++) { 784 for (k = 0; k < g_threads_stats.threads.threads_count; k++) { 785 if (core_info->threads.thread[j].id == g_threads_stats.threads.thread_info[k].id) { 786 g_threads_stats.threads.thread_info[k].core_num = core_info->lcore; 787 } 788 } 789 } 790 } 791 792 end: 793 pthread_mutex_unlock(&g_thread_lock); 794 spdk_jsonrpc_client_free_response(json_resp); 795 return rc; 796 } 797 798 enum str_alignment { 799 ALIGN_LEFT, 800 ALIGN_RIGHT, 801 }; 802 803 static void 804 print_max_len(WINDOW *win, int row, uint16_t col, uint16_t max_len, enum str_alignment alignment, 805 const char *string) 806 { 807 const char dots[] = "..."; 808 int DOTS_STR_LEN = sizeof(dots) / sizeof(dots[0]); 809 char tmp_str[MAX_STRING_LEN]; 810 int len, max_col, max_str, cmp_len; 811 int max_row; 812 813 len = strlen(string); 814 getmaxyx(win, max_row, max_col); 815 816 if (row > max_row) { 817 /* We are in a process of resizing and this may happen */ 818 return; 819 } 820 821 if (max_len != 0 && col + max_len < max_col) { 822 max_col = col + max_len; 823 } 824 825 max_str = max_col - col; 826 827 if (max_str <= DOTS_STR_LEN + 1) { 828 /* No space to print anything, but we have to let a user know about it */ 829 mvwprintw(win, row, max_col - DOTS_STR_LEN - 1, "..."); 830 refresh(); 831 wrefresh(win); 832 return; 833 } 834 835 if (max_len) { 836 if (alignment == ALIGN_LEFT) { 837 snprintf(tmp_str, max_str, "%s%*c", string, max_len - len - 1, ' '); 838 } else { 839 snprintf(tmp_str, max_str, "%*c%s", max_len - len - 1, ' ', string); 840 } 841 cmp_len = max_len - 1; 842 } else { 843 snprintf(tmp_str, max_str, "%s", string); 844 cmp_len = len; 845 } 846 847 if (col + cmp_len > max_col - 1) { 848 snprintf(&tmp_str[max_str - DOTS_STR_LEN - 2], DOTS_STR_LEN, "%s", dots); 849 } 850 851 mvwprintw(win, row, col, tmp_str); 852 853 refresh(); 854 wrefresh(win); 855 } 856 857 static void 858 draw_menu_win(void) 859 { 860 wbkgd(g_menu_win, COLOR_PAIR(2)); 861 box(g_menu_win, 0, 0); 862 print_max_len(g_menu_win, 1, 1, 0, ALIGN_LEFT, 863 " [q] Quit | [1-3][Tab] Switch tab | [PgUp] Previous page | [PgDown] Next page | [Enter] Item details | [h] Help"); 864 } 865 866 static void 867 draw_tab_win(enum tabs tab) 868 { 869 uint16_t col; 870 uint8_t white_spaces = TABS_SPACING * NUMBER_OF_TABS; 871 872 wbkgd(g_tab_win[tab], COLOR_PAIR(2)); 873 box(g_tab_win[tab], 0, 0); 874 875 col = ((g_max_col - white_spaces) / NUMBER_OF_TABS / 2) - (strlen(g_tab_title[tab]) / 2) - 876 TABS_SPACING; 877 print_max_len(g_tab_win[tab], 1, col, 0, ALIGN_LEFT, g_tab_title[tab]); 878 } 879 880 static void 881 draw_tabs(enum tabs tab_index, uint8_t sort_col) 882 { 883 struct col_desc *col_desc = g_col_desc[tab_index]; 884 WINDOW *tab = g_tabs[tab_index]; 885 int i, j; 886 uint16_t offset, draw_offset; 887 888 for (i = 0; col_desc[i].name != NULL; i++) { 889 if (col_desc[i].disabled) { 890 continue; 891 } 892 893 offset = 1; 894 for (j = i; j != 0; j--) { 895 if (!col_desc[j - 1].disabled) { 896 offset += col_desc[j - 1].max_data_string; 897 offset += col_desc[j - 1].name_len % 2 + 1; 898 } 899 } 900 901 draw_offset = offset + (col_desc[i].max_data_string / 2) - (col_desc[i].name_len / 2); 902 903 if (i == sort_col) { 904 wattron(tab, COLOR_PAIR(3)); 905 print_max_len(tab, 1, draw_offset, 0, ALIGN_LEFT, col_desc[i].name); 906 wattroff(tab, COLOR_PAIR(3)); 907 } else { 908 print_max_len(tab, 1, draw_offset, 0, ALIGN_LEFT, col_desc[i].name); 909 } 910 911 if (offset != 1) { 912 print_max_len(tab, 1, offset - 1, 0, ALIGN_LEFT, "|"); 913 } 914 } 915 916 print_max_len(tab, 2, 1, 0, ALIGN_LEFT, ""); /* Move to next line */ 917 whline(tab, ACS_HLINE, MAX_STRING_LEN); 918 box(tab, 0, 0); 919 wrefresh(tab); 920 } 921 922 static void 923 resize_interface(enum tabs tab) 924 { 925 int i; 926 927 clear(); 928 wclear(g_menu_win); 929 mvwin(g_menu_win, g_max_row - MENU_WIN_SPACING, MENU_WIN_LOCATION_COL); 930 wresize(g_menu_win, MENU_WIN_HEIGHT, g_max_col); 931 draw_menu_win(); 932 933 for (i = 0; i < NUMBER_OF_TABS; i++) { 934 wclear(g_tabs[i]); 935 wresize(g_tabs[i], g_max_row - MENU_WIN_HEIGHT - TAB_WIN_HEIGHT - 2, g_max_col); 936 mvwin(g_tabs[i], TABS_LOCATION_ROW, TABS_LOCATION_COL); 937 draw_tabs(i, g_current_sort_col[i]); 938 } 939 940 draw_tabs(tab, g_current_sort_col[tab]); 941 942 for (i = 0; i < NUMBER_OF_TABS; i++) { 943 wclear(g_tab_win[i]); 944 wresize(g_tab_win[i], TAB_WIN_HEIGHT, 945 (g_max_col - (TABS_SPACING * NUMBER_OF_TABS)) / NUMBER_OF_TABS); 946 mvwin(g_tab_win[i], TAB_WIN_LOCATION_ROW, 1 + (g_max_col / NUMBER_OF_TABS) * i); 947 draw_tab_win(i); 948 } 949 950 update_panels(); 951 doupdate(); 952 } 953 954 static void 955 switch_tab(enum tabs tab) 956 { 957 top_panel(g_panels[tab]); 958 update_panels(); 959 doupdate(); 960 } 961 962 static void 963 get_time_str(uint64_t ticks, char *time_str) 964 { 965 uint64_t time; 966 967 time = ticks * SPDK_SEC_TO_USEC / g_cores_stats.tick_rate; 968 snprintf(time_str, MAX_TIME_STR_LEN, "%" PRIu64, time); 969 } 970 971 static void 972 draw_row_background(uint8_t item_index, uint8_t tab) 973 { 974 int k; 975 976 if (item_index == g_selected_row) { 977 wattron(g_tabs[tab], COLOR_PAIR(2)); 978 } 979 for (k = 1; k < g_max_col - 1; k++) { 980 mvwprintw(g_tabs[tab], TABS_DATA_START_ROW + item_index, k, " "); 981 } 982 } 983 984 static uint8_t 985 refresh_threads_tab(uint8_t current_page) 986 { 987 struct col_desc *col_desc = g_col_desc[THREADS_TAB]; 988 uint64_t i, threads_count; 989 uint16_t j; 990 uint16_t col; 991 uint8_t max_pages, item_index; 992 static uint8_t last_page = 0; 993 char pollers_number[MAX_POLLER_COUNT_STR_LEN], idle_time[MAX_TIME_STR_LEN], 994 busy_time[MAX_TIME_STR_LEN], core_str[MAX_CORE_MASK_STR_LEN]; 995 struct rpc_thread_info *thread_info[RPC_MAX_THREADS]; 996 997 threads_count = g_threads_stats.threads.threads_count; 998 999 /* Clear screen if number of threads changed */ 1000 if (g_last_threads_count != threads_count) { 1001 for (i = TABS_DATA_START_ROW; i < g_data_win_size; i++) { 1002 for (j = 1; j < (uint64_t)g_max_col - 1; j++) { 1003 mvwprintw(g_tabs[THREADS_TAB], i, j, " "); 1004 } 1005 } 1006 1007 g_last_threads_count = threads_count; 1008 } 1009 1010 for (i = 0; i < threads_count; i++) { 1011 thread_info[i] = &g_threads_stats.threads.thread_info[i]; 1012 } 1013 1014 if (last_page != current_page) { 1015 last_page = current_page; 1016 } 1017 1018 max_pages = (threads_count + g_max_data_rows - 1) / g_max_data_rows; 1019 1020 for (i = current_page * g_max_data_rows; 1021 i < spdk_min(threads_count, (uint64_t)((current_page + 1) * g_max_data_rows)); 1022 i++) { 1023 item_index = i - (current_page * g_max_data_rows); 1024 1025 col = TABS_DATA_START_COL; 1026 1027 draw_row_background(item_index, THREADS_TAB); 1028 1029 if (!col_desc[0].disabled) { 1030 print_max_len(g_tabs[THREADS_TAB], TABS_DATA_START_ROW + item_index, col, 1031 col_desc[0].max_data_string, ALIGN_LEFT, thread_info[i]->name); 1032 col += col_desc[0].max_data_string; 1033 } 1034 1035 if (!col_desc[1].disabled) { 1036 snprintf(core_str, MAX_CORE_STR_LEN, "%d", thread_info[i]->core_num); 1037 print_max_len(g_tabs[THREADS_TAB], TABS_DATA_START_ROW + item_index, 1038 col, col_desc[1].max_data_string, ALIGN_RIGHT, core_str); 1039 col += col_desc[1].max_data_string + 2; 1040 } 1041 1042 if (!col_desc[2].disabled) { 1043 snprintf(pollers_number, MAX_POLLER_COUNT_STR_LEN, "%ld", thread_info[i]->active_pollers_count); 1044 print_max_len(g_tabs[THREADS_TAB], TABS_DATA_START_ROW + item_index, 1045 col + (col_desc[2].name_len / 2), col_desc[2].max_data_string, ALIGN_LEFT, pollers_number); 1046 col += col_desc[2].max_data_string + 2; 1047 } 1048 1049 if (!col_desc[3].disabled) { 1050 snprintf(pollers_number, MAX_POLLER_COUNT_STR_LEN, "%ld", thread_info[i]->timed_pollers_count); 1051 print_max_len(g_tabs[THREADS_TAB], TABS_DATA_START_ROW + item_index, 1052 col + (col_desc[3].name_len / 2), col_desc[3].max_data_string, ALIGN_LEFT, pollers_number); 1053 col += col_desc[3].max_data_string + 1; 1054 } 1055 1056 if (!col_desc[4].disabled) { 1057 snprintf(pollers_number, MAX_POLLER_COUNT_STR_LEN, "%ld", thread_info[i]->paused_pollers_count); 1058 print_max_len(g_tabs[THREADS_TAB], TABS_DATA_START_ROW + item_index, 1059 col + (col_desc[4].name_len / 2), col_desc[4].max_data_string, ALIGN_LEFT, pollers_number); 1060 col += col_desc[4].max_data_string + 2; 1061 } 1062 1063 if (!col_desc[5].disabled) { 1064 if (g_interval_data == true) { 1065 get_time_str(thread_info[i]->idle - thread_info[i]->last_idle, idle_time); 1066 } else { 1067 get_time_str(thread_info[i]->idle, idle_time); 1068 } 1069 print_max_len(g_tabs[THREADS_TAB], TABS_DATA_START_ROW + item_index, col, 1070 col_desc[5].max_data_string, ALIGN_RIGHT, idle_time); 1071 col += col_desc[5].max_data_string; 1072 } 1073 1074 if (!col_desc[6].disabled) { 1075 if (g_interval_data == true) { 1076 get_time_str(thread_info[i]->busy - thread_info[i]->last_busy, busy_time); 1077 } else { 1078 get_time_str(thread_info[i]->busy, busy_time); 1079 } 1080 print_max_len(g_tabs[THREADS_TAB], TABS_DATA_START_ROW + item_index, col, 1081 col_desc[6].max_data_string, ALIGN_RIGHT, busy_time); 1082 } 1083 1084 if (item_index == g_selected_row) { 1085 wattroff(g_tabs[THREADS_TAB], COLOR_PAIR(2)); 1086 } 1087 } 1088 1089 g_max_selected_row = i - current_page * g_max_data_rows - 1; 1090 1091 return max_pages; 1092 } 1093 1094 static uint64_t * 1095 get_last_run_counter(const char *poller_name, uint64_t thread_id) 1096 { 1097 struct run_counter_history *history; 1098 1099 TAILQ_FOREACH(history, &g_run_counter_history, link) { 1100 if (!strcmp(history->poller_name, poller_name) && history->thread_id == thread_id) { 1101 return &history->last_run_counter; 1102 } 1103 } 1104 1105 return NULL; 1106 } 1107 1108 enum sort_type { 1109 BY_NAME, 1110 USE_GLOBAL, 1111 }; 1112 1113 static int 1114 #ifdef __FreeBSD__ 1115 sort_pollers(void *arg, const void *p1, const void *p2) 1116 #else 1117 sort_pollers(const void *p1, const void *p2, void *arg) 1118 #endif 1119 { 1120 const struct rpc_poller_info *poller1 = *(struct rpc_poller_info **)p1; 1121 const struct rpc_poller_info *poller2 = *(struct rpc_poller_info **)p2; 1122 enum sort_type sorting = *(enum sort_type *)arg; 1123 uint64_t count1, count2; 1124 uint64_t *last_run_counter; 1125 1126 if (sorting == BY_NAME) { 1127 /* Sorting by name requested explicitly */ 1128 return strcmp(poller1->name, poller2->name); 1129 } else { 1130 /* Use globaly set sorting */ 1131 switch (g_current_sort_col[POLLERS_TAB]) { 1132 case 0: /* Sort by name */ 1133 return strcmp(poller1->name, poller2->name); 1134 case 1: /* Sort by type */ 1135 return poller1->type - poller2->type; 1136 case 2: /* Sort by thread */ 1137 return strcmp(poller1->thread_name, poller2->thread_name); 1138 case 3: /* Sort by run counter */ 1139 last_run_counter = get_last_run_counter(poller1->name, poller1->thread_id); 1140 assert(last_run_counter != NULL); 1141 count1 = poller1->run_count - *last_run_counter; 1142 last_run_counter = get_last_run_counter(poller2->name, poller2->thread_id); 1143 assert(last_run_counter != NULL); 1144 count2 = poller2->run_count - *last_run_counter; 1145 break; 1146 case 4: /* Sort by period */ 1147 count1 = poller1->period_ticks; 1148 count2 = poller2->period_ticks; 1149 break; 1150 default: 1151 return 0; 1152 } 1153 } 1154 1155 if (count2 > count1) { 1156 return 1; 1157 } else if (count2 < count1) { 1158 return -1; 1159 } else { 1160 return 0; 1161 } 1162 } 1163 1164 static void 1165 copy_pollers(struct rpc_pollers *pollers, uint64_t pollers_count, enum spdk_poller_type type, 1166 struct rpc_poller_thread_info *thread, uint64_t *current_count, bool reset_last_counter, 1167 struct rpc_poller_info **pollers_info) 1168 { 1169 uint64_t *last_run_counter; 1170 uint64_t i, j; 1171 struct rpc_thread_info *thread_info; 1172 1173 for (i = 0; i < pollers_count; i++) { 1174 for (j = 0; j < g_threads_stats.threads.threads_count; j++) { 1175 thread_info = &g_threads_stats.threads.thread_info[j]; 1176 /* Check if poller's thread exists in g_threads_stats 1177 * (if poller is not "hanging" without a thread). */ 1178 if (thread_info->id != thread->id) { 1179 continue; 1180 } 1181 1182 if (reset_last_counter) { 1183 last_run_counter = get_last_run_counter(pollers->pollers[i].name, thread->id); 1184 if (last_run_counter == NULL) { 1185 store_last_run_counter(pollers->pollers[i].name, thread->id, pollers->pollers[i].run_count); 1186 last_run_counter = get_last_run_counter(pollers->pollers[i].name, thread->id); 1187 } 1188 1189 assert(last_run_counter != NULL); 1190 *last_run_counter = pollers->pollers[i].run_count; 1191 } 1192 pollers_info[*current_count] = &pollers->pollers[i]; 1193 snprintf(pollers_info[*current_count]->thread_name, MAX_POLLER_NAME - 1, "%s", thread->name); 1194 pollers_info[*current_count]->thread_id = thread->id; 1195 pollers_info[(*current_count)++]->type = type; 1196 break; 1197 } 1198 } 1199 } 1200 1201 static uint8_t 1202 prepare_poller_data(uint8_t current_page, struct rpc_poller_info **pollers, 1203 uint64_t *count, uint8_t last_page) 1204 { 1205 struct rpc_poller_thread_info *thread; 1206 uint64_t i; 1207 bool reset_last_counter = false; 1208 enum sort_type sorting; 1209 1210 for (i = 0; i < g_pollers_stats.pollers_threads.threads_count; i++) { 1211 thread = &g_pollers_stats.pollers_threads.threads[i]; 1212 if (last_page != current_page) { 1213 reset_last_counter = true; 1214 } 1215 1216 copy_pollers(&thread->active_pollers, thread->active_pollers.pollers_count, SPDK_ACTIVE_POLLER, 1217 thread, count, reset_last_counter, pollers); 1218 copy_pollers(&thread->timed_pollers, thread->timed_pollers.pollers_count, SPDK_TIMED_POLLER, thread, 1219 count, reset_last_counter, pollers); 1220 copy_pollers(&thread->paused_pollers, thread->paused_pollers.pollers_count, SPDK_PAUSED_POLLER, 1221 thread, count, reset_last_counter, pollers); 1222 } 1223 1224 if (last_page != current_page) { 1225 last_page = current_page; 1226 } 1227 1228 /* Timed pollers can switch their possition on a list because of how they work. 1229 * Let's sort them by name first so that they won't switch on data refresh */ 1230 sorting = BY_NAME; 1231 qsort_r(pollers, *count, sizeof(pollers[0]), sort_pollers, (void *)&sorting); 1232 sorting = USE_GLOBAL; 1233 qsort_r(pollers, *count, sizeof(pollers[0]), sort_pollers, (void *)&sorting); 1234 1235 return last_page; 1236 } 1237 1238 static uint8_t 1239 refresh_pollers_tab(uint8_t current_page) 1240 { 1241 struct col_desc *col_desc = g_col_desc[POLLERS_TAB]; 1242 uint64_t *last_run_counter; 1243 uint64_t i, count = 0; 1244 uint16_t col, j; 1245 uint8_t max_pages, item_index; 1246 static uint8_t g_last_page = 0xF; 1247 /* Init g_last_page with value != 0 to force store_last_run_counter() call in copy_pollers() 1248 * so that initial values for run_counter are stored in g_run_counter_history */ 1249 char run_count[MAX_TIME_STR_LEN], period_ticks[MAX_PERIOD_STR_LEN]; 1250 struct rpc_poller_info *pollers[RPC_MAX_POLLERS]; 1251 1252 g_last_page = prepare_poller_data(current_page, pollers, &count, g_last_page); 1253 1254 max_pages = (count + g_max_data_rows - 1) / g_max_data_rows; 1255 1256 /* Clear screen if number of pollers changed */ 1257 if (g_last_pollers_count != count) { 1258 for (i = TABS_DATA_START_ROW; i < g_data_win_size; i++) { 1259 for (j = 1; j < (uint64_t)g_max_col - 1; j++) { 1260 mvwprintw(g_tabs[POLLERS_TAB], i, j, " "); 1261 } 1262 } 1263 1264 /* We need to run store_last_run_counter() again, so the easiest way is to call this function 1265 * again with changed g_last_page value */ 1266 g_last_page = 0xF; 1267 refresh_pollers_tab(current_page); 1268 return max_pages; 1269 } 1270 1271 /* Display info */ 1272 for (i = current_page * g_max_data_rows; 1273 i < spdk_min(count, (uint64_t)((current_page + 1) * g_max_data_rows)); 1274 i++) { 1275 item_index = i - (current_page * g_max_data_rows); 1276 1277 col = TABS_DATA_START_COL; 1278 1279 draw_row_background(item_index, POLLERS_TAB); 1280 1281 last_run_counter = get_last_run_counter(pollers[i]->name, pollers[i]->thread_id); 1282 assert(last_run_counter != NULL); 1283 1284 if (!col_desc[0].disabled) { 1285 print_max_len(g_tabs[POLLERS_TAB], TABS_DATA_START_ROW + item_index, col + 1, 1286 col_desc[0].max_data_string, ALIGN_LEFT, pollers[i]->name); 1287 col += col_desc[0].max_data_string + 2; 1288 } 1289 1290 if (!col_desc[1].disabled) { 1291 print_max_len(g_tabs[POLLERS_TAB], TABS_DATA_START_ROW + item_index, col, 1292 col_desc[1].max_data_string, ALIGN_LEFT, poller_type_str[pollers[i]->type]); 1293 col += col_desc[1].max_data_string + 2; 1294 } 1295 1296 if (!col_desc[2].disabled) { 1297 print_max_len(g_tabs[POLLERS_TAB], TABS_DATA_START_ROW + item_index, col, 1298 col_desc[2].max_data_string, ALIGN_LEFT, pollers[i]->thread_name); 1299 col += col_desc[2].max_data_string + 1; 1300 } 1301 1302 if (!col_desc[3].disabled) { 1303 if (g_interval_data == true) { 1304 snprintf(run_count, MAX_TIME_STR_LEN, "%" PRIu64, pollers[i]->run_count - *last_run_counter); 1305 } else { 1306 snprintf(run_count, MAX_TIME_STR_LEN, "%" PRIu64, pollers[i]->run_count); 1307 } 1308 print_max_len(g_tabs[POLLERS_TAB], TABS_DATA_START_ROW + item_index, col, 1309 col_desc[3].max_data_string, ALIGN_RIGHT, run_count); 1310 col += col_desc[3].max_data_string; 1311 } 1312 1313 if (!col_desc[4].disabled) { 1314 if (pollers[i]->period_ticks != 0) { 1315 get_time_str(pollers[i]->period_ticks, period_ticks); 1316 print_max_len(g_tabs[POLLERS_TAB], TABS_DATA_START_ROW + item_index, col, 1317 col_desc[4].max_data_string, ALIGN_RIGHT, period_ticks); 1318 } 1319 col += col_desc[3].max_data_string + 4; 1320 } 1321 1322 if (!col_desc[5].disabled) { 1323 if (pollers[i]->busy_count > 0) { 1324 if (item_index != g_selected_row) { 1325 wattron(g_tabs[POLLERS_TAB], COLOR_PAIR(6)); 1326 print_max_len(g_tabs[POLLERS_TAB], TABS_DATA_START_ROW + item_index, col, 1327 col_desc[5].max_data_string, ALIGN_RIGHT, "Busy"); 1328 wattroff(g_tabs[POLLERS_TAB], COLOR_PAIR(6)); 1329 } else { 1330 wattron(g_tabs[POLLERS_TAB], COLOR_PAIR(8)); 1331 print_max_len(g_tabs[POLLERS_TAB], TABS_DATA_START_ROW + item_index, col, 1332 col_desc[5].max_data_string, ALIGN_RIGHT, "Busy"); 1333 wattroff(g_tabs[POLLERS_TAB], COLOR_PAIR(8)); 1334 } 1335 } else { 1336 if (item_index != g_selected_row) { 1337 wattron(g_tabs[POLLERS_TAB], COLOR_PAIR(7)); 1338 print_max_len(g_tabs[POLLERS_TAB], TABS_DATA_START_ROW + item_index, col, 1339 col_desc[5].max_data_string, ALIGN_RIGHT, "Idle"); 1340 wattroff(g_tabs[POLLERS_TAB], COLOR_PAIR(7)); 1341 } else { 1342 wattron(g_tabs[POLLERS_TAB], COLOR_PAIR(9)); 1343 print_max_len(g_tabs[POLLERS_TAB], TABS_DATA_START_ROW + item_index, col, 1344 col_desc[5].max_data_string, ALIGN_RIGHT, "Idle"); 1345 wattroff(g_tabs[POLLERS_TAB], COLOR_PAIR(9)); 1346 } 1347 } 1348 } 1349 1350 if (item_index == g_selected_row) { 1351 wattroff(g_tabs[POLLERS_TAB], COLOR_PAIR(2)); 1352 } 1353 } 1354 1355 g_max_selected_row = i - current_page * g_max_data_rows - 1; 1356 1357 return max_pages; 1358 } 1359 1360 static int 1361 sort_cores(const void *p1, const void *p2) 1362 { 1363 const struct core_info core_info1 = *(struct core_info *)p1; 1364 const struct core_info core_info2 = *(struct core_info *)p2; 1365 uint64_t count1, count2; 1366 1367 switch (g_current_sort_col[CORES_TAB]) { 1368 case 0: /* Sort by core */ 1369 count1 = core_info2.core; 1370 count2 = core_info1.core; 1371 break; 1372 case 1: /* Sort by threads number */ 1373 count1 = core_info1.threads_count; 1374 count2 = core_info2.threads_count; 1375 break; 1376 case 2: /* Sort by pollers number */ 1377 count1 = core_info1.pollers_count; 1378 count2 = core_info2.pollers_count; 1379 break; 1380 case 3: /* Sort by idle time */ 1381 count2 = g_cores_history[core_info1.core].last_idle - core_info1.idle; 1382 count1 = g_cores_history[core_info2.core].last_idle - core_info2.idle; 1383 break; 1384 case 4: /* Sort by busy time */ 1385 count2 = g_cores_history[core_info1.core].last_busy - core_info1.busy; 1386 count1 = g_cores_history[core_info2.core].last_busy - core_info2.busy; 1387 break; 1388 default: 1389 return 0; 1390 } 1391 1392 if (count2 > count1) { 1393 return 1; 1394 } else if (count2 < count1) { 1395 return -1; 1396 } else { 1397 return 0; 1398 } 1399 } 1400 1401 static void 1402 store_core_last_stats(uint32_t core, uint64_t idle, uint64_t busy) 1403 { 1404 g_cores_history[core].last_idle = idle; 1405 g_cores_history[core].last_busy = busy; 1406 } 1407 1408 static void 1409 get_core_last_stats(uint32_t core, uint64_t *idle, uint64_t *busy) 1410 { 1411 *idle = g_cores_history[core].last_idle; 1412 *busy = g_cores_history[core].last_busy; 1413 } 1414 1415 static void 1416 store_core_stats(uint32_t core, uint64_t threads, uint64_t pollers, uint64_t idle, uint64_t busy) 1417 { 1418 g_cores_history[core].threads_count = threads; 1419 g_cores_history[core].pollers_count = pollers; 1420 g_cores_history[core].idle = idle; 1421 g_cores_history[core].busy = busy; 1422 } 1423 1424 static uint8_t 1425 refresh_cores_tab(uint8_t current_page) 1426 { 1427 struct col_desc *col_desc = g_col_desc[CORES_TAB]; 1428 uint64_t i; 1429 uint32_t core_num; 1430 uint16_t offset, count = 0; 1431 uint8_t max_pages, item_index; 1432 static uint8_t last_page = 0; 1433 char core[MAX_CORE_STR_LEN], threads_number[MAX_THREAD_COUNT_STR_LEN], 1434 pollers_number[MAX_POLLER_COUNT_STR_LEN], idle_time[MAX_TIME_STR_LEN], 1435 busy_time[MAX_TIME_STR_LEN], core_freq[MAX_CORE_FREQ_STR_LEN]; 1436 struct core_info cores[RPC_MAX_CORES]; 1437 1438 memset(&cores, 0, sizeof(cores)); 1439 1440 count = g_cores_stats.cores.cores_count; 1441 1442 for (i = 0; i < count; i++) { 1443 cores[i].core = g_cores_stats.cores.core[i].lcore; 1444 cores[i].busy = g_cores_stats.cores.core[i].busy; 1445 cores[i].idle = g_cores_stats.cores.core[i].idle; 1446 cores[i].core_freq = g_cores_stats.cores.core[i].core_freq; 1447 if (last_page != current_page) { 1448 store_core_last_stats(cores[i].core, cores[i].idle, cores[i].busy); 1449 } 1450 } 1451 1452 for (i = 0; i < g_threads_stats.threads.threads_count; i++) { 1453 core_num = g_threads_stats.threads.thread_info[i].core_num; 1454 for (int j = 0; j < count; j++) { 1455 if (cores[j].core == core_num) { 1456 cores[j].threads_count++; 1457 cores[j].pollers_count += g_threads_stats.threads.thread_info[i].active_pollers_count + 1458 g_threads_stats.threads.thread_info[i].timed_pollers_count + 1459 g_threads_stats.threads.thread_info[i].paused_pollers_count; 1460 } 1461 } 1462 } 1463 1464 if (last_page != current_page) { 1465 last_page = current_page; 1466 } 1467 1468 max_pages = (count + g_max_row - WINDOW_HEADER - 1) / (g_max_row - WINDOW_HEADER); 1469 1470 qsort(&cores, count, sizeof(cores[0]), sort_cores); 1471 1472 for (i = current_page * g_max_data_rows; 1473 i < spdk_min(count, (uint64_t)((current_page + 1) * g_max_data_rows)); 1474 i++) { 1475 item_index = i - (current_page * g_max_data_rows); 1476 1477 snprintf(threads_number, MAX_THREAD_COUNT_STR_LEN, "%ld", cores[i].threads_count); 1478 snprintf(pollers_number, MAX_POLLER_COUNT_STR_LEN, "%ld", cores[i].pollers_count); 1479 get_core_last_stats(cores[i].core, &cores[i].last_idle, &cores[i].last_busy); 1480 1481 offset = 1; 1482 1483 draw_row_background(item_index, CORES_TAB); 1484 1485 if (!col_desc[0].disabled) { 1486 snprintf(core, MAX_CORE_STR_LEN, "%d", cores[i].core); 1487 print_max_len(g_tabs[CORES_TAB], TABS_DATA_START_ROW + item_index, offset, 1488 col_desc[0].max_data_string, ALIGN_RIGHT, core); 1489 offset += col_desc[0].max_data_string + 2; 1490 } 1491 1492 if (!col_desc[1].disabled) { 1493 print_max_len(g_tabs[CORES_TAB], TABS_DATA_START_ROW + item_index, 1494 offset + (col_desc[1].name_len / 2), col_desc[1].max_data_string, ALIGN_LEFT, threads_number); 1495 offset += col_desc[1].max_data_string + 2; 1496 } 1497 1498 if (!col_desc[2].disabled) { 1499 print_max_len(g_tabs[CORES_TAB], TABS_DATA_START_ROW + item_index, 1500 offset + (col_desc[2].name_len / 2), col_desc[2].max_data_string, ALIGN_LEFT, pollers_number); 1501 offset += col_desc[2].max_data_string; 1502 } 1503 1504 if (!col_desc[3].disabled) { 1505 if (g_interval_data == true) { 1506 get_time_str(cores[i].idle - cores[i].last_idle, idle_time); 1507 } else { 1508 get_time_str(cores[i].idle, idle_time); 1509 } 1510 print_max_len(g_tabs[CORES_TAB], TABS_DATA_START_ROW + item_index, offset, 1511 col_desc[3].max_data_string, ALIGN_RIGHT, idle_time); 1512 offset += col_desc[3].max_data_string + 2; 1513 } 1514 1515 if (!col_desc[4].disabled) { 1516 if (g_interval_data == true) { 1517 get_time_str(cores[i].busy - cores[i].last_busy, busy_time); 1518 } else { 1519 get_time_str(cores[i].busy, busy_time); 1520 } 1521 print_max_len(g_tabs[CORES_TAB], TABS_DATA_START_ROW + item_index, offset, 1522 col_desc[4].max_data_string, ALIGN_RIGHT, busy_time); 1523 offset += col_desc[4].max_data_string + 2; 1524 } 1525 1526 if (!col_desc[5].disabled) { 1527 if (!cores[i].core_freq) { 1528 snprintf(core_freq, MAX_CORE_FREQ_STR_LEN, "%s", "N/A"); 1529 } else { 1530 snprintf(core_freq, MAX_CORE_FREQ_STR_LEN, "%" PRIu32, 1531 cores[i].core_freq); 1532 } 1533 print_max_len(g_tabs[CORES_TAB], TABS_DATA_START_ROW + item_index, offset, 1534 col_desc[5].max_data_string, ALIGN_RIGHT, core_freq); 1535 } 1536 1537 store_core_last_stats(cores[i].core, cores[i].idle, cores[i].busy); 1538 store_core_stats(cores[i].core, cores[i].threads_count, cores[i].pollers_count, 1539 cores[i].idle - cores[i].last_idle, cores[i].busy - cores[i].last_busy); 1540 1541 if (item_index == g_selected_row) { 1542 wattroff(g_tabs[CORES_TAB], COLOR_PAIR(2)); 1543 } 1544 } 1545 1546 g_max_selected_row = i - current_page * g_max_data_rows - 1; 1547 1548 return max_pages; 1549 } 1550 1551 static uint8_t 1552 refresh_tab(enum tabs tab, uint8_t current_page) 1553 { 1554 uint8_t (*refresh_function[NUMBER_OF_TABS])(uint8_t current_page) = {refresh_threads_tab, refresh_pollers_tab, refresh_cores_tab}; 1555 int color_pair[NUMBER_OF_TABS] = {COLOR_PAIR(2), COLOR_PAIR(2), COLOR_PAIR(2)}; 1556 int i; 1557 uint8_t max_pages = 0; 1558 1559 color_pair[tab] = COLOR_PAIR(1); 1560 1561 for (i = 0; i < NUMBER_OF_TABS; i++) { 1562 wbkgd(g_tab_win[i], color_pair[i]); 1563 } 1564 1565 max_pages = (*refresh_function[tab])(current_page); 1566 refresh(); 1567 1568 for (i = 0; i < NUMBER_OF_TABS; i++) { 1569 wrefresh(g_tab_win[i]); 1570 } 1571 draw_menu_win(); 1572 1573 return max_pages; 1574 } 1575 1576 static void 1577 print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color) 1578 { 1579 int length, temp; 1580 1581 length = strlen(string); 1582 temp = (width - length) / 2; 1583 wattron(win, color); 1584 mvwprintw(win, starty, startx + temp, "%s", string); 1585 wattroff(win, color); 1586 refresh(); 1587 } 1588 1589 static void 1590 print_left(WINDOW *win, int starty, int startx, int width, char *string, chtype color) 1591 { 1592 wattron(win, color); 1593 mvwprintw(win, starty, startx, "%s", string); 1594 wattroff(win, color); 1595 refresh(); 1596 } 1597 1598 static void 1599 apply_filters(enum tabs tab) 1600 { 1601 wclear(g_tabs[tab]); 1602 draw_tabs(tab, g_current_sort_col[tab]); 1603 } 1604 1605 static ITEM ** 1606 draw_filtering_menu(uint8_t position, WINDOW *filter_win, uint8_t tab, MENU **my_menu) 1607 { 1608 const int ADDITIONAL_ELEMENTS = 3; 1609 const int ROW_PADDING = 6; 1610 const int WINDOW_START_X = 1; 1611 const int WINDOW_START_Y = 3; 1612 const int WINDOW_COLUMNS = 2; 1613 struct col_desc *col_desc = g_col_desc[tab]; 1614 ITEM **my_items; 1615 MENU *menu; 1616 int i, elements; 1617 uint8_t len = 0; 1618 1619 for (i = 0; col_desc[i].name != NULL; ++i) { 1620 len = spdk_max(col_desc[i].name_len, len); 1621 } 1622 1623 elements = i; 1624 1625 my_items = (ITEM **)calloc(elements * WINDOW_COLUMNS + ADDITIONAL_ELEMENTS, sizeof(ITEM *)); 1626 if (my_items == NULL) { 1627 fprintf(stderr, "Unable to allocate an item list in draw_filtering_menu.\n"); 1628 return NULL; 1629 } 1630 1631 for (i = 0; i < elements * 2; i++) { 1632 my_items[i] = new_item(col_desc[i / WINDOW_COLUMNS].name, NULL); 1633 i++; 1634 my_items[i] = new_item(col_desc[i / WINDOW_COLUMNS].disabled ? "[ ]" : "[*]", NULL); 1635 } 1636 1637 my_items[i] = new_item(" CLOSE", NULL); 1638 set_item_userptr(my_items[i], apply_filters); 1639 1640 menu = new_menu((ITEM **)my_items); 1641 1642 menu_opts_off(menu, O_SHOWDESC); 1643 set_menu_format(menu, elements + 1, WINDOW_COLUMNS); 1644 1645 set_menu_win(menu, filter_win); 1646 set_menu_sub(menu, derwin(filter_win, elements + 1, len + ROW_PADDING, WINDOW_START_Y, 1647 WINDOW_START_X)); 1648 1649 *my_menu = menu; 1650 1651 post_menu(menu); 1652 refresh(); 1653 wrefresh(filter_win); 1654 1655 for (i = 0; i < position / WINDOW_COLUMNS; i++) { 1656 menu_driver(menu, REQ_DOWN_ITEM); 1657 } 1658 1659 return my_items; 1660 } 1661 1662 static void 1663 delete_filtering_menu(MENU *my_menu, ITEM **my_items, uint8_t elements) 1664 { 1665 int i; 1666 1667 unpost_menu(my_menu); 1668 free_menu(my_menu); 1669 for (i = 0; i < elements * 2 + 2; ++i) { 1670 free_item(my_items[i]); 1671 } 1672 free(my_items); 1673 } 1674 1675 static ITEM ** 1676 refresh_filtering_menu(MENU **my_menu, WINDOW *filter_win, uint8_t tab, ITEM **my_items, 1677 uint8_t elements, uint8_t position) 1678 { 1679 delete_filtering_menu(*my_menu, my_items, elements); 1680 return draw_filtering_menu(position, filter_win, tab, my_menu); 1681 } 1682 1683 static void 1684 filter_columns(uint8_t tab) 1685 { 1686 const int WINDOW_HEADER_LEN = 5; 1687 const int WINDOW_BORDER_LEN = 8; 1688 const int WINDOW_HEADER_END_LINE = 2; 1689 const int WINDOW_COLUMNS = 2; 1690 struct col_desc *col_desc = g_col_desc[tab]; 1691 PANEL *filter_panel; 1692 WINDOW *filter_win; 1693 ITEM **my_items; 1694 MENU *my_menu = NULL; 1695 int i, c, elements; 1696 bool stop_loop = false; 1697 ITEM *cur; 1698 void (*p)(enum tabs tab); 1699 uint8_t current_index, len = 0; 1700 bool disabled[TABS_COL_COUNT]; 1701 1702 for (i = 0; col_desc[i].name != NULL; ++i) { 1703 len = spdk_max(col_desc[i].name_len, len); 1704 } 1705 1706 elements = i; 1707 1708 filter_win = newwin(elements + WINDOW_HEADER_LEN, len + WINDOW_BORDER_LEN, 1709 (g_max_row - elements - 1) / 2, (g_max_col - len) / 2); 1710 assert(filter_win != NULL); 1711 keypad(filter_win, TRUE); 1712 filter_panel = new_panel(filter_win); 1713 assert(filter_panel != NULL); 1714 1715 top_panel(filter_panel); 1716 update_panels(); 1717 doupdate(); 1718 1719 box(filter_win, 0, 0); 1720 1721 print_in_middle(filter_win, 1, 0, len + WINDOW_BORDER_LEN, "Filtering", COLOR_PAIR(3)); 1722 mvwaddch(filter_win, WINDOW_HEADER_END_LINE, 0, ACS_LTEE); 1723 mvwhline(filter_win, WINDOW_HEADER_END_LINE, 1, ACS_HLINE, len + WINDOW_BORDER_LEN - 2); 1724 mvwaddch(filter_win, WINDOW_HEADER_END_LINE, len + WINDOW_BORDER_LEN - 1, ACS_RTEE); 1725 1726 my_items = draw_filtering_menu(0, filter_win, tab, &my_menu); 1727 if (my_items == NULL || my_menu == NULL) { 1728 goto fail; 1729 } 1730 1731 for (int i = 0; i < TABS_COL_COUNT; i++) { 1732 disabled[i] = col_desc[i].disabled; 1733 } 1734 1735 while (!stop_loop) { 1736 c = wgetch(filter_win); 1737 1738 switch (c) { 1739 case KEY_DOWN: 1740 menu_driver(my_menu, REQ_DOWN_ITEM); 1741 break; 1742 case KEY_UP: 1743 menu_driver(my_menu, REQ_UP_ITEM); 1744 break; 1745 case 27: /* ESC */ 1746 case 'q': 1747 for (int i = 0; i < TABS_COL_COUNT; i++) { 1748 cur = current_item(my_menu); 1749 col_desc[i].disabled = disabled[i]; 1750 1751 my_items = refresh_filtering_menu(&my_menu, filter_win, tab, my_items, elements, 1752 item_index(cur) + 1); 1753 if (my_items == NULL || my_menu == NULL) { 1754 goto fail; 1755 } 1756 } 1757 1758 stop_loop = true; 1759 break; 1760 case ' ': /* Space */ 1761 cur = current_item(my_menu); 1762 current_index = item_index(cur) / WINDOW_COLUMNS; 1763 col_desc[current_index].disabled = !col_desc[current_index].disabled; 1764 my_items = refresh_filtering_menu(&my_menu, filter_win, tab, my_items, elements, 1765 item_index(cur) + 1); 1766 if (my_items == NULL || my_menu == NULL) { 1767 goto fail; 1768 } 1769 break; 1770 case 10: /* Enter */ 1771 cur = current_item(my_menu); 1772 current_index = item_index(cur) / WINDOW_COLUMNS; 1773 if (current_index == elements) { 1774 stop_loop = true; 1775 p = item_userptr(cur); 1776 p(tab); 1777 } else { 1778 col_desc[current_index].disabled = !col_desc[current_index].disabled; 1779 my_items = refresh_filtering_menu(&my_menu, filter_win, tab, my_items, elements, 1780 item_index(cur) + 1); 1781 if (my_items == NULL || my_menu == NULL) { 1782 goto fail; 1783 } 1784 } 1785 break; 1786 } 1787 wrefresh(filter_win); 1788 } 1789 1790 delete_filtering_menu(my_menu, my_items, elements); 1791 1792 del_panel(filter_panel); 1793 delwin(filter_win); 1794 1795 wclear(g_menu_win); 1796 draw_menu_win(); 1797 return; 1798 1799 fail: 1800 fprintf(stderr, "Unable to filter the columns due to allocation failure.\n"); 1801 assert(false); 1802 } 1803 1804 static void 1805 sort_type(enum tabs tab, int item_index) 1806 { 1807 g_current_sort_col[tab] = item_index; 1808 wclear(g_tabs[tab]); 1809 draw_tabs(tab, g_current_sort_col[tab]); 1810 } 1811 1812 static void 1813 change_sorting(uint8_t tab) 1814 { 1815 const int WINDOW_HEADER_LEN = 4; 1816 const int WINDOW_BORDER_LEN = 3; 1817 const int WINDOW_START_X = 1; 1818 const int WINDOW_START_Y = 3; 1819 const int WINDOW_HEADER_END_LINE = 2; 1820 PANEL *sort_panel; 1821 WINDOW *sort_win; 1822 ITEM **my_items; 1823 MENU *my_menu; 1824 int i, c, elements; 1825 bool stop_loop = false; 1826 ITEM *cur; 1827 void (*p)(enum tabs tab, int item_index); 1828 uint8_t len = 0; 1829 1830 for (i = 0; g_col_desc[tab][i].name != NULL; ++i) { 1831 len = spdk_max(len, g_col_desc[tab][i].name_len); 1832 } 1833 1834 elements = i; 1835 1836 my_items = (ITEM **)calloc(elements + 1, sizeof(ITEM *)); 1837 if (my_items == NULL) { 1838 fprintf(stderr, "Unable to allocate an item list in change_sorting.\n"); 1839 return; 1840 } 1841 1842 for (i = 0; i < elements; ++i) { 1843 my_items[i] = new_item(g_col_desc[tab][i].name, NULL); 1844 set_item_userptr(my_items[i], sort_type); 1845 } 1846 1847 my_menu = new_menu((ITEM **)my_items); 1848 1849 menu_opts_off(my_menu, O_SHOWDESC); 1850 1851 sort_win = newwin(elements + WINDOW_HEADER_LEN, len + WINDOW_BORDER_LEN, (g_max_row - elements) / 2, 1852 (g_max_col - len) / 2); 1853 assert(sort_win != NULL); 1854 keypad(sort_win, TRUE); 1855 sort_panel = new_panel(sort_win); 1856 assert(sort_panel != NULL); 1857 1858 top_panel(sort_panel); 1859 update_panels(); 1860 doupdate(); 1861 1862 set_menu_win(my_menu, sort_win); 1863 set_menu_sub(my_menu, derwin(sort_win, elements, len + 1, WINDOW_START_Y, WINDOW_START_X)); 1864 box(sort_win, 0, 0); 1865 1866 print_in_middle(sort_win, 1, 0, len + WINDOW_BORDER_LEN, "Sorting", COLOR_PAIR(3)); 1867 mvwaddch(sort_win, WINDOW_HEADER_END_LINE, 0, ACS_LTEE); 1868 mvwhline(sort_win, WINDOW_HEADER_END_LINE, 1, ACS_HLINE, len + 1); 1869 mvwaddch(sort_win, WINDOW_HEADER_END_LINE, len + WINDOW_BORDER_LEN - 1, ACS_RTEE); 1870 1871 post_menu(my_menu); 1872 refresh(); 1873 wrefresh(sort_win); 1874 1875 while (!stop_loop) { 1876 c = wgetch(sort_win); 1877 1878 switch (c) { 1879 case KEY_DOWN: 1880 menu_driver(my_menu, REQ_DOWN_ITEM); 1881 break; 1882 case KEY_UP: 1883 menu_driver(my_menu, REQ_UP_ITEM); 1884 break; 1885 case 27: /* ESC */ 1886 stop_loop = true; 1887 break; 1888 case 10: /* Enter */ 1889 stop_loop = true; 1890 cur = current_item(my_menu); 1891 p = item_userptr(cur); 1892 p(tab, item_index(cur)); 1893 break; 1894 } 1895 wrefresh(sort_win); 1896 } 1897 1898 unpost_menu(my_menu); 1899 free_menu(my_menu); 1900 1901 for (i = 0; i < elements; ++i) { 1902 free_item(my_items[i]); 1903 } 1904 1905 free(my_items); 1906 1907 del_panel(sort_panel); 1908 delwin(sort_win); 1909 1910 wclear(g_menu_win); 1911 draw_menu_win(); 1912 } 1913 1914 static void 1915 change_refresh_rate(void) 1916 { 1917 const int WINDOW_HEADER_END_LINE = 2; 1918 PANEL *refresh_panel; 1919 WINDOW *refresh_win; 1920 int c; 1921 bool stop_loop = false; 1922 uint32_t rr_tmp, refresh_rate = 0; 1923 char refresh_rate_str[MAX_STRING_LEN]; 1924 1925 refresh_win = newwin(RR_WIN_HEIGHT, RR_WIN_WIDTH, (g_max_row - RR_WIN_HEIGHT - 1) / 2, 1926 (g_max_col - RR_WIN_WIDTH) / 2); 1927 assert(refresh_win != NULL); 1928 keypad(refresh_win, TRUE); 1929 refresh_panel = new_panel(refresh_win); 1930 assert(refresh_panel != NULL); 1931 1932 top_panel(refresh_panel); 1933 update_panels(); 1934 doupdate(); 1935 1936 box(refresh_win, 0, 0); 1937 1938 print_in_middle(refresh_win, 1, 0, RR_WIN_WIDTH + 1, "Enter refresh rate value [s]", COLOR_PAIR(3)); 1939 mvwaddch(refresh_win, WINDOW_HEADER_END_LINE, 0, ACS_LTEE); 1940 mvwhline(refresh_win, WINDOW_HEADER_END_LINE, 1, ACS_HLINE, RR_WIN_WIDTH - 2); 1941 mvwaddch(refresh_win, WINDOW_HEADER_END_LINE, RR_WIN_WIDTH, ACS_RTEE); 1942 mvwprintw(refresh_win, WINDOW_HEADER_END_LINE + 1, (RR_WIN_WIDTH - 1) / 2, "%d", refresh_rate); 1943 1944 refresh(); 1945 wrefresh(refresh_win); 1946 1947 while (!stop_loop) { 1948 c = wgetch(refresh_win); 1949 1950 switch (c) { 1951 case '0': 1952 case '1': 1953 case '2': 1954 case '3': 1955 case '4': 1956 case '5': 1957 case '6': 1958 case '7': 1959 case '8': 1960 case '9': 1961 rr_tmp = refresh_rate * 10 + c - '0'; 1962 1963 if (rr_tmp <= RR_MAX_VALUE) { 1964 refresh_rate = rr_tmp; 1965 snprintf(refresh_rate_str, MAX_STRING_LEN - 1, "%d", refresh_rate); 1966 mvwprintw(refresh_win, WINDOW_HEADER_END_LINE + 1, 1967 (RR_WIN_WIDTH - 1 - strlen(refresh_rate_str)) / 2, "%d", refresh_rate); 1968 refresh(); 1969 wrefresh(refresh_win); 1970 } 1971 break; 1972 case KEY_BACKSPACE: 1973 case 127: 1974 case '\b': 1975 refresh_rate = refresh_rate / 10; 1976 snprintf(refresh_rate_str, MAX_STRING_LEN - 1, "%d", refresh_rate); 1977 mvwprintw(refresh_win, WINDOW_HEADER_END_LINE + 1, 1978 (RR_WIN_WIDTH - 1 - strlen(refresh_rate_str) - 2) / 2, " "); 1979 mvwprintw(refresh_win, WINDOW_HEADER_END_LINE + 1, 1980 (RR_WIN_WIDTH - 1 - strlen(refresh_rate_str)) / 2, "%d", refresh_rate); 1981 refresh(); 1982 wrefresh(refresh_win); 1983 break; 1984 case 27: /* ESC */ 1985 case 'q': 1986 stop_loop = true; 1987 break; 1988 case 10: /* Enter */ 1989 g_sleep_time = refresh_rate; 1990 stop_loop = true; 1991 break; 1992 } 1993 wrefresh(refresh_win); 1994 } 1995 1996 del_panel(refresh_panel); 1997 delwin(refresh_win); 1998 } 1999 2000 static void 2001 free_poller_history(void) 2002 { 2003 struct run_counter_history *history, *tmp; 2004 2005 TAILQ_FOREACH_SAFE(history, &g_run_counter_history, link, tmp) { 2006 TAILQ_REMOVE(&g_run_counter_history, history, link); 2007 free(history->poller_name); 2008 free(history); 2009 } 2010 } 2011 2012 static uint64_t 2013 get_position_for_window(uint64_t window_size, uint64_t max_size) 2014 { 2015 /* This function calculates position for pop-up detail window. 2016 * Since horizontal and vertical positions are calculated the same way 2017 * there is no need for separate functions. */ 2018 window_size = spdk_min(window_size, max_size); 2019 2020 return (max_size - window_size) / 2; 2021 } 2022 2023 static void 2024 display_thread(struct rpc_thread_info *thread_info) 2025 { 2026 PANEL *thread_panel; 2027 WINDOW *thread_win; 2028 struct rpc_poller_thread_info *thread; 2029 struct rpc_pollers *pollers; 2030 struct rpc_poller_info *poller; 2031 uint64_t pollers_count, current_row, i, j, time; 2032 int c; 2033 bool stop_loop = false; 2034 char idle_time[MAX_TIME_STR_LEN], busy_time[MAX_TIME_STR_LEN], run_count[MAX_POLLER_COUNT_STR_LEN]; 2035 2036 pthread_mutex_lock(&g_thread_lock); 2037 pollers_count = thread_info->active_pollers_count + 2038 thread_info->timed_pollers_count + 2039 thread_info->paused_pollers_count; 2040 2041 thread_win = newwin(pollers_count + THREAD_WIN_HEIGHT, THREAD_WIN_WIDTH, 2042 get_position_for_window(THREAD_WIN_HEIGHT + pollers_count, g_max_row), 2043 get_position_for_window(THREAD_WIN_WIDTH, g_max_col)); 2044 keypad(thread_win, TRUE); 2045 thread_panel = new_panel(thread_win); 2046 2047 top_panel(thread_panel); 2048 update_panels(); 2049 doupdate(); 2050 2051 box(thread_win, 0, 0); 2052 2053 print_in_middle(thread_win, 1, 0, THREAD_WIN_WIDTH, thread_info->name, 2054 COLOR_PAIR(3)); 2055 mvwhline(thread_win, 2, 1, ACS_HLINE, THREAD_WIN_WIDTH - 2); 2056 mvwaddch(thread_win, 2, THREAD_WIN_WIDTH, ACS_RTEE); 2057 2058 print_left(thread_win, 3, THREAD_WIN_FIRST_COL, THREAD_WIN_WIDTH, 2059 "Core: Idle [us]: Busy [us]:", COLOR_PAIR(5)); 2060 mvwprintw(thread_win, 3, THREAD_WIN_FIRST_COL + 6, "%" PRIu64, 2061 thread_info->core_num); 2062 2063 if (g_interval_data) { 2064 get_time_str(thread_info->idle - thread_info->last_idle, idle_time); 2065 mvwprintw(thread_win, 3, THREAD_WIN_FIRST_COL + 32, idle_time); 2066 get_time_str(thread_info->busy - thread_info->last_busy, busy_time); 2067 mvwprintw(thread_win, 3, THREAD_WIN_FIRST_COL + 54, busy_time); 2068 } else { 2069 get_time_str(thread_info->idle, idle_time); 2070 mvwprintw(thread_win, 3, THREAD_WIN_FIRST_COL + 32, idle_time); 2071 get_time_str(thread_info->busy, busy_time); 2072 mvwprintw(thread_win, 3, THREAD_WIN_FIRST_COL + 54, busy_time); 2073 } 2074 2075 print_left(thread_win, 4, THREAD_WIN_FIRST_COL, THREAD_WIN_WIDTH, 2076 "Active pollers: Timed pollers: Paused pollers:", COLOR_PAIR(5)); 2077 mvwprintw(thread_win, 4, THREAD_WIN_FIRST_COL + 17, "%" PRIu64, 2078 thread_info->active_pollers_count); 2079 mvwprintw(thread_win, 4, THREAD_WIN_FIRST_COL + 36, "%" PRIu64, 2080 thread_info->timed_pollers_count); 2081 mvwprintw(thread_win, 4, THREAD_WIN_FIRST_COL + 59, "%" PRIu64, 2082 thread_info->paused_pollers_count); 2083 2084 mvwhline(thread_win, 5, 1, ACS_HLINE, THREAD_WIN_WIDTH - 2); 2085 2086 print_in_middle(thread_win, 6, 0, THREAD_WIN_WIDTH, 2087 "Pollers Type Total run count Period", COLOR_PAIR(5)); 2088 2089 mvwhline(thread_win, 7, 1, ACS_HLINE, THREAD_WIN_WIDTH - 2); 2090 2091 current_row = 8; 2092 2093 for (i = 0; i < g_pollers_stats.pollers_threads.threads_count; i++) { 2094 thread = &g_pollers_stats.pollers_threads.threads[i]; 2095 if (thread->id == thread_info->id) { 2096 pollers = &thread->active_pollers; 2097 for (j = 0; j < pollers->pollers_count; j++) { 2098 poller = &pollers->pollers[j]; 2099 mvwprintw(thread_win, current_row, THREAD_WIN_FIRST_COL, "%s", poller->name); 2100 mvwprintw(thread_win, current_row, THREAD_WIN_FIRST_COL + 33, "Active"); 2101 snprintf(run_count, MAX_POLLER_COUNT_STR_LEN, "%" PRIu64, poller->run_count); 2102 mvwprintw(thread_win, current_row, THREAD_WIN_FIRST_COL + 41, run_count); 2103 current_row++; 2104 } 2105 pollers = &thread->timed_pollers; 2106 for (j = 0; j < pollers->pollers_count; j++) { 2107 poller = &pollers->pollers[j]; 2108 mvwprintw(thread_win, current_row, THREAD_WIN_FIRST_COL, "%s", poller->name); 2109 mvwprintw(thread_win, current_row, THREAD_WIN_FIRST_COL + 33, "Timed"); 2110 mvwprintw(thread_win, current_row, THREAD_WIN_FIRST_COL + 41, "%" PRIu64, poller->run_count); 2111 time = poller->period_ticks * SPDK_SEC_TO_USEC / g_cores_stats.tick_rate; 2112 mvwprintw(thread_win, current_row, THREAD_WIN_FIRST_COL + 59, "%" PRIu64, time); 2113 current_row++; 2114 } 2115 pollers = &thread->paused_pollers; 2116 for (j = 0; j < pollers->pollers_count; j++) { 2117 poller = &pollers->pollers[j]; 2118 mvwprintw(thread_win, current_row, THREAD_WIN_FIRST_COL, "%s", poller->name); 2119 mvwprintw(thread_win, current_row, THREAD_WIN_FIRST_COL + 33, "Paused"); 2120 mvwprintw(thread_win, current_row, THREAD_WIN_FIRST_COL + 41, "%" PRIu64, poller->run_count); 2121 current_row++; 2122 } 2123 } 2124 } 2125 2126 refresh(); 2127 wrefresh(thread_win); 2128 2129 pthread_mutex_unlock(&g_thread_lock); 2130 while (!stop_loop) { 2131 c = wgetch(thread_win); 2132 2133 switch (c) { 2134 case 27: /* ESC */ 2135 stop_loop = true; 2136 break; 2137 default: 2138 break; 2139 } 2140 } 2141 2142 del_panel(thread_panel); 2143 delwin(thread_win); 2144 } 2145 2146 static void 2147 show_thread(uint8_t current_page) 2148 { 2149 struct rpc_thread_info thread_info; 2150 uint64_t thread_number = current_page * g_max_data_rows + g_selected_row; 2151 2152 pthread_mutex_lock(&g_thread_lock); 2153 assert(thread_number < g_threads_stats.threads.threads_count); 2154 thread_info = g_threads_stats.threads.thread_info[thread_number]; 2155 pthread_mutex_unlock(&g_thread_lock); 2156 2157 display_thread(&thread_info); 2158 } 2159 2160 static void 2161 show_single_thread(uint64_t thread_id) 2162 { 2163 uint64_t i; 2164 struct rpc_thread_info thread_info; 2165 2166 pthread_mutex_lock(&g_thread_lock); 2167 for (i = 0; i < g_threads_stats.threads.threads_count; i++) { 2168 if (g_threads_stats.threads.thread_info[i].id == thread_id) { 2169 thread_info = g_threads_stats.threads.thread_info[i]; 2170 pthread_mutex_unlock(&g_thread_lock); 2171 display_thread(&thread_info); 2172 return; 2173 } 2174 } 2175 pthread_mutex_unlock(&g_thread_lock); 2176 } 2177 2178 static void 2179 show_core(uint8_t current_page) 2180 { 2181 PANEL *core_panel; 2182 WINDOW *core_win; 2183 uint64_t core_number = current_page * g_max_data_rows + g_selected_row; 2184 struct rpc_core_info *core_info[g_cores_stats.cores.cores_count]; 2185 uint64_t threads_count, i, j; 2186 uint64_t thread_id; 2187 uint16_t current_threads_row; 2188 int c; 2189 char core_win_title[25]; 2190 bool stop_loop = false; 2191 char idle_time[MAX_TIME_STR_LEN], busy_time[MAX_TIME_STR_LEN]; 2192 2193 pthread_mutex_lock(&g_thread_lock); 2194 assert(core_number < g_cores_stats.cores.cores_count); 2195 for (i = 0; i < g_cores_stats.cores.cores_count; i++) { 2196 core_info[i] = &g_cores_stats.cores.core[i]; 2197 } 2198 2199 threads_count = g_cores_stats.cores.core[core_number].threads.threads_count; 2200 2201 core_win = newwin(threads_count + CORE_WIN_HEIGHT, CORE_WIN_WIDTH, 2202 get_position_for_window(CORE_WIN_HEIGHT + threads_count, g_max_row), 2203 get_position_for_window(CORE_WIN_WIDTH, g_max_col)); 2204 2205 keypad(core_win, TRUE); 2206 core_panel = new_panel(core_win); 2207 2208 top_panel(core_panel); 2209 update_panels(); 2210 doupdate(); 2211 2212 box(core_win, 0, 0); 2213 snprintf(core_win_title, sizeof(core_win_title), "Core %" PRIu32 " details", 2214 core_info[core_number]->lcore); 2215 print_in_middle(core_win, 1, 0, CORE_WIN_WIDTH, core_win_title, COLOR_PAIR(3)); 2216 2217 mvwaddch(core_win, -1, 0, ACS_LTEE); 2218 mvwhline(core_win, 2, 1, ACS_HLINE, CORE_WIN_WIDTH - 2); 2219 mvwaddch(core_win, 2, CORE_WIN_WIDTH, ACS_RTEE); 2220 print_in_middle(core_win, 3, 0, CORE_WIN_WIDTH - (CORE_WIN_WIDTH / 3), "Frequency:", COLOR_PAIR(5)); 2221 if (core_info[core_number]->core_freq) { 2222 mvwprintw(core_win, 3, CORE_WIN_FIRST_COL + 15, "%" PRIu32, 2223 core_info[core_number]->core_freq); 2224 } else { 2225 mvwprintw(core_win, 3, CORE_WIN_FIRST_COL + 15, "%s", "N/A"); 2226 } 2227 2228 mvwaddch(core_win, -1, 0, ACS_LTEE); 2229 mvwhline(core_win, 4, 1, ACS_HLINE, CORE_WIN_WIDTH - 2); 2230 mvwaddch(core_win, 4, CORE_WIN_WIDTH, ACS_RTEE); 2231 print_left(core_win, 5, 1, CORE_WIN_WIDTH, "Thread count: Idle time:", COLOR_PAIR(5)); 2232 2233 mvwprintw(core_win, 5, CORE_WIN_FIRST_COL, "%" PRIu64, 2234 g_cores_history[core_info[core_number]->lcore].threads_count); 2235 2236 if (g_interval_data == true) { 2237 get_time_str(g_cores_history[core_info[core_number]->lcore].idle, idle_time); 2238 get_time_str(g_cores_history[core_info[core_number]->lcore].busy, busy_time); 2239 } else { 2240 get_time_str(core_info[core_number]->idle, idle_time); 2241 get_time_str(core_info[core_number]->busy, busy_time); 2242 } 2243 mvwprintw(core_win, 5, CORE_WIN_FIRST_COL + 20, idle_time); 2244 mvwhline(core_win, 6, 1, ACS_HLINE, CORE_WIN_WIDTH - 2); 2245 2246 print_left(core_win, 7, 1, CORE_WIN_WIDTH, "Poller count: Busy time:", COLOR_PAIR(5)); 2247 mvwprintw(core_win, 7, CORE_WIN_FIRST_COL, "%" PRIu64, 2248 g_cores_history[core_info[core_number]->lcore].pollers_count); 2249 2250 mvwprintw(core_win, 7, CORE_WIN_FIRST_COL + 20, busy_time); 2251 2252 mvwhline(core_win, 8, 1, ACS_HLINE, CORE_WIN_WIDTH - 2); 2253 print_left(core_win, 9, 1, CORE_WIN_WIDTH, "Threads on this core", COLOR_PAIR(5)); 2254 2255 for (j = 0; j < core_info[core_number]->threads.threads_count; j++) { 2256 mvwprintw(core_win, j + 10, 1, core_info[core_number]->threads.thread[j].name); 2257 } 2258 pthread_mutex_unlock(&g_thread_lock); 2259 2260 refresh(); 2261 wrefresh(core_win); 2262 2263 current_threads_row = 0; 2264 2265 while (!stop_loop) { 2266 pthread_mutex_lock(&g_thread_lock); 2267 for (j = 0; j < core_info[core_number]->threads.threads_count; j++) { 2268 if (j != current_threads_row) { 2269 mvwprintw(core_win, j + 10, 1, core_info[core_number]->threads.thread[j].name); 2270 } else { 2271 print_left(core_win, j + 10, 1, CORE_WIN_WIDTH - 2, 2272 core_info[core_number]->threads.thread[j].name, COLOR_PAIR(2)); 2273 } 2274 } 2275 pthread_mutex_unlock(&g_thread_lock); 2276 2277 wrefresh(core_win); 2278 2279 c = wgetch(core_win); 2280 switch (c) { 2281 case 10: /* ENTER */ 2282 pthread_mutex_lock(&g_thread_lock); 2283 thread_id = core_info[core_number]->threads.thread[current_threads_row].id; 2284 pthread_mutex_unlock(&g_thread_lock); 2285 show_single_thread(thread_id); 2286 break; 2287 case 27: /* ESC */ 2288 stop_loop = true; 2289 break; 2290 case KEY_UP: 2291 if (current_threads_row != 0) { 2292 current_threads_row--; 2293 } 2294 break; 2295 case KEY_DOWN: 2296 pthread_mutex_lock(&g_thread_lock); 2297 if (current_threads_row != core_info[core_number]->threads.threads_count - 1) { 2298 current_threads_row++; 2299 } 2300 pthread_mutex_unlock(&g_thread_lock); 2301 break; 2302 default: 2303 break; 2304 } 2305 } 2306 2307 del_panel(core_panel); 2308 delwin(core_win); 2309 } 2310 2311 static void 2312 show_poller(uint8_t current_page) 2313 { 2314 PANEL *poller_panel; 2315 WINDOW *poller_win; 2316 uint64_t count = 0; 2317 uint64_t *last_run_counter; 2318 uint64_t poller_number = current_page * g_max_data_rows + g_selected_row; 2319 struct rpc_poller_info *pollers[RPC_MAX_POLLERS]; 2320 bool stop_loop = false; 2321 char poller_period[MAX_TIME_STR_LEN]; 2322 int c; 2323 2324 pthread_mutex_lock(&g_thread_lock); 2325 prepare_poller_data(current_page, pollers, &count, current_page); 2326 assert(poller_number < count); 2327 2328 last_run_counter = get_last_run_counter(pollers[poller_number]->name, 2329 pollers[poller_number]->thread_id); 2330 assert(last_run_counter != NULL); 2331 2332 poller_win = newwin(POLLER_WIN_HEIGHT, POLLER_WIN_WIDTH, 2333 get_position_for_window(POLLER_WIN_HEIGHT, g_max_row), 2334 get_position_for_window(POLLER_WIN_WIDTH, g_max_col)); 2335 2336 keypad(poller_win, TRUE); 2337 poller_panel = new_panel(poller_win); 2338 2339 top_panel(poller_panel); 2340 update_panels(); 2341 doupdate(); 2342 2343 box(poller_win, 0, 0); 2344 2345 print_in_middle(poller_win, 1, 0, POLLER_WIN_WIDTH, pollers[poller_number]->name, COLOR_PAIR(3)); 2346 mvwhline(poller_win, 2, 1, ACS_HLINE, POLLER_WIN_WIDTH - 2); 2347 mvwaddch(poller_win, 2, POLLER_WIN_WIDTH, ACS_RTEE); 2348 2349 print_left(poller_win, 3, 2, POLLER_WIN_WIDTH, "Type: On thread:", COLOR_PAIR(5)); 2350 mvwprintw(poller_win, 3, POLLER_WIN_FIRST_COL, 2351 poller_type_str[pollers[poller_number]->type]); 2352 mvwprintw(poller_win, 3, POLLER_WIN_FIRST_COL + 23, pollers[poller_number]->thread_name); 2353 2354 print_left(poller_win, 4, 2, POLLER_WIN_WIDTH, "Run count:", COLOR_PAIR(5)); 2355 2356 if (g_interval_data) { 2357 mvwprintw(poller_win, 4, POLLER_WIN_FIRST_COL, "%" PRIu64, 2358 pollers[poller_number]->run_count - *last_run_counter); 2359 } else { 2360 mvwprintw(poller_win, 4, POLLER_WIN_FIRST_COL, "%" PRIu64, 2361 pollers[poller_number]->run_count); 2362 } 2363 2364 if (pollers[poller_number]->period_ticks != 0) { 2365 print_left(poller_win, 4, 28, POLLER_WIN_WIDTH, "Period:", COLOR_PAIR(5)); 2366 get_time_str(pollers[poller_number]->period_ticks, poller_period); 2367 mvwprintw(poller_win, 4, POLLER_WIN_FIRST_COL + 23, poller_period); 2368 } 2369 mvwhline(poller_win, 5, 1, ACS_HLINE, POLLER_WIN_WIDTH - 2); 2370 print_in_middle(poller_win, 6, 1, POLLER_WIN_WIDTH - 7, "Status:", COLOR_PAIR(5)); 2371 2372 if (pollers[poller_number]->busy_count > 0) { 2373 print_in_middle(poller_win, 6, 1, POLLER_WIN_WIDTH + 6, "Busy", COLOR_PAIR(6)); 2374 } else { 2375 print_in_middle(poller_win, 6, 1, POLLER_WIN_WIDTH + 6, "Idle", COLOR_PAIR(7)); 2376 } 2377 2378 refresh(); 2379 wrefresh(poller_win); 2380 2381 pthread_mutex_unlock(&g_thread_lock); 2382 while (!stop_loop) { 2383 c = wgetch(poller_win); 2384 switch (c) { 2385 case 27: /* ESC */ 2386 stop_loop = true; 2387 break; 2388 default: 2389 break; 2390 } 2391 } 2392 2393 del_panel(poller_panel); 2394 delwin(poller_win); 2395 } 2396 2397 static void 2398 print_bottom_error_message(char *msg) 2399 { 2400 mvprintw(g_max_row - 1, g_max_col - strlen(msg) - 2, msg); 2401 } 2402 2403 static void * 2404 data_thread_routine(void *arg) 2405 { 2406 int rc; 2407 2408 while (1) { 2409 pthread_mutex_lock(&g_thread_lock); 2410 if (g_quit_app) { 2411 pthread_mutex_unlock(&g_thread_lock); 2412 break; 2413 } 2414 pthread_mutex_unlock(&g_thread_lock); 2415 2416 /* Get data from RPC for each object type. 2417 * Start with cores since their number should not change. */ 2418 rc = get_cores_data(); 2419 if (rc) { 2420 print_bottom_error_message("ERROR occurred while getting cores data"); 2421 } 2422 rc = get_thread_data(); 2423 if (rc) { 2424 print_bottom_error_message("ERROR occurred while getting threads data"); 2425 } 2426 2427 rc = get_pollers_data(); 2428 if (rc) { 2429 print_bottom_error_message("ERROR occurred while getting pollers data"); 2430 } 2431 2432 usleep(g_sleep_time * SPDK_SEC_TO_USEC); 2433 } 2434 2435 return NULL; 2436 } 2437 2438 static void 2439 help_window_display(void) 2440 { 2441 PANEL *help_panel; 2442 WINDOW *help_win; 2443 bool stop_loop = false; 2444 int c; 2445 uint64_t row = 1, col = 2, desc_second_row_col = 26, header_footer_col = 0; 2446 2447 help_win = newwin(HELP_WIN_HEIGHT, HELP_WIN_WIDTH, 2448 get_position_for_window(HELP_WIN_HEIGHT, g_max_row), 2449 get_position_for_window(HELP_WIN_WIDTH, g_max_col)); 2450 help_panel = new_panel(help_win); 2451 top_panel(help_panel); 2452 update_panels(); 2453 doupdate(); 2454 2455 box(help_win, 0, 0); 2456 2457 /* Header */ 2458 print_in_middle(help_win, row, header_footer_col, HELP_WIN_WIDTH, "HELP", COLOR_PAIR(3)); 2459 mvwhline(help_win, 2, 1, ACS_HLINE, HELP_WIN_WIDTH - 2); 2460 mvwaddch(help_win, 2, HELP_WIN_WIDTH, ACS_RTEE); 2461 row = 3; 2462 2463 /* Content */ 2464 print_left(help_win, row, col, HELP_WIN_WIDTH, "MENU options", COLOR_PAIR(5)); 2465 print_left(help_win, ++row, ++col, HELP_WIN_WIDTH, "[q] Quit - quit this application", 2466 COLOR_PAIR(10)); 2467 print_left(help_win, ++row, col, HELP_WIN_WIDTH, 2468 "[Tab] Next tab - switch to next tab", COLOR_PAIR(10)); 2469 print_left(help_win, ++row, col, HELP_WIN_WIDTH, 2470 "[1-3] Select tab - switch to THREADS, POLLERS or CORES tab", COLOR_PAIR(10)); 2471 print_left(help_win, ++row, col, HELP_WIN_WIDTH, 2472 "[PgUp] Previous page - scroll up to previous page", COLOR_PAIR(10)); 2473 print_left(help_win, ++row, col, HELP_WIN_WIDTH, 2474 "[PgDown] Next page - scroll down to next page", COLOR_PAIR(10)); 2475 print_left(help_win, ++row, col, HELP_WIN_WIDTH, 2476 "[Up] Arrow key - go to previous data row", COLOR_PAIR(10)); 2477 print_left(help_win, ++row, col, HELP_WIN_WIDTH, 2478 "[Down] Arrow key - go to next data row", COLOR_PAIR(10)); 2479 print_left(help_win, ++row, col, HELP_WIN_WIDTH, 2480 "[c] Columns - choose data columns to display", COLOR_PAIR(10)); 2481 print_left(help_win, ++row, col, HELP_WIN_WIDTH, 2482 "[s] Sorting - change sorting by column", COLOR_PAIR(10)); 2483 print_left(help_win, ++row, col, HELP_WIN_WIDTH, 2484 "[r] Refresh rate - set refresh rate <0, 255> in seconds", COLOR_PAIR(10)); 2485 print_left(help_win, ++row, desc_second_row_col, HELP_WIN_WIDTH, "that value in seconds", 2486 COLOR_PAIR(10)); 2487 print_left(help_win, ++row, col, HELP_WIN_WIDTH, 2488 "[Enter] Item details - show current data row details (Enter to open, Esc to close)", 2489 COLOR_PAIR(10)); 2490 print_left(help_win, ++row, col, HELP_WIN_WIDTH, 2491 "[t] Total/Interval - switch to display data measured from the start of SPDK", COLOR_PAIR(10)); 2492 print_left(help_win, ++row, desc_second_row_col, HELP_WIN_WIDTH, 2493 "application or last refresh", COLOR_PAIR(10)); 2494 print_left(help_win, ++row, col, HELP_WIN_WIDTH, "[h] Help - show this help window", 2495 COLOR_PAIR(10)); 2496 2497 /* Footer */ 2498 mvwhline(help_win, HELP_WIN_HEIGHT - 3, 1, ACS_HLINE, HELP_WIN_WIDTH - 2); 2499 mvwaddch(help_win, HELP_WIN_HEIGHT - 3, HELP_WIN_WIDTH, ACS_RTEE); 2500 2501 print_in_middle(help_win, HELP_WIN_HEIGHT - 2, header_footer_col, HELP_WIN_WIDTH, 2502 "[Esc] Close this window", COLOR_PAIR(10)); 2503 2504 refresh(); 2505 wrefresh(help_win); 2506 2507 while (!stop_loop) { 2508 c = wgetch(help_win); 2509 2510 switch (c) { 2511 case 27: /* ESC */ 2512 stop_loop = true; 2513 break; 2514 default: 2515 break; 2516 } 2517 } 2518 2519 del_panel(help_panel); 2520 delwin(help_win); 2521 2522 } 2523 2524 static void 2525 show_stats(pthread_t *data_thread) 2526 { 2527 const int CURRENT_PAGE_STR_LEN = 50; 2528 long int time_last, time_dif; 2529 struct timespec time_now; 2530 int c; 2531 int max_row, max_col; 2532 uint8_t active_tab = THREADS_TAB; 2533 uint8_t current_page = 0; 2534 uint8_t max_pages = 1; 2535 uint16_t required_size = WINDOW_HEADER + 1; 2536 char current_page_str[CURRENT_PAGE_STR_LEN]; 2537 bool force_refresh = true; 2538 2539 clock_gettime(CLOCK_REALTIME, &time_now); 2540 time_last = time_now.tv_sec; 2541 2542 switch_tab(THREADS_TAB); 2543 2544 while (1) { 2545 /* Check if interface has to be resized (terminal size changed) */ 2546 getmaxyx(stdscr, max_row, max_col); 2547 2548 if (max_row != g_max_row || max_col != g_max_col) { 2549 g_max_row = spdk_max(max_row, required_size); 2550 g_max_col = max_col; 2551 g_data_win_size = g_max_row - required_size + 1; 2552 g_max_data_rows = g_max_row - WINDOW_HEADER; 2553 resize_interface(active_tab); 2554 } 2555 2556 clock_gettime(CLOCK_REALTIME, &time_now); 2557 time_dif = time_now.tv_sec - time_last; 2558 if (time_dif < 0) { 2559 time_dif = g_sleep_time; 2560 } 2561 2562 if (time_dif >= g_sleep_time || force_refresh) { 2563 time_last = time_now.tv_sec; 2564 pthread_mutex_lock(&g_thread_lock); 2565 max_pages = refresh_tab(active_tab, current_page); 2566 pthread_mutex_unlock(&g_thread_lock); 2567 2568 snprintf(current_page_str, CURRENT_PAGE_STR_LEN - 1, "Page: %d/%d", current_page + 1, max_pages); 2569 mvprintw(g_max_row - 1, 1, current_page_str); 2570 2571 refresh(); 2572 } 2573 2574 c = getch(); 2575 if (c == 'q') { 2576 pthread_mutex_lock(&g_thread_lock); 2577 g_quit_app = true; 2578 pthread_mutex_unlock(&g_thread_lock); 2579 break; 2580 } 2581 2582 force_refresh = true; 2583 2584 switch (c) { 2585 case '1': 2586 case '2': 2587 case '3': 2588 active_tab = c - '1'; 2589 current_page = 0; 2590 g_selected_row = 0; 2591 switch_tab(active_tab); 2592 break; 2593 case '\t': 2594 if (active_tab < NUMBER_OF_TABS - 1) { 2595 active_tab++; 2596 } else { 2597 active_tab = THREADS_TAB; 2598 } 2599 g_selected_row = 0; 2600 current_page = 0; 2601 switch_tab(active_tab); 2602 break; 2603 case 's': 2604 change_sorting(active_tab); 2605 break; 2606 case 'c': 2607 filter_columns(active_tab); 2608 break; 2609 case 'r': 2610 change_refresh_rate(); 2611 break; 2612 case 't': 2613 g_interval_data = !g_interval_data; 2614 break; 2615 case KEY_NPAGE: /* PgDown */ 2616 if (current_page + 1 < max_pages) { 2617 current_page++; 2618 } 2619 wclear(g_tabs[active_tab]); 2620 g_selected_row = 0; 2621 draw_tabs(active_tab, g_current_sort_col[active_tab]); 2622 break; 2623 case KEY_PPAGE: /* PgUp */ 2624 if (current_page > 0) { 2625 current_page--; 2626 } 2627 wclear(g_tabs[active_tab]); 2628 g_selected_row = 0; 2629 draw_tabs(active_tab, g_current_sort_col[active_tab]); 2630 break; 2631 case KEY_UP: /* Arrow up */ 2632 if (g_selected_row > 0) { 2633 g_selected_row--; 2634 } 2635 break; 2636 case KEY_DOWN: /* Arrow down */ 2637 if (g_selected_row < g_max_selected_row) { 2638 g_selected_row++; 2639 } 2640 break; 2641 case 10: /* Enter */ 2642 if (active_tab == THREADS_TAB) { 2643 show_thread(current_page); 2644 } else if (active_tab == CORES_TAB) { 2645 show_core(current_page); 2646 } else if (active_tab == POLLERS_TAB) { 2647 show_poller(current_page); 2648 } 2649 break; 2650 case 'h': 2651 help_window_display(); 2652 break; 2653 default: 2654 force_refresh = false; 2655 break; 2656 } 2657 } 2658 2659 pthread_join(*data_thread, NULL); 2660 2661 free_poller_history(); 2662 2663 /* Free memory holding current data states before quitting application */ 2664 free_rpc_threads_stats(&g_threads_stats); 2665 free_rpc_pollers_stats(&g_pollers_stats); 2666 free_rpc_cores_stats(&g_cores_stats); 2667 } 2668 2669 static void 2670 draw_interface(void) 2671 { 2672 int i; 2673 uint16_t required_size = WINDOW_HEADER + 1; 2674 2675 getmaxyx(stdscr, g_max_row, g_max_col); 2676 g_max_row = spdk_max(g_max_row, required_size); 2677 g_data_win_size = g_max_row - required_size; 2678 g_max_data_rows = g_max_row - WINDOW_HEADER; 2679 2680 g_menu_win = newwin(MENU_WIN_HEIGHT, g_max_col, g_max_row - MENU_WIN_HEIGHT - 1, 2681 MENU_WIN_LOCATION_COL); 2682 assert(g_menu_win != NULL); 2683 draw_menu_win(); 2684 2685 for (i = 0; i < NUMBER_OF_TABS; i++) { 2686 g_tab_win[i] = newwin(TAB_WIN_HEIGHT, g_max_col / NUMBER_OF_TABS - TABS_SPACING, 2687 TAB_WIN_LOCATION_ROW, g_max_col / NUMBER_OF_TABS * i + 1); 2688 assert(g_tab_win[i] != NULL); 2689 draw_tab_win(i); 2690 2691 g_tabs[i] = newwin(g_max_row - MENU_WIN_HEIGHT - TAB_WIN_HEIGHT - 2, g_max_col, TABS_LOCATION_ROW, 2692 TABS_LOCATION_COL); 2693 draw_tabs(i, g_current_sort_col[i]); 2694 g_panels[i] = new_panel(g_tabs[i]); 2695 assert(g_panels[i] != NULL); 2696 } 2697 2698 update_panels(); 2699 doupdate(); 2700 } 2701 2702 static void 2703 finish(int sig) 2704 { 2705 /* End ncurses mode */ 2706 endwin(); 2707 spdk_jsonrpc_client_close(g_rpc_client); 2708 exit(0); 2709 } 2710 2711 static void 2712 setup_ncurses(void) 2713 { 2714 clear(); 2715 noecho(); 2716 timeout(1); 2717 curs_set(0); 2718 keypad(stdscr, TRUE); 2719 start_color(); 2720 init_pair(1, COLOR_BLACK, COLOR_GREEN); 2721 init_pair(2, COLOR_BLACK, COLOR_WHITE); 2722 init_pair(3, COLOR_YELLOW, COLOR_BLACK); 2723 init_pair(4, COLOR_BLACK, COLOR_YELLOW); 2724 init_pair(5, COLOR_GREEN, COLOR_BLACK); 2725 init_pair(6, COLOR_RED, COLOR_BLACK); 2726 init_pair(7, COLOR_BLUE, COLOR_BLACK); 2727 init_pair(8, COLOR_RED, COLOR_WHITE); 2728 init_pair(9, COLOR_BLUE, COLOR_WHITE); 2729 init_pair(10, COLOR_WHITE, COLOR_BLACK); 2730 2731 if (has_colors() == FALSE) { 2732 endwin(); 2733 printf("Your terminal does not support color\n"); 2734 exit(1); 2735 } 2736 2737 /* Handle signals to exit gracfully cleaning up ncurses */ 2738 (void) signal(SIGINT, finish); 2739 (void) signal(SIGPIPE, finish); 2740 (void) signal(SIGABRT, finish); 2741 } 2742 2743 static void 2744 usage(const char *program_name) 2745 { 2746 printf("%s [options]", program_name); 2747 printf("\n"); 2748 printf("options:\n"); 2749 printf(" -r <path> RPC connect address (default: /var/tmp/spdk.sock)\n"); 2750 printf(" -h show this usage\n"); 2751 } 2752 2753 static int 2754 wait_init(pthread_t *data_thread) 2755 { 2756 struct spdk_jsonrpc_client_response *json_resp = NULL; 2757 char *uninit_log = "Waiting for SPDK target application to initialize...", 2758 *uninit_error = "Unable to read SPDK application state!"; 2759 int c, max_col, rc = 0; 2760 2761 max_col = getmaxx(stdscr); 2762 print_in_middle(stdscr, FIRST_DATA_ROW, 1, max_col, uninit_log, COLOR_PAIR(5)); 2763 rc = rpc_send_req("framework_wait_init", &json_resp); 2764 if (rc) { 2765 spdk_jsonrpc_client_free_response(json_resp); 2766 2767 while (1) { 2768 print_in_middle(stdscr, FIRST_DATA_ROW, 1, max_col, uninit_error, COLOR_PAIR(8)); 2769 c = getch(); 2770 if (c == 'q') { 2771 return -1; 2772 } 2773 } 2774 } 2775 2776 spdk_jsonrpc_client_free_response(json_resp); 2777 2778 rc = pthread_mutex_init(&g_thread_lock, NULL); 2779 if (rc) { 2780 fprintf(stderr, "mutex lock failed to initialize: %d\n", errno); 2781 return -1; 2782 } 2783 2784 memset(&g_threads_stats, 0, sizeof(g_threads_stats)); 2785 2786 /* This is to get first batch of data for display functions. 2787 * Since data thread makes RPC calls that take more time than 2788 * startup of display functions on main thread, without these 2789 * calls both threads would be subject to a race condition. */ 2790 rc = get_thread_data(); 2791 if (rc) { 2792 return -1; 2793 } 2794 2795 rc = get_pollers_data(); 2796 if (rc) { 2797 return -1; 2798 } 2799 2800 rc = get_cores_data(); 2801 if (rc) { 2802 return -1; 2803 } 2804 2805 rc = pthread_create(data_thread, NULL, &data_thread_routine, NULL); 2806 if (rc) { 2807 fprintf(stderr, "data thread creation failed: %d\n", errno); 2808 return -1; 2809 } 2810 return 0; 2811 } 2812 2813 int main(int argc, char **argv) 2814 { 2815 int op, rc; 2816 char *socket = SPDK_DEFAULT_RPC_ADDR; 2817 pthread_t data_thread; 2818 2819 while ((op = getopt(argc, argv, "r:h")) != -1) { 2820 switch (op) { 2821 case 'r': 2822 socket = optarg; 2823 break; 2824 default: 2825 usage(argv[0]); 2826 return op == 'h' ? 0 : 1; 2827 } 2828 } 2829 2830 g_rpc_client = spdk_jsonrpc_client_connect(socket, AF_UNIX); 2831 if (!g_rpc_client) { 2832 fprintf(stderr, "spdk_jsonrpc_client_connect() failed: %d\n", errno); 2833 return 1; 2834 } 2835 2836 initscr(); 2837 init_str_len(); 2838 setup_ncurses(); 2839 draw_interface(); 2840 2841 rc = wait_init(&data_thread); 2842 if (!rc) { 2843 show_stats(&data_thread); 2844 } 2845 2846 finish(0); 2847 2848 return (0); 2849 } 2850