1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (C) 2020 Intel Corporation.
3 * All rights reserved.
4 */
5
6 #include "spdk/stdinc.h"
7 #include "spdk/jsonrpc.h"
8 #include "spdk/rpc.h"
9 #include "spdk/event.h"
10 #include "spdk/util.h"
11 #include "spdk/env.h"
12
13 #if defined __has_include
14 #if __has_include(<ncurses/panel.h>)
15 #include <ncurses/ncurses.h>
16 #include <ncurses/panel.h>
17 #include <ncurses/menu.h>
18 #else
19 #include <ncurses.h>
20 #include <panel.h>
21 #include <menu.h>
22 #endif
23 #else
24 #include <ncurses.h>
25 #include <panel.h>
26 #include <menu.h>
27 #endif
28
29 #define RPC_MAX_THREADS 1024
30 #define RPC_MAX_POLLERS 1024
31 #define RPC_MAX_CORES 1024
32 #define MAX_THREAD_NAME 128
33 #define MAX_POLLER_NAME 128
34 #define MAX_THREADS 4096
35 #define RR_MAX_VALUE 255
36
37 #define MAX_STRING_LEN 12289 /* 3x 4k monitors + 1 */
38 #define TAB_WIN_HEIGHT 3
39 #define TAB_WIN_LOCATION_ROW 1
40 #define TABS_SPACING 2
41 #define TABS_LOCATION_ROW 4
42 #define TABS_LOCATION_COL 0
43 #define TABS_DATA_START_ROW 3
44 #define TABS_DATA_START_COL 2
45 #define TABS_COL_COUNT 13
46 #define MENU_WIN_HEIGHT 3
47 #define MENU_WIN_SPACING 4
48 #define MENU_WIN_LOCATION_COL 0
49 #define RR_WIN_WIDTH 32
50 #define RR_WIN_HEIGHT 5
51 #define MAX_THREAD_NAME_LEN 26
52 #define MAX_THREAD_COUNT_STR_LEN 10
53 #define MAX_POLLER_NAME_LEN 36
54 #define MAX_POLLER_TYPE_COUNT_STR_LEN 16
55 #define MAX_POLLER_TYPE_STR_LEN 8
56 #define MAX_POLLER_COUNT_STR_LEN 10
57 #define MAX_STATUS_IND_STR_LEN 8
58 #define MAX_POLLER_IND_STR_LEN 28
59 #define MAX_CORE_MASK_STR_LEN 16
60 #define MAX_CORE_STR_LEN 6
61 #define MAX_CORE_FREQ_STR_LEN 13
62 #define MAX_TIME_STR_LEN 12
63 #define MAX_FLOAT_STR_LEN 8
64 #define MAX_POLLER_RUN_COUNT 20
65 #define MAX_PERIOD_STR_LEN 12
66 #define MAX_INTR_LEN 6
67 #define WINDOW_HEADER 12
68 #define FROM_HEX 16
69 #define THREAD_WIN_WIDTH 69
70 #define THREAD_WIN_HEIGHT 9
71 #define THREAD_WIN_FIRST_COL 2
72 #define CORE_WIN_FIRST_COL 16
73 #define CORE_WIN_WIDTH 48
74 #define CORE_WIN_HEIGHT 11
75 #define POLLER_WIN_HEIGHT 8
76 #define POLLER_WIN_WIDTH 64
77 #define POLLER_WIN_FIRST_COL 14
78 #define FIRST_DATA_ROW 7
79 #define HELP_WIN_WIDTH 88
80 #define HELP_WIN_HEIGHT 24
81 #define SCHEDULER_WIN_HEIGHT 7
82 #define SCHEDULER_WIN_FIRST_COL 2
83 #define MAX_SCHEDULER_PERIOD_STR_LEN 10
84
85 enum tabs {
86 THREADS_TAB,
87 POLLERS_TAB,
88 CORES_TAB,
89 NUMBER_OF_TABS,
90 };
91
92 enum column_threads_type {
93 COL_THREADS_NAME,
94 COL_THREADS_CORE,
95 COL_THREADS_ACTIVE_POLLERS,
96 COL_THREADS_TIMED_POLLERS,
97 COL_THREADS_PAUSED_POLLERS,
98 COL_THREADS_IDLE_TIME,
99 COL_THREADS_BUSY_TIME,
100 COL_THREADS_CPU_USAGE,
101 COL_THREADS_STATUS,
102 COL_THREADS_NONE = 255,
103 };
104
105 enum column_pollers_type {
106 COL_POLLERS_NAME,
107 COL_POLLERS_TYPE,
108 COL_POLLERS_THREAD_NAME,
109 COL_POLLERS_RUN_COUNTER,
110 COL_POLLERS_PERIOD,
111 COL_POLLERS_BUSY_COUNT,
112 COL_POLLERS_NONE = 255,
113 };
114
115 enum column_cores_type {
116 COL_CORES_CORE,
117 COL_CORES_THREADS,
118 COL_CORES_POLLERS,
119 COL_CORES_IDLE_TIME,
120 COL_CORES_BUSY_TIME,
121 COL_CORES_BUSY_PCT,
122 COL_CORES_STATUS,
123 COL_CORES_INTR,
124 COL_CORES_SYS_PCT,
125 COL_CORES_IRQ_PCT,
126 COL_CORES_CPU_PCT,
127 COL_CORES_CORE_FREQ,
128 COL_CORES_NONE = 255,
129 };
130
131 enum spdk_poller_type {
132 SPDK_ACTIVE_POLLER,
133 SPDK_TIMED_POLLER,
134 SPDK_PAUSED_POLLER,
135 SPDK_POLLER_TYPES_COUNT,
136 };
137
138 struct col_desc {
139 const char *name;
140 uint8_t name_len;
141 uint8_t max_data_string;
142 bool disabled;
143 };
144
145 struct run_counter_history {
146 uint64_t poller_id;
147 uint64_t thread_id;
148 uint64_t last_run_counter;
149 uint64_t last_busy_counter;
150 TAILQ_ENTRY(run_counter_history) link;
151 };
152
153 uint8_t g_sleep_time = 1;
154 uint16_t g_selected_row;
155 uint16_t g_max_selected_row;
156 uint64_t g_tick_rate;
157 const char *poller_type_str[SPDK_POLLER_TYPES_COUNT] = {"Active", "Timed", "Paused"};
158 const char *g_tab_title[NUMBER_OF_TABS] = {"[1] THREADS", "[2] POLLERS", "[3] CORES"};
159 struct spdk_jsonrpc_client *g_rpc_client;
160 static TAILQ_HEAD(, run_counter_history) g_run_counter_history = TAILQ_HEAD_INITIALIZER(
161 g_run_counter_history);
162 WINDOW *g_menu_win, *g_tab_win[NUMBER_OF_TABS], *g_tabs[NUMBER_OF_TABS];
163 PANEL *g_panels[NUMBER_OF_TABS];
164 uint16_t g_max_row, g_max_col;
165 uint16_t g_data_win_size, g_max_data_rows;
166 uint32_t g_last_threads_count, g_last_pollers_count, g_last_cores_count;
167 uint8_t g_current_sort_col[NUMBER_OF_TABS] = {COL_THREADS_NAME, COL_POLLERS_NAME, COL_CORES_CORE};
168 uint8_t g_current_sort_col2[NUMBER_OF_TABS] = {COL_THREADS_NONE, COL_POLLERS_NONE, COL_CORES_NONE};
169 bool g_interval_data = true;
170 bool g_quit_app = false;
171 pthread_mutex_t g_thread_lock;
172 static struct col_desc g_col_desc[NUMBER_OF_TABS][TABS_COL_COUNT] = {
173 { {.name = "Thread name", .max_data_string = MAX_THREAD_NAME_LEN},
174 {.name = "Core", .max_data_string = MAX_CORE_STR_LEN},
175 {.name = "Active pollers", .max_data_string = MAX_POLLER_TYPE_COUNT_STR_LEN},
176 {.name = "Timed pollers", .max_data_string = MAX_POLLER_TYPE_COUNT_STR_LEN},
177 {.name = "Paused pollers", .max_data_string = MAX_POLLER_TYPE_COUNT_STR_LEN},
178 {.name = "Idle [us]", .max_data_string = MAX_TIME_STR_LEN},
179 {.name = "Busy [us]", .max_data_string = MAX_TIME_STR_LEN},
180 {.name = "CPU %", .max_data_string = MAX_FLOAT_STR_LEN},
181 {.name = "Status", .max_data_string = MAX_STATUS_IND_STR_LEN},
182 {.name = (char *)NULL}
183 },
184 { {.name = "Poller name", .max_data_string = MAX_POLLER_NAME_LEN},
185 {.name = "Type", .max_data_string = MAX_POLLER_TYPE_STR_LEN},
186 {.name = "On thread", .max_data_string = MAX_THREAD_NAME_LEN},
187 {.name = "Run count", .max_data_string = MAX_POLLER_RUN_COUNT},
188 {.name = "Period [us]", .max_data_string = MAX_PERIOD_STR_LEN},
189 {.name = "Status (busy count)", .max_data_string = MAX_POLLER_IND_STR_LEN},
190 {.name = (char *)NULL}
191 },
192 { {.name = "Core", .max_data_string = MAX_CORE_STR_LEN},
193 {.name = "Threads", .max_data_string = MAX_THREAD_COUNT_STR_LEN},
194 {.name = "Pollers", .max_data_string = MAX_POLLER_COUNT_STR_LEN},
195 {.name = "Idle [us]", .max_data_string = MAX_TIME_STR_LEN},
196 {.name = "Busy [us]", .max_data_string = MAX_TIME_STR_LEN},
197 {.name = "Busy %", .max_data_string = MAX_FLOAT_STR_LEN},
198 {.name = "Status", .max_data_string = MAX_STATUS_IND_STR_LEN},
199 {.name = "Intr", .max_data_string = MAX_INTR_LEN},
200 {.name = "Sys %", .max_data_string = MAX_FLOAT_STR_LEN},
201 {.name = "Irq %", .max_data_string = MAX_FLOAT_STR_LEN},
202 {.name = "CPU %", .max_data_string = MAX_FLOAT_STR_LEN},
203 {.name = "Freq [MHz]", .max_data_string = MAX_CORE_FREQ_STR_LEN},
204 {.name = (char *)NULL}
205 }
206 };
207
208 struct rpc_thread_info {
209 char *name;
210 uint64_t id;
211 int core_num;
212 int core_idx;
213 char *cpumask;
214 uint64_t busy;
215 uint64_t last_busy;
216 uint64_t idle;
217 uint64_t last_idle;
218 uint64_t active_pollers_count;
219 uint64_t timed_pollers_count;
220 uint64_t paused_pollers_count;
221 };
222
223 struct rpc_poller_info {
224 char *name;
225 char *state;
226 uint64_t id;
227 uint64_t run_count;
228 uint64_t busy_count;
229 uint64_t period_ticks;
230 enum spdk_poller_type type;
231 char thread_name[MAX_THREAD_NAME];
232 uint64_t thread_id;
233 };
234
235 struct rpc_core_thread_info {
236 char *name;
237 uint64_t id;
238 char *cpumask;
239 uint64_t elapsed;
240 };
241
242 struct rpc_core_threads {
243 uint64_t threads_count;
244 struct rpc_core_thread_info *thread;
245 };
246
247 struct rpc_core_info {
248 uint32_t lcore;
249 uint64_t pollers_count;
250 uint64_t busy;
251 uint64_t idle;
252 uint64_t irq;
253 uint64_t sys;
254 uint64_t usr;
255 uint32_t core_freq;
256 uint64_t last_idle;
257 uint64_t last_busy;
258 uint64_t last_sys;
259 uint64_t last_usr;
260 uint64_t last_irq;
261 bool in_interrupt;
262 struct rpc_core_threads threads;
263 uint64_t tid;
264 };
265
266 struct rpc_scheduler {
267 char *scheduler_name;
268 char *governor_name;
269 uint64_t scheduler_period;
270 };
271
272 struct rpc_thread_info g_threads_info[RPC_MAX_THREADS];
273 struct rpc_poller_info g_pollers_info[RPC_MAX_POLLERS];
274 struct rpc_core_info g_cores_info[RPC_MAX_CORES];
275 struct rpc_scheduler g_scheduler_info;
276
277 static void
init_str_len(void)278 init_str_len(void)
279 {
280 int i, j;
281
282 for (i = 0; i < NUMBER_OF_TABS; i++) {
283 for (j = 0; g_col_desc[i][j].name != NULL; j++) {
284 g_col_desc[i][j].name_len = strlen(g_col_desc[i][j].name);
285 }
286 }
287 }
288
289 static void
free_rpc_threads_stats(struct rpc_thread_info * req)290 free_rpc_threads_stats(struct rpc_thread_info *req)
291 {
292 free(req->name);
293 req->name = NULL;
294 free(req->cpumask);
295 req->cpumask = NULL;
296 }
297
298 static const struct spdk_json_object_decoder rpc_thread_info_decoders[] = {
299 {"name", offsetof(struct rpc_thread_info, name), spdk_json_decode_string},
300 {"id", offsetof(struct rpc_thread_info, id), spdk_json_decode_uint64},
301 {"cpumask", offsetof(struct rpc_thread_info, cpumask), spdk_json_decode_string},
302 {"busy", offsetof(struct rpc_thread_info, busy), spdk_json_decode_uint64},
303 {"idle", offsetof(struct rpc_thread_info, idle), spdk_json_decode_uint64},
304 {"active_pollers_count", offsetof(struct rpc_thread_info, active_pollers_count), spdk_json_decode_uint64},
305 {"timed_pollers_count", offsetof(struct rpc_thread_info, timed_pollers_count), spdk_json_decode_uint64},
306 {"paused_pollers_count", offsetof(struct rpc_thread_info, paused_pollers_count), spdk_json_decode_uint64},
307 };
308
309 static int
rpc_decode_threads_array(struct spdk_json_val * val,struct rpc_thread_info * out,uint64_t * current_threads_count)310 rpc_decode_threads_array(struct spdk_json_val *val, struct rpc_thread_info *out,
311 uint64_t *current_threads_count)
312 {
313 struct spdk_json_val *thread = val;
314 uint64_t i = 0;
315 int rc;
316
317 /* Fetch the beginning of threads array */
318 rc = spdk_json_find_array(thread, "threads", NULL, &thread);
319 if (rc) {
320 printf("Could not fetch threads array from JSON.\n");
321 goto end;
322 }
323
324 for (thread = spdk_json_array_first(thread); thread != NULL; thread = spdk_json_next(thread)) {
325 rc = spdk_json_decode_object(thread, rpc_thread_info_decoders,
326 SPDK_COUNTOF(rpc_thread_info_decoders), &out[i]);
327 if (rc) {
328 printf("Could not decode thread object from JSON.\n");
329 break;
330 }
331
332 i++;
333 }
334
335 end:
336
337 *current_threads_count = i;
338 return rc;
339 }
340
341 static void
free_rpc_poller(struct rpc_poller_info * poller)342 free_rpc_poller(struct rpc_poller_info *poller)
343 {
344 free(poller->name);
345 poller->name = NULL;
346 free(poller->state);
347 poller->state = NULL;
348 }
349
350 static void
free_rpc_core_info(struct rpc_core_info * core_info,size_t size)351 free_rpc_core_info(struct rpc_core_info *core_info, size_t size)
352 {
353 struct rpc_core_threads *threads;
354 struct rpc_core_thread_info *thread;
355 uint64_t i, core_number;
356
357 for (core_number = 0; core_number < size; core_number++) {
358 threads = &core_info[core_number].threads;
359 for (i = 0; i < threads->threads_count; i++) {
360 thread = &threads->thread[i];
361 free(thread->name);
362 free(thread->cpumask);
363 }
364 free(threads->thread);
365 }
366 }
367
368 static const struct spdk_json_object_decoder rpc_pollers_decoders[] = {
369 {"name", offsetof(struct rpc_poller_info, name), spdk_json_decode_string},
370 {"state", offsetof(struct rpc_poller_info, state), spdk_json_decode_string},
371 {"id", offsetof(struct rpc_poller_info, id), spdk_json_decode_uint64},
372 {"run_count", offsetof(struct rpc_poller_info, run_count), spdk_json_decode_uint64},
373 {"busy_count", offsetof(struct rpc_poller_info, busy_count), spdk_json_decode_uint64},
374 {"period_ticks", offsetof(struct rpc_poller_info, period_ticks), spdk_json_decode_uint64, true},
375 };
376
377 static int
rpc_decode_pollers_array(struct spdk_json_val * poller,struct rpc_poller_info * out,uint64_t * poller_count,const char * thread_name,uint64_t thread_name_length,uint64_t thread_id,enum spdk_poller_type poller_type)378 rpc_decode_pollers_array(struct spdk_json_val *poller, struct rpc_poller_info *out,
379 uint64_t *poller_count,
380 const char *thread_name, uint64_t thread_name_length, uint64_t thread_id,
381 enum spdk_poller_type poller_type)
382 {
383 int rc;
384
385 for (poller = spdk_json_array_first(poller); poller != NULL; poller = spdk_json_next(poller)) {
386 out[*poller_count].thread_id = thread_id;
387 memcpy(out[*poller_count].thread_name, thread_name, sizeof(char) * thread_name_length);
388 out[*poller_count].type = poller_type;
389
390 rc = spdk_json_decode_object(poller, rpc_pollers_decoders,
391 SPDK_COUNTOF(rpc_pollers_decoders), &out[*poller_count]);
392 if (rc) {
393 printf("Could not decode poller object from JSON.\n");
394 return rc;
395 }
396
397 (*poller_count)++;
398 if (*poller_count == RPC_MAX_POLLERS) {
399 return -1;
400 }
401 }
402
403 return 0;
404 }
405
406 static const struct spdk_json_object_decoder rpc_thread_pollers_decoders[] = {
407 {"name", offsetof(struct rpc_thread_info, name), spdk_json_decode_string},
408 {"id", offsetof(struct rpc_thread_info, id), spdk_json_decode_uint64},
409 };
410
411 static int
rpc_decode_pollers_threads_array(struct spdk_json_val * val,struct rpc_poller_info * out,uint32_t * num_pollers)412 rpc_decode_pollers_threads_array(struct spdk_json_val *val, struct rpc_poller_info *out,
413 uint32_t *num_pollers)
414 {
415 struct spdk_json_val *thread = val, *poller;
416 /* This is a temporary poller structure to hold thread name and id.
417 * It is filled with data only once per thread change and then
418 * that memory is copied to each poller running on that thread. */
419 struct rpc_thread_info thread_info = {};
420 uint64_t poller_count = 0, i, thread_name_length;
421 int rc;
422 const char *poller_typenames[] = { "active_pollers", "timed_pollers", "paused_pollers" };
423 enum spdk_poller_type poller_types[] = { SPDK_ACTIVE_POLLER, SPDK_TIMED_POLLER, SPDK_PAUSED_POLLER };
424
425 /* Fetch the beginning of threads array */
426 rc = spdk_json_find_array(thread, "threads", NULL, &thread);
427 if (rc) {
428 printf("Could not fetch threads array from JSON.\n");
429 goto end;
430 }
431
432 for (thread = spdk_json_array_first(thread); thread != NULL; thread = spdk_json_next(thread)) {
433 rc = spdk_json_decode_object_relaxed(thread, rpc_thread_pollers_decoders,
434 SPDK_COUNTOF(rpc_thread_pollers_decoders), &thread_info);
435 if (rc) {
436 printf("Could not decode thread info from JSON.\n");
437 goto end;
438 }
439
440 thread_name_length = strlen(thread_info.name);
441
442 for (i = 0; i < SPDK_COUNTOF(poller_types); i++) {
443 /* Find poller array */
444 rc = spdk_json_find(thread, poller_typenames[i], NULL, &poller,
445 SPDK_JSON_VAL_ARRAY_BEGIN);
446 if (rc) {
447 printf("Could not fetch pollers array from JSON.\n");
448 goto end;
449 }
450
451 rc = rpc_decode_pollers_array(poller, out, &poller_count, thread_info.name,
452 thread_name_length,
453 thread_info.id, poller_types[i]);
454 if (rc) {
455 printf("Could not decode the first object in pollers array.\n");
456 goto end;
457 }
458 }
459 }
460
461 *num_pollers = poller_count;
462
463 end:
464 /* Since we rely in spdk_json_object_decode() to free this value
465 * each time we rewrite it, we need to free the last allocation
466 * manually. */
467 free(thread_info.name);
468
469 if (rc) {
470 *num_pollers = 0;
471 for (i = 0; i < poller_count; i++) {
472 free_rpc_poller(&out[i]);
473 }
474 }
475
476 return rc;
477 }
478
479 static const struct spdk_json_object_decoder rpc_core_thread_info_decoders[] = {
480 {"name", offsetof(struct rpc_core_thread_info, name), spdk_json_decode_string},
481 {"id", offsetof(struct rpc_core_thread_info, id), spdk_json_decode_uint64},
482 {"cpumask", offsetof(struct rpc_core_thread_info, cpumask), spdk_json_decode_string},
483 {"elapsed", offsetof(struct rpc_core_thread_info, elapsed), spdk_json_decode_uint64},
484 };
485
486 static int
rpc_decode_core_threads_object(const struct spdk_json_val * val,void * out)487 rpc_decode_core_threads_object(const struct spdk_json_val *val, void *out)
488 {
489 struct rpc_core_thread_info *info = out;
490
491 return spdk_json_decode_object(val, rpc_core_thread_info_decoders,
492 SPDK_COUNTOF(rpc_core_thread_info_decoders), info);
493 }
494
495 #define RPC_THREAD_ENTRY_SIZE (SPDK_COUNTOF(rpc_core_thread_info_decoders) * 2)
496
497 static int
rpc_decode_cores_lw_threads(const struct spdk_json_val * val,void * out)498 rpc_decode_cores_lw_threads(const struct spdk_json_val *val, void *out)
499 {
500 struct rpc_core_threads *threads = out;
501 /* The number of thread entries received from RPC can be calculated using
502 * above define value (each JSON line = key + value, hence '* 2' ) and JSON
503 * 'val' value (-2 is to subtract VAL_OBJECT_BEGIN/END). */
504 uint16_t threads_count = (spdk_json_val_len(val) - 2) / RPC_THREAD_ENTRY_SIZE;
505
506 assert(threads != NULL);
507 threads->thread = calloc(threads_count, sizeof(struct rpc_core_thread_info));
508
509 return spdk_json_decode_array(val, rpc_decode_core_threads_object, threads->thread, threads_count,
510 &threads->threads_count, sizeof(struct rpc_core_thread_info));
511 }
512
513 static const struct spdk_json_object_decoder rpc_core_info_decoders[] = {
514 {"lcore", offsetof(struct rpc_core_info, lcore), spdk_json_decode_uint32},
515 {"busy", offsetof(struct rpc_core_info, busy), spdk_json_decode_uint64},
516 {"idle", offsetof(struct rpc_core_info, idle), spdk_json_decode_uint64},
517 {"irq", offsetof(struct rpc_core_info, irq), spdk_json_decode_uint64},
518 {"sys", offsetof(struct rpc_core_info, sys), spdk_json_decode_uint64},
519 {"usr", offsetof(struct rpc_core_info, usr), spdk_json_decode_uint64},
520 {"core_freq", offsetof(struct rpc_core_info, core_freq), spdk_json_decode_uint32, true},
521 {"in_interrupt", offsetof(struct rpc_core_info, in_interrupt), spdk_json_decode_bool},
522 {"lw_threads", offsetof(struct rpc_core_info, threads), rpc_decode_cores_lw_threads},
523 {"tid", offsetof(struct rpc_core_info, tid), spdk_json_decode_uint64},
524 };
525
526 static int
rpc_decode_core_object(const struct spdk_json_val * val,void * out)527 rpc_decode_core_object(const struct spdk_json_val *val, void *out)
528 {
529 struct rpc_core_info *info = out;
530
531 return spdk_json_decode_object(val, rpc_core_info_decoders,
532 SPDK_COUNTOF(rpc_core_info_decoders), info);
533 }
534
535 static int
rpc_decode_cores_array(struct spdk_json_val * val,struct rpc_core_info * out,uint32_t * current_cores_count)536 rpc_decode_cores_array(struct spdk_json_val *val, struct rpc_core_info *out,
537 uint32_t *current_cores_count)
538 {
539 struct spdk_json_val *core = val;
540 size_t cores_count;
541 int rc;
542
543 /* Fetch the beginning of reactors array. */
544 rc = spdk_json_find_array(core, "reactors", NULL, &core);
545 if (rc) {
546 printf("Could not fetch cores array from JSON.");
547 goto end;
548 }
549
550 rc = spdk_json_decode_array(core, rpc_decode_core_object, out, RPC_MAX_CORES, &cores_count,
551 sizeof(struct rpc_core_info));
552
553 *current_cores_count = (uint32_t)cores_count;
554
555 end:
556 return rc;
557 }
558
559 static void
free_rpc_scheduler(struct rpc_scheduler * req)560 free_rpc_scheduler(struct rpc_scheduler *req)
561 {
562 free(req->scheduler_name);
563 free(req->governor_name);
564 }
565
566 static const struct spdk_json_object_decoder rpc_scheduler_decoders[] = {
567 {"scheduler_name", offsetof(struct rpc_scheduler, scheduler_name), spdk_json_decode_string, true},
568 {"governor_name", offsetof(struct rpc_scheduler, governor_name), spdk_json_decode_string, true},
569 {"scheduler_period", offsetof(struct rpc_scheduler, scheduler_period), spdk_json_decode_uint64},
570 };
571
572 static int
rpc_send_req(char * rpc_name,struct spdk_jsonrpc_client_response ** resp)573 rpc_send_req(char *rpc_name, struct spdk_jsonrpc_client_response **resp)
574 {
575 struct spdk_jsonrpc_client_response *json_resp = NULL;
576 struct spdk_json_write_ctx *w;
577 struct spdk_jsonrpc_client_request *request;
578 int rc;
579
580 request = spdk_jsonrpc_client_create_request();
581 if (request == NULL) {
582 return -ENOMEM;
583 }
584
585 w = spdk_jsonrpc_begin_request(request, 1, rpc_name);
586 spdk_jsonrpc_end_request(request, w);
587 spdk_jsonrpc_client_send_request(g_rpc_client, request);
588
589 do {
590 rc = spdk_jsonrpc_client_poll(g_rpc_client, 1);
591 } while (rc == 0 || rc == -ENOTCONN);
592
593 if (rc <= 0) {
594 return -1;
595 }
596
597 json_resp = spdk_jsonrpc_client_get_response(g_rpc_client);
598 if (json_resp == NULL) {
599 return -1;
600 }
601
602 /* Check for error response */
603 if (json_resp->error != NULL) {
604 spdk_jsonrpc_client_free_response(json_resp);
605 return -1;
606 }
607
608 assert(json_resp->result);
609
610 *resp = json_resp;
611
612 return 0;
613 }
614
615 static uint64_t
get_cpu_usage(uint64_t busy_ticks,uint64_t idle_ticks)616 get_cpu_usage(uint64_t busy_ticks, uint64_t idle_ticks)
617 {
618 if (busy_ticks + idle_ticks > 0) {
619 /* Increase numerator to convert fraction into decimal with
620 * additional precision */
621 return busy_ticks * 10000 / (busy_ticks + idle_ticks);
622 }
623
624 return 0;
625 }
626
627 static int
subsort_threads(enum column_threads_type sort_column,const void * p1,const void * p2)628 subsort_threads(enum column_threads_type sort_column, const void *p1, const void *p2)
629 {
630 const struct rpc_thread_info thread_info1 = *(struct rpc_thread_info *)p1;
631 const struct rpc_thread_info thread_info2 = *(struct rpc_thread_info *)p2;
632 uint64_t count1, count2;
633
634 switch (sort_column) {
635 case COL_THREADS_NAME:
636 return strcmp(thread_info1.name, thread_info2.name);
637 case COL_THREADS_CORE:
638 count2 = thread_info1.core_num;
639 count1 = thread_info2.core_num;
640 break;
641 case COL_THREADS_ACTIVE_POLLERS:
642 count1 = thread_info1.active_pollers_count;
643 count2 = thread_info2.active_pollers_count;
644 break;
645 case COL_THREADS_TIMED_POLLERS:
646 count1 = thread_info1.timed_pollers_count;
647 count2 = thread_info2.timed_pollers_count;
648 break;
649 case COL_THREADS_PAUSED_POLLERS:
650 count1 = thread_info1.paused_pollers_count;
651 count2 = thread_info2.paused_pollers_count;
652 break;
653 case COL_THREADS_IDLE_TIME:
654 if (g_interval_data) {
655 count1 = thread_info1.idle - thread_info1.last_idle;
656 count2 = thread_info2.idle - thread_info2.last_idle;
657 } else {
658 count1 = thread_info1.idle;
659 count2 = thread_info2.idle;
660 }
661 break;
662 case COL_THREADS_BUSY_TIME:
663 if (g_interval_data) {
664 count1 = thread_info1.busy - thread_info1.last_busy;
665 count2 = thread_info2.busy - thread_info2.last_busy;
666 } else {
667 count1 = thread_info1.busy;
668 count2 = thread_info2.busy;
669 }
670 break;
671 case COL_THREADS_CPU_USAGE:
672 count1 = get_cpu_usage(thread_info1.busy - thread_info1.last_busy,
673 g_cores_info[thread_info1.core_idx].busy + g_cores_info[thread_info1.core_idx].idle);
674 count2 = get_cpu_usage(thread_info2.busy - thread_info2.last_busy,
675 g_cores_info[thread_info2.core_idx].busy + g_cores_info[thread_info2.core_idx].idle);
676 break;
677 case COL_THREADS_NONE:
678 default:
679 return 0;
680 }
681
682 if (count2 > count1) {
683 return 1;
684 } else if (count2 < count1) {
685 return -1;
686 } else {
687 return 0;
688 }
689 }
690
691 static int
sort_threads(const void * p1,const void * p2)692 sort_threads(const void *p1, const void *p2)
693 {
694 int res;
695
696 res = subsort_threads(g_current_sort_col[THREADS_TAB], p1, p2);
697 if (res == 0) {
698 res = subsort_threads(g_current_sort_col2[THREADS_TAB], p1, p2);
699 }
700 return res;
701 }
702
703 static void
store_last_counters(uint64_t poller_id,uint64_t thread_id,uint64_t last_run_counter,uint64_t last_busy_counter)704 store_last_counters(uint64_t poller_id, uint64_t thread_id, uint64_t last_run_counter,
705 uint64_t last_busy_counter)
706 {
707 struct run_counter_history *history;
708
709 TAILQ_FOREACH(history, &g_run_counter_history, link) {
710 if ((history->poller_id == poller_id) && (history->thread_id == thread_id)) {
711 history->last_run_counter = last_run_counter;
712 history->last_busy_counter = last_busy_counter;
713 return;
714 }
715 }
716
717 history = calloc(1, sizeof(*history));
718 if (history == NULL) {
719 fprintf(stderr, "Unable to allocate a history object in store_last_counters.\n");
720 return;
721 }
722 history->poller_id = poller_id;
723 history->thread_id = thread_id;
724 history->last_run_counter = last_run_counter;
725 history->last_busy_counter = last_busy_counter;
726
727 TAILQ_INSERT_TAIL(&g_run_counter_history, history, link);
728 }
729
730 static int
get_thread_data(void)731 get_thread_data(void)
732 {
733 struct spdk_jsonrpc_client_response *json_resp = NULL;
734 struct rpc_thread_info thread_info[RPC_MAX_THREADS], *thread;
735 struct rpc_core_info *core_info;
736 uint64_t i, j, k, current_threads_count = 0;
737 int rc = 0;
738
739 rc = rpc_send_req("thread_get_stats", &json_resp);
740 if (rc) {
741 return rc;
742 }
743
744 /* Decode json */
745 memset(thread_info, 0, sizeof(struct rpc_thread_info) * RPC_MAX_THREADS);
746 if (rpc_decode_threads_array(json_resp->result, thread_info, ¤t_threads_count)) {
747 rc = -EINVAL;
748 for (i = 0; i < current_threads_count; i++) {
749 free_rpc_threads_stats(&thread_info[i]);
750 }
751 goto end;
752 }
753
754 pthread_mutex_lock(&g_thread_lock);
755
756 /* This is to free allocated char arrays with old thread names */
757 for (i = 0; i < g_last_threads_count; i++) {
758 free_rpc_threads_stats(&g_threads_info[i]);
759 }
760
761 for (i = 0; i < current_threads_count; i++) {
762 for (j = 0; j < g_last_threads_count; j++) {
763 if (thread_info[i].id == g_threads_info[j].id) {
764 thread_info[i].last_busy = g_threads_info[j].busy;
765 thread_info[i].last_idle = g_threads_info[j].idle;
766 }
767 }
768 }
769 g_last_threads_count = current_threads_count;
770
771 memcpy(g_threads_info, thread_info, sizeof(struct rpc_thread_info) * RPC_MAX_THREADS);
772
773 for (i = 0; i < g_last_threads_count; i++) {
774 g_threads_info[i].core_num = -1;
775 }
776
777 for (i = 0; i < g_last_cores_count; i++) {
778 core_info = &g_cores_info[i];
779
780 for (j = 0; j < core_info->threads.threads_count; j++) {
781 for (k = 0; k < g_last_threads_count; k++) {
782 /* For each thread on current core: check if it's ID also exists
783 * in g_thread_info data structure. If it does then assign current
784 * core's number to that thread, otherwise application state is inconsistent
785 * (e.g. scheduler is moving threads between cores). */
786 thread = &g_threads_info[k];
787 if (thread->id == core_info->threads.thread[j].id) {
788 thread->core_num = core_info->lcore;
789 thread->core_idx = i;
790 break;
791 }
792 }
793 }
794 }
795
796 qsort(g_threads_info, g_last_threads_count, sizeof(struct rpc_thread_info), sort_threads);
797
798 pthread_mutex_unlock(&g_thread_lock);
799
800 end:
801 spdk_jsonrpc_client_free_response(json_resp);
802 return rc;
803 }
804
805 static uint64_t
get_last_run_counter(uint64_t poller_id,uint64_t thread_id)806 get_last_run_counter(uint64_t poller_id, uint64_t thread_id)
807 {
808 struct run_counter_history *history;
809
810 TAILQ_FOREACH(history, &g_run_counter_history, link) {
811 if ((history->poller_id == poller_id) && (history->thread_id == thread_id)) {
812 return history->last_run_counter;
813 }
814 }
815
816 return 0;
817 }
818
819 static uint64_t
get_last_busy_counter(uint64_t poller_id,uint64_t thread_id)820 get_last_busy_counter(uint64_t poller_id, uint64_t thread_id)
821 {
822 struct run_counter_history *history;
823
824 TAILQ_FOREACH(history, &g_run_counter_history, link) {
825 if ((history->poller_id == poller_id) && (history->thread_id == thread_id)) {
826 return history->last_busy_counter;
827 }
828 }
829
830 return 0;
831 }
832
833 static int
subsort_pollers(enum column_pollers_type sort_column,const void * p1,const void * p2)834 subsort_pollers(enum column_pollers_type sort_column, const void *p1, const void *p2)
835 {
836 const struct rpc_poller_info *poller1 = (struct rpc_poller_info *)p1;
837 const struct rpc_poller_info *poller2 = (struct rpc_poller_info *)p2;
838 uint64_t count1, count2;
839 uint64_t last_busy_counter1, last_busy_counter2;
840
841 switch (sort_column) {
842 case COL_POLLERS_NAME:
843 return strcmp(poller1->name, poller2->name);
844 case COL_POLLERS_TYPE:
845 return poller1->type - poller2->type;
846 case COL_POLLERS_THREAD_NAME:
847 return strcmp(poller1->thread_name, poller2->thread_name);
848 case COL_POLLERS_RUN_COUNTER:
849 if (g_interval_data) {
850 count1 = poller1->run_count - get_last_run_counter(poller1->id, poller1->thread_id);
851 count2 = poller2->run_count - get_last_run_counter(poller2->id, poller2->thread_id);
852 } else {
853 count1 = poller1->run_count;
854 count2 = poller2->run_count;
855 }
856 break;
857 case COL_POLLERS_PERIOD:
858 count1 = poller1->period_ticks;
859 count2 = poller2->period_ticks;
860 break;
861 case COL_POLLERS_BUSY_COUNT:
862 count1 = poller1->busy_count;
863 count2 = poller2->busy_count;
864 if (g_interval_data) {
865 last_busy_counter1 = get_last_busy_counter(poller1->id, poller1->thread_id);
866 last_busy_counter2 = get_last_busy_counter(poller2->id, poller2->thread_id);
867 if (count1 > last_busy_counter1) {
868 count1 -= last_busy_counter1;
869 }
870 if (count2 > last_busy_counter2) {
871 count2 -= last_busy_counter2;
872 }
873 }
874 break;
875 case COL_POLLERS_NONE:
876 default:
877 return 0;
878 }
879
880 if (count2 > count1) {
881 return 1;
882 } else if (count2 < count1) {
883 return -1;
884 } else {
885 return 0;
886 }
887 }
888
889 static int
sort_pollers(const void * p1,const void * p2)890 sort_pollers(const void *p1, const void *p2)
891 {
892 int rc;
893
894 rc = subsort_pollers(g_current_sort_col[POLLERS_TAB], p1, p2);
895 if (rc == 0) {
896 rc = subsort_pollers(g_current_sort_col2[POLLERS_TAB], p1, p2);
897 }
898 return rc;
899 }
900
901 static int
get_pollers_data(void)902 get_pollers_data(void)
903 {
904 struct spdk_jsonrpc_client_response *json_resp = NULL;
905 int rc = 0;
906 uint64_t i = 0;
907 uint32_t current_pollers_count;
908 struct rpc_poller_info pollers_info[RPC_MAX_POLLERS];
909
910 rc = rpc_send_req("thread_get_pollers", &json_resp);
911 if (rc) {
912 return rc;
913 }
914
915 /* Decode json */
916 memset(&pollers_info, 0, sizeof(pollers_info));
917 if (rpc_decode_pollers_threads_array(json_resp->result, pollers_info, ¤t_pollers_count)) {
918 rc = -EINVAL;
919 for (i = 0; i < current_pollers_count; i++) {
920 free_rpc_poller(&pollers_info[i]);
921 }
922 goto end;
923 }
924
925 pthread_mutex_lock(&g_thread_lock);
926
927 /* Save last run counter of each poller before updating g_pollers_stats. */
928 for (i = 0; i < g_last_pollers_count; i++) {
929 store_last_counters(g_pollers_info[i].id, g_pollers_info[i].thread_id,
930 g_pollers_info[i].run_count, g_pollers_info[i].busy_count);
931 }
932
933 /* Free old pollers values before allocating memory for new ones */
934 for (i = 0; i < g_last_pollers_count; i++) {
935 free_rpc_poller(&g_pollers_info[i]);
936 }
937
938 g_last_pollers_count = current_pollers_count;
939
940 qsort(&pollers_info, g_last_pollers_count, sizeof(struct rpc_poller_info), sort_pollers);
941
942 memcpy(&g_pollers_info, &pollers_info, sizeof(struct rpc_poller_info) * g_last_pollers_count);
943
944 pthread_mutex_unlock(&g_thread_lock);
945
946 end:
947 spdk_jsonrpc_client_free_response(json_resp);
948 return rc;
949 }
950
951 static int
subsort_cores(enum column_cores_type sort_column,const void * p1,const void * p2)952 subsort_cores(enum column_cores_type sort_column, const void *p1, const void *p2)
953 {
954 const struct rpc_core_info core_info1 = *(struct rpc_core_info *)p1;
955 const struct rpc_core_info core_info2 = *(struct rpc_core_info *)p2;
956 uint64_t count1, count2;
957
958 switch (sort_column) {
959 case COL_CORES_CORE:
960 count1 = core_info2.lcore;
961 count2 = core_info1.lcore;
962 break;
963 case COL_CORES_THREADS:
964 count1 = core_info1.threads.threads_count;
965 count2 = core_info2.threads.threads_count;
966 break;
967 case COL_CORES_POLLERS:
968 count1 = core_info1.pollers_count;
969 count2 = core_info2.pollers_count;
970 break;
971 case COL_CORES_IDLE_TIME:
972 if (g_interval_data) {
973 count1 = core_info1.last_idle - core_info1.idle;
974 count2 = core_info2.last_idle - core_info2.idle;
975 } else {
976 count1 = core_info1.idle;
977 count2 = core_info2.idle;
978 }
979 break;
980 case COL_CORES_BUSY_TIME:
981 if (g_interval_data) {
982 count1 = core_info1.last_busy - core_info1.busy;
983 count2 = core_info2.last_busy - core_info2.busy;
984 } else {
985 count1 = core_info1.busy;
986 count2 = core_info2.busy;
987 }
988 break;
989 case COL_CORES_CORE_FREQ:
990 count1 = core_info1.core_freq;
991 count2 = core_info2.core_freq;
992 break;
993 case COL_CORES_INTR:
994 count1 = core_info1.in_interrupt;
995 count2 = core_info2.in_interrupt;
996 break;
997 case COL_CORES_BUSY_PCT:
998 count1 = get_cpu_usage(core_info1.last_busy - core_info1.busy,
999 core_info1.last_idle - core_info1.idle);
1000 count2 = get_cpu_usage(core_info2.last_busy - core_info2.busy,
1001 core_info2.last_idle - core_info2.idle);
1002 break;
1003 case COL_CORES_NONE:
1004 default:
1005 return 0;
1006 }
1007
1008 if (count2 > count1) {
1009 return 1;
1010 } else if (count2 < count1) {
1011 return -1;
1012 } else {
1013 return 0;
1014 }
1015 }
1016
1017 static int
sort_cores(const void * p1,const void * p2)1018 sort_cores(const void *p1, const void *p2)
1019 {
1020 int rc;
1021
1022 rc = subsort_cores(g_current_sort_col[CORES_TAB], p1, p2);
1023 if (rc == 0) {
1024 return subsort_cores(g_current_sort_col[CORES_TAB], p1, p2);
1025 }
1026 return rc;
1027 }
1028
1029 static int
get_cores_data(void)1030 get_cores_data(void)
1031 {
1032 struct spdk_jsonrpc_client_response *json_resp = NULL;
1033 struct rpc_core_info *core_info;
1034 uint64_t i, j, k;
1035 uint32_t current_cores_count;
1036 struct rpc_core_info cores_info[RPC_MAX_CORES];
1037 int rc = 0;
1038
1039 rc = rpc_send_req("framework_get_reactors", &json_resp);
1040 if (rc) {
1041 return rc;
1042 }
1043
1044 /* Decode json */
1045 memset(cores_info, 0, sizeof(struct rpc_core_info) * RPC_MAX_CORES);
1046 if (rpc_decode_cores_array(json_resp->result, cores_info, ¤t_cores_count)) {
1047 rc = -EINVAL;
1048 goto end;
1049 }
1050
1051 pthread_mutex_lock(&g_thread_lock);
1052 for (i = 0; i < current_cores_count; i++) {
1053 for (j = 0; j < g_last_cores_count; j++) {
1054 if (cores_info[i].lcore == g_cores_info[j].lcore) {
1055 cores_info[i].last_busy = g_cores_info[j].busy;
1056 cores_info[i].last_idle = g_cores_info[j].idle;
1057 cores_info[i].last_irq = g_cores_info[j].irq;
1058 cores_info[i].last_sys = g_cores_info[j].sys;
1059 cores_info[i].last_usr = g_cores_info[j].usr;
1060 }
1061 }
1062 }
1063
1064 /* Free old cores values before allocating memory for new ones */
1065 free_rpc_core_info(g_cores_info, current_cores_count);
1066 memcpy(g_cores_info, cores_info, sizeof(struct rpc_core_info) * current_cores_count);
1067
1068 for (i = 0; i < g_last_cores_count; i++) {
1069 core_info = &g_cores_info[i];
1070
1071 core_info->threads.thread = cores_info[i].threads.thread;
1072
1073 for (j = 0; j < core_info->threads.threads_count; j++) {
1074 memcpy(&core_info->threads.thread[j], &cores_info[i].threads.thread[j],
1075 sizeof(struct rpc_core_thread_info));
1076 for (k = 0; k < g_last_threads_count; k++) {
1077 if (core_info->threads.thread[j].id == g_threads_info[k].id) {
1078 g_threads_info[k].core_num = core_info->lcore;
1079 /* Do not consider threads which changed cores when issuing
1080 * RPCs to get_core_data and get_thread_data and threads
1081 * not currently assigned to this core. */
1082 core_info->pollers_count += g_threads_info[k].active_pollers_count +
1083 g_threads_info[k].timed_pollers_count +
1084 g_threads_info[k].paused_pollers_count;
1085 }
1086 }
1087 }
1088 }
1089
1090 g_last_cores_count = current_cores_count;
1091
1092 qsort(&g_cores_info, g_last_cores_count, sizeof(struct rpc_core_info), sort_cores);
1093
1094 end:
1095 pthread_mutex_unlock(&g_thread_lock);
1096 spdk_jsonrpc_client_free_response(json_resp);
1097 return rc;
1098 }
1099
1100 static int
get_scheduler_data(void)1101 get_scheduler_data(void)
1102 {
1103 struct spdk_jsonrpc_client_response *json_resp = NULL;
1104 struct rpc_scheduler scheduler_info;
1105 int rc = 0;
1106
1107 rc = rpc_send_req("framework_get_scheduler", &json_resp);
1108 if (rc) {
1109 return rc;
1110 }
1111
1112 memset(&scheduler_info, 0, sizeof(scheduler_info));
1113 if (spdk_json_decode_object_relaxed(json_resp->result, rpc_scheduler_decoders,
1114 SPDK_COUNTOF(rpc_scheduler_decoders), &scheduler_info)) {
1115 rc = -EINVAL;
1116 } else {
1117 pthread_mutex_lock(&g_thread_lock);
1118
1119 free_rpc_scheduler(&g_scheduler_info);
1120
1121 memcpy(&g_scheduler_info, &scheduler_info, sizeof(struct rpc_scheduler));
1122 pthread_mutex_unlock(&g_thread_lock);
1123 }
1124
1125 spdk_jsonrpc_client_free_response(json_resp);
1126 return rc;
1127 }
1128
1129 enum str_alignment {
1130 ALIGN_LEFT,
1131 ALIGN_RIGHT,
1132 };
1133
1134 static void
print_max_len(WINDOW * win,int row,uint16_t col,uint16_t max_len,enum str_alignment alignment,const char * string)1135 print_max_len(WINDOW *win, int row, uint16_t col, uint16_t max_len, enum str_alignment alignment,
1136 const char *string)
1137 {
1138 const char dots[] = "...";
1139 int DOTS_STR_LEN = sizeof(dots) / sizeof(dots[0]);
1140 char tmp_str[MAX_STRING_LEN];
1141 int len, max_col, max_str, cmp_len;
1142 int max_row;
1143
1144 len = strlen(string);
1145 getmaxyx(win, max_row, max_col);
1146
1147 if (row > max_row) {
1148 /* We are in a process of resizing and this may happen */
1149 return;
1150 }
1151
1152 if (max_len != 0 && col + max_len < max_col) {
1153 max_col = col + max_len;
1154 }
1155
1156 max_str = max_col - col;
1157
1158 if (max_str <= DOTS_STR_LEN + 1) {
1159 /* No space to print anything, but we have to let a user know about it */
1160 mvwprintw(win, row, max_col - DOTS_STR_LEN - 1, "...");
1161 wnoutrefresh(win);
1162 return;
1163 }
1164
1165 if (max_len) {
1166 if (alignment == ALIGN_LEFT) {
1167 snprintf(tmp_str, max_str, "%s%*c", string, max_len - len - 1, ' ');
1168 } else {
1169 snprintf(tmp_str, max_str, "%*c%s", max_len - len - 1, ' ', string);
1170 }
1171 cmp_len = max_len - 1;
1172 } else {
1173 snprintf(tmp_str, max_str, "%s", string);
1174 cmp_len = len;
1175 }
1176
1177 if (col + cmp_len > max_col - 1) {
1178 snprintf(&tmp_str[max_str - DOTS_STR_LEN - 2], DOTS_STR_LEN, "%s", dots);
1179 }
1180
1181 mvwprintw(win, row, col, "%s", tmp_str);
1182
1183 wnoutrefresh(win);
1184 }
1185
1186 static void
draw_menu_win(void)1187 draw_menu_win(void)
1188 {
1189 wbkgd(g_menu_win, COLOR_PAIR(2));
1190 box(g_menu_win, 0, 0);
1191 print_max_len(g_menu_win, 1, 1, 0, ALIGN_LEFT,
1192 " [q] Quit | [1-3][Tab] Switch tab | [PgUp] Previous page | [PgDown] Next page | [Enter] Item details | [h] Help");
1193 }
1194
1195 static void
draw_tab_win(enum tabs tab)1196 draw_tab_win(enum tabs tab)
1197 {
1198 uint16_t col;
1199 uint8_t white_spaces = TABS_SPACING * NUMBER_OF_TABS;
1200
1201 wbkgd(g_tab_win[tab], COLOR_PAIR(10));
1202 box(g_tab_win[tab], 0, 0);
1203
1204 col = ((g_max_col - white_spaces) / NUMBER_OF_TABS / 2) - (strlen(g_tab_title[tab]) / 2) -
1205 TABS_SPACING;
1206 print_max_len(g_tab_win[tab], 1, col, 0, ALIGN_LEFT, g_tab_title[tab]);
1207 }
1208
1209 static void
draw_tabs(enum tabs tab_index,uint8_t sort_col,uint8_t sort_col2)1210 draw_tabs(enum tabs tab_index, uint8_t sort_col, uint8_t sort_col2)
1211 {
1212 struct col_desc *col_desc = g_col_desc[tab_index];
1213 WINDOW *tab = g_tabs[tab_index];
1214 int i, j;
1215 uint16_t offset, draw_offset;
1216 uint16_t tab_height = g_max_row - MENU_WIN_HEIGHT - TAB_WIN_HEIGHT - 3;
1217
1218 for (i = 0; col_desc[i].name != NULL; i++) {
1219 if (col_desc[i].disabled) {
1220 continue;
1221 }
1222
1223 offset = 1;
1224 for (j = i; j != 0; j--) {
1225 if (!col_desc[j - 1].disabled) {
1226 offset += col_desc[j - 1].max_data_string;
1227 offset += col_desc[j - 1].name_len % 2 + 1;
1228 }
1229 }
1230
1231 draw_offset = offset + (col_desc[i].max_data_string / 2) - (col_desc[i].name_len / 2);
1232
1233 if (i == sort_col) {
1234 wattron(tab, COLOR_PAIR(4));
1235 print_max_len(tab, 1, draw_offset, 0, ALIGN_LEFT, col_desc[i].name);
1236 wattroff(tab, COLOR_PAIR(4));
1237 } else if (i == sort_col2) {
1238 wattron(tab, COLOR_PAIR(3));
1239 print_max_len(tab, 1, draw_offset, 0, ALIGN_LEFT, col_desc[i].name);
1240 wattroff(tab, COLOR_PAIR(3));
1241 } else {
1242 print_max_len(tab, 1, draw_offset, 0, ALIGN_LEFT, col_desc[i].name);
1243 }
1244
1245 if (offset != 1) {
1246 print_max_len(tab, 1, offset - 1, 0, ALIGN_LEFT, "|");
1247 }
1248 }
1249
1250 print_max_len(tab, 2, 1, 0, ALIGN_LEFT, ""); /* Move to next line */
1251 whline(tab, ACS_HLINE, g_max_col - 2);
1252
1253 /* Border lines */
1254 mvwhline(tab, 0, 1, ACS_HLINE, g_max_col - 2);
1255 mvwhline(tab, tab_height, 1, ACS_HLINE, g_max_col - 2);
1256
1257 wrefresh(tab);
1258 }
1259
1260 static void
resize_interface(enum tabs tab)1261 resize_interface(enum tabs tab)
1262 {
1263 int i;
1264
1265 clear();
1266 wclear(g_menu_win);
1267 mvwin(g_menu_win, g_max_row - MENU_WIN_SPACING, MENU_WIN_LOCATION_COL);
1268 wresize(g_menu_win, MENU_WIN_HEIGHT, g_max_col);
1269 draw_menu_win();
1270
1271 for (i = 0; i < NUMBER_OF_TABS; i++) {
1272 wclear(g_tabs[i]);
1273 wresize(g_tabs[i], g_max_row - MENU_WIN_HEIGHT - TAB_WIN_HEIGHT - 2, g_max_col);
1274 mvwin(g_tabs[i], TABS_LOCATION_ROW, TABS_LOCATION_COL);
1275 }
1276
1277 draw_tabs(tab, g_current_sort_col[tab], g_current_sort_col2[tab]);
1278
1279 for (i = 0; i < NUMBER_OF_TABS; i++) {
1280 wclear(g_tab_win[i]);
1281 wresize(g_tab_win[i], TAB_WIN_HEIGHT,
1282 (g_max_col - (TABS_SPACING * NUMBER_OF_TABS)) / NUMBER_OF_TABS);
1283 mvwin(g_tab_win[i], TAB_WIN_LOCATION_ROW, 1 + (g_max_col / NUMBER_OF_TABS) * i);
1284 draw_tab_win(i);
1285 }
1286
1287 update_panels();
1288 doupdate();
1289 }
1290
1291 static void
switch_tab(enum tabs tab)1292 switch_tab(enum tabs tab)
1293 {
1294 wclear(g_tabs[tab]);
1295 draw_tabs(tab, g_current_sort_col[tab], g_current_sort_col2[tab]);
1296 top_panel(g_panels[tab]);
1297 update_panels();
1298 doupdate();
1299 }
1300
1301 static void
get_time_str(uint64_t ticks,char * time_str)1302 get_time_str(uint64_t ticks, char *time_str)
1303 {
1304 uint64_t time;
1305
1306 time = ticks * SPDK_SEC_TO_USEC / g_tick_rate;
1307 snprintf(time_str, MAX_TIME_STR_LEN, "%" PRIu64, time);
1308 }
1309
1310 static void
draw_row_background(uint8_t item_index,uint8_t tab)1311 draw_row_background(uint8_t item_index, uint8_t tab)
1312 {
1313 int k;
1314
1315 if (item_index == g_selected_row) {
1316 wattron(g_tabs[tab], COLOR_PAIR(2));
1317 }
1318 for (k = 1; k < g_max_col - 1; k++) {
1319 mvwprintw(g_tabs[tab], TABS_DATA_START_ROW + item_index, k, " ");
1320 }
1321 }
1322
1323 static void
get_cpu_usage_str(uint64_t busy_ticks,uint64_t total_ticks,char * cpu_str)1324 get_cpu_usage_str(uint64_t busy_ticks, uint64_t total_ticks, char *cpu_str)
1325 {
1326 if (total_ticks > 0) {
1327 snprintf(cpu_str, MAX_FLOAT_STR_LEN, "%.2f",
1328 (double)(busy_ticks) * 100 / (double)(total_ticks));
1329 } else {
1330 cpu_str[0] = '\0';
1331 }
1332 }
1333
1334 static void
draw_thread_tab_row(uint64_t current_row,uint8_t item_index)1335 draw_thread_tab_row(uint64_t current_row, uint8_t item_index)
1336 {
1337 struct col_desc *col_desc = g_col_desc[THREADS_TAB];
1338 uint16_t col = TABS_DATA_START_COL;
1339 int core_idx, color_attr = COLOR_PAIR(6);
1340 char pollers_number[MAX_POLLER_TYPE_COUNT_STR_LEN], idle_time[MAX_TIME_STR_LEN],
1341 busy_time[MAX_TIME_STR_LEN], core_str[MAX_CORE_MASK_STR_LEN],
1342 cpu_usage[MAX_FLOAT_STR_LEN], *status_str;
1343
1344 if (!col_desc[COL_THREADS_NAME].disabled) {
1345 print_max_len(g_tabs[THREADS_TAB], TABS_DATA_START_ROW + item_index, col,
1346 col_desc[COL_THREADS_NAME].max_data_string, ALIGN_LEFT, g_threads_info[current_row].name);
1347 col += col_desc[COL_THREADS_NAME].max_data_string;
1348 }
1349
1350 if (!col_desc[COL_THREADS_CORE].disabled) {
1351 snprintf(core_str, MAX_CORE_STR_LEN, "%d", g_threads_info[current_row].core_num);
1352 print_max_len(g_tabs[THREADS_TAB], TABS_DATA_START_ROW + item_index,
1353 col, col_desc[COL_THREADS_CORE].max_data_string, ALIGN_RIGHT, core_str);
1354 col += col_desc[COL_THREADS_CORE].max_data_string + 2;
1355 }
1356
1357 if (!col_desc[COL_THREADS_ACTIVE_POLLERS].disabled) {
1358 snprintf(pollers_number, MAX_POLLER_TYPE_COUNT_STR_LEN, "%ld",
1359 g_threads_info[current_row].active_pollers_count);
1360 print_max_len(g_tabs[THREADS_TAB], TABS_DATA_START_ROW + item_index,
1361 col + (col_desc[COL_THREADS_ACTIVE_POLLERS].name_len / 2),
1362 col_desc[COL_THREADS_ACTIVE_POLLERS].max_data_string, ALIGN_LEFT, pollers_number);
1363 col += col_desc[COL_THREADS_ACTIVE_POLLERS].max_data_string + 2;
1364 }
1365
1366 if (!col_desc[COL_THREADS_TIMED_POLLERS].disabled) {
1367 snprintf(pollers_number, MAX_POLLER_TYPE_COUNT_STR_LEN, "%ld",
1368 g_threads_info[current_row].timed_pollers_count);
1369 print_max_len(g_tabs[THREADS_TAB], TABS_DATA_START_ROW + item_index,
1370 col + (col_desc[COL_THREADS_TIMED_POLLERS].name_len / 2),
1371 col_desc[COL_THREADS_TIMED_POLLERS].max_data_string, ALIGN_LEFT, pollers_number);
1372 col += col_desc[COL_THREADS_TIMED_POLLERS].max_data_string + 1;
1373 }
1374
1375 if (!col_desc[COL_THREADS_PAUSED_POLLERS].disabled) {
1376 snprintf(pollers_number, MAX_POLLER_TYPE_COUNT_STR_LEN, "%ld",
1377 g_threads_info[current_row].paused_pollers_count);
1378 print_max_len(g_tabs[THREADS_TAB], TABS_DATA_START_ROW + item_index,
1379 col + (col_desc[COL_THREADS_PAUSED_POLLERS].name_len / 2),
1380 col_desc[COL_THREADS_PAUSED_POLLERS].max_data_string, ALIGN_LEFT, pollers_number);
1381 col += col_desc[COL_THREADS_PAUSED_POLLERS].max_data_string + 2;
1382 }
1383
1384 uint64_t idle_period = g_threads_info[current_row].idle - g_threads_info[current_row].last_idle;
1385 uint64_t busy_period = g_threads_info[current_row].busy - g_threads_info[current_row].last_busy;
1386 if (!col_desc[COL_THREADS_IDLE_TIME].disabled) {
1387 if (g_interval_data == true) {
1388 get_time_str(idle_period, idle_time);
1389 } else {
1390 get_time_str(g_threads_info[current_row].idle, idle_time);
1391 }
1392 print_max_len(g_tabs[THREADS_TAB], TABS_DATA_START_ROW + item_index, col,
1393 col_desc[COL_THREADS_IDLE_TIME].max_data_string, ALIGN_RIGHT, idle_time);
1394 col += col_desc[COL_THREADS_IDLE_TIME].max_data_string;
1395 }
1396
1397 if (!col_desc[COL_THREADS_BUSY_TIME].disabled) {
1398 if (g_interval_data == true) {
1399 get_time_str(busy_period, busy_time);
1400 } else {
1401 get_time_str(g_threads_info[current_row].busy, busy_time);
1402 }
1403 print_max_len(g_tabs[THREADS_TAB], TABS_DATA_START_ROW + item_index, col,
1404 col_desc[COL_THREADS_BUSY_TIME].max_data_string, ALIGN_RIGHT, busy_time);
1405 col += col_desc[COL_THREADS_BUSY_TIME].max_data_string + 3;
1406 }
1407
1408 if (!col_desc[COL_THREADS_CPU_USAGE].disabled) {
1409 core_idx = g_threads_info[current_row].core_idx;
1410 if (core_idx >= 0 && core_idx < RPC_MAX_CORES) {
1411 uint64_t core_busy_period = g_cores_info[core_idx].busy - g_cores_info[core_idx].last_busy;
1412 uint64_t core_idle_period = g_cores_info[core_idx].idle - g_cores_info[core_idx].last_idle;
1413 get_cpu_usage_str(busy_period, core_busy_period + core_idle_period, cpu_usage);
1414 } else {
1415 snprintf(cpu_usage, sizeof(cpu_usage), "n/a");
1416 }
1417
1418 print_max_len(g_tabs[THREADS_TAB], TABS_DATA_START_ROW + item_index, col,
1419 col_desc[COL_THREADS_CPU_USAGE].max_data_string, ALIGN_RIGHT, cpu_usage);
1420 col += col_desc[COL_THREADS_CPU_USAGE].max_data_string + 2;
1421 }
1422
1423 if (!col_desc[COL_THREADS_STATUS].disabled) {
1424 if (busy_period > idle_period) {
1425 if (item_index != g_selected_row) {
1426 color_attr = COLOR_PAIR(6);
1427 } else {
1428 color_attr = COLOR_PAIR(8);
1429 }
1430 status_str = "Busy";
1431 } else {
1432 if (item_index != g_selected_row) {
1433 color_attr = COLOR_PAIR(7);
1434 } else {
1435 color_attr = COLOR_PAIR(9);
1436 }
1437 status_str = "Idle";
1438 }
1439 wattron(g_tabs[THREADS_TAB], color_attr);
1440 print_max_len(g_tabs[THREADS_TAB], TABS_DATA_START_ROW + item_index, col,
1441 col_desc[COL_THREADS_STATUS].max_data_string, ALIGN_RIGHT, status_str);
1442 wattroff(g_tabs[THREADS_TAB], color_attr);
1443 }
1444 }
1445
1446 static uint8_t
refresh_threads_tab(uint8_t current_page)1447 refresh_threads_tab(uint8_t current_page)
1448 {
1449 uint64_t i, j, threads_count;
1450 uint16_t empty_col = 0;
1451 uint8_t max_pages, item_index;
1452
1453 threads_count = g_last_threads_count;
1454
1455 max_pages = (threads_count + g_max_data_rows - 1) / g_max_data_rows;
1456
1457 for (i = current_page * g_max_data_rows;
1458 i < (uint64_t)((current_page + 1) * g_max_data_rows);
1459 i++) {
1460 item_index = i - (current_page * g_max_data_rows);
1461
1462 /* When number of threads decreases, this will print spaces in places
1463 * where non existent threads were previously displayed. */
1464 if (i >= threads_count) {
1465 for (j = 1; j < (uint64_t)g_max_col - 1; j++) {
1466 mvwprintw(g_tabs[THREADS_TAB], item_index + TABS_DATA_START_ROW, j, " ");
1467 }
1468
1469 empty_col++;
1470 continue;
1471 }
1472
1473 draw_row_background(item_index, THREADS_TAB);
1474 draw_thread_tab_row(i, item_index);
1475
1476 if (item_index == g_selected_row) {
1477 wattroff(g_tabs[THREADS_TAB], COLOR_PAIR(2));
1478 }
1479 }
1480
1481 g_max_selected_row = i - current_page * g_max_data_rows - 1 - empty_col;
1482
1483 return max_pages;
1484 }
1485
1486 static void
draw_poller_tab_row(uint64_t current_row,uint8_t item_index)1487 draw_poller_tab_row(uint64_t current_row, uint8_t item_index)
1488 {
1489 struct col_desc *col_desc = g_col_desc[POLLERS_TAB];
1490 uint64_t last_run_counter, last_busy_counter;
1491 uint16_t col = TABS_DATA_START_COL;
1492 char run_count[MAX_POLLER_RUN_COUNT], period_ticks[MAX_PERIOD_STR_LEN],
1493 status[MAX_POLLER_IND_STR_LEN];
1494
1495 last_busy_counter = get_last_busy_counter(g_pollers_info[current_row].id,
1496 g_pollers_info[current_row].thread_id);
1497
1498 if (!col_desc[COL_POLLERS_NAME].disabled) {
1499 print_max_len(g_tabs[POLLERS_TAB], TABS_DATA_START_ROW + item_index, col + 1,
1500 col_desc[COL_POLLERS_NAME].max_data_string, ALIGN_LEFT, g_pollers_info[current_row].name);
1501 col += col_desc[COL_POLLERS_NAME].max_data_string + 2;
1502 }
1503
1504 if (!col_desc[COL_POLLERS_TYPE].disabled) {
1505 print_max_len(g_tabs[POLLERS_TAB], TABS_DATA_START_ROW + item_index, col,
1506 col_desc[COL_POLLERS_TYPE].max_data_string, ALIGN_LEFT,
1507 poller_type_str[g_pollers_info[current_row].type]);
1508 col += col_desc[COL_POLLERS_TYPE].max_data_string + 2;
1509 }
1510
1511 if (!col_desc[COL_POLLERS_THREAD_NAME].disabled) {
1512 print_max_len(g_tabs[POLLERS_TAB], TABS_DATA_START_ROW + item_index, col,
1513 col_desc[COL_POLLERS_THREAD_NAME].max_data_string, ALIGN_LEFT,
1514 g_pollers_info[current_row].thread_name);
1515 col += col_desc[COL_POLLERS_THREAD_NAME].max_data_string + 1;
1516 }
1517
1518 if (!col_desc[COL_POLLERS_RUN_COUNTER].disabled) {
1519 last_run_counter = get_last_run_counter(g_pollers_info[current_row].id,
1520 g_pollers_info[current_row].thread_id);
1521 if (g_interval_data == true) {
1522 snprintf(run_count, MAX_POLLER_RUN_COUNT, "%" PRIu64,
1523 g_pollers_info[current_row].run_count - last_run_counter);
1524 } else {
1525 snprintf(run_count, MAX_POLLER_RUN_COUNT, "%" PRIu64, g_pollers_info[current_row].run_count);
1526 }
1527 print_max_len(g_tabs[POLLERS_TAB], TABS_DATA_START_ROW + item_index, col,
1528 col_desc[COL_POLLERS_RUN_COUNTER].max_data_string, ALIGN_RIGHT, run_count);
1529 col += col_desc[COL_POLLERS_RUN_COUNTER].max_data_string;
1530 }
1531
1532 if (!col_desc[COL_POLLERS_PERIOD].disabled) {
1533 if (g_pollers_info[current_row].period_ticks != 0) {
1534 get_time_str(g_pollers_info[current_row].period_ticks, period_ticks);
1535 print_max_len(g_tabs[POLLERS_TAB], TABS_DATA_START_ROW + item_index, col,
1536 col_desc[COL_POLLERS_PERIOD].max_data_string, ALIGN_RIGHT, period_ticks);
1537 }
1538 col += col_desc[COL_POLLERS_PERIOD].max_data_string + 7;
1539 }
1540
1541 if (!col_desc[COL_POLLERS_BUSY_COUNT].disabled) {
1542 if (g_pollers_info[current_row].busy_count > last_busy_counter) {
1543 if (g_interval_data == true) {
1544 snprintf(status, MAX_POLLER_IND_STR_LEN, "Busy (%" PRIu64 ")",
1545 g_pollers_info[current_row].busy_count - last_busy_counter);
1546 } else {
1547 snprintf(status, MAX_POLLER_IND_STR_LEN, "Busy (%" PRIu64 ")",
1548 g_pollers_info[current_row].busy_count);
1549 }
1550
1551 if (item_index != g_selected_row) {
1552 wattron(g_tabs[POLLERS_TAB], COLOR_PAIR(6));
1553 print_max_len(g_tabs[POLLERS_TAB], TABS_DATA_START_ROW + item_index, col,
1554 col_desc[COL_POLLERS_BUSY_COUNT].max_data_string, ALIGN_LEFT, status);
1555 wattroff(g_tabs[POLLERS_TAB], COLOR_PAIR(6));
1556 } else {
1557 wattron(g_tabs[POLLERS_TAB], COLOR_PAIR(8));
1558 print_max_len(g_tabs[POLLERS_TAB], TABS_DATA_START_ROW + item_index, col,
1559 col_desc[COL_POLLERS_BUSY_COUNT].max_data_string, ALIGN_LEFT, status);
1560 wattroff(g_tabs[POLLERS_TAB], COLOR_PAIR(8));
1561 }
1562 } else {
1563 if (g_interval_data == true) {
1564 snprintf(status, MAX_POLLER_IND_STR_LEN, "%s", "Idle");
1565 } else {
1566 snprintf(status, MAX_POLLER_IND_STR_LEN, "Idle (%" PRIu64 ")",
1567 g_pollers_info[current_row].busy_count);
1568 }
1569
1570 if (item_index != g_selected_row) {
1571 wattron(g_tabs[POLLERS_TAB], COLOR_PAIR(7));
1572 print_max_len(g_tabs[POLLERS_TAB], TABS_DATA_START_ROW + item_index, col,
1573 col_desc[COL_POLLERS_BUSY_COUNT].max_data_string, ALIGN_LEFT, status);
1574 wattroff(g_tabs[POLLERS_TAB], COLOR_PAIR(7));
1575 } else {
1576 wattron(g_tabs[POLLERS_TAB], COLOR_PAIR(9));
1577 print_max_len(g_tabs[POLLERS_TAB], TABS_DATA_START_ROW + item_index, col,
1578 col_desc[COL_POLLERS_BUSY_COUNT].max_data_string, ALIGN_LEFT, status);
1579 wattroff(g_tabs[POLLERS_TAB], COLOR_PAIR(9));
1580 }
1581 }
1582 }
1583 }
1584
1585 static uint8_t
refresh_pollers_tab(uint8_t current_page)1586 refresh_pollers_tab(uint8_t current_page)
1587 {
1588 uint64_t i, j;
1589 uint16_t empty_col = 0;
1590 uint8_t max_pages, item_index;
1591
1592 max_pages = (g_last_pollers_count + g_max_data_rows - 1) / g_max_data_rows;
1593
1594 /* Display info */
1595 for (i = current_page * g_max_data_rows;
1596 i < (uint64_t)((current_page + 1) * g_max_data_rows);
1597 i++) {
1598 item_index = i - (current_page * g_max_data_rows);
1599
1600 /* When number of pollers decreases, this will print spaces in places
1601 * where non existent pollers were previously displayed. */
1602 if (i >= g_last_pollers_count) {
1603 for (j = 1; j < (uint64_t)g_max_col - 1; j++) {
1604 mvwprintw(g_tabs[POLLERS_TAB], item_index + TABS_DATA_START_ROW, j, " ");
1605 }
1606
1607 empty_col++;
1608 continue;
1609 }
1610
1611 draw_row_background(item_index, POLLERS_TAB);
1612 draw_poller_tab_row(i, item_index);
1613
1614 if (item_index == g_selected_row) {
1615 wattroff(g_tabs[POLLERS_TAB], COLOR_PAIR(2));
1616 }
1617 }
1618
1619 g_max_selected_row = i - current_page * g_max_data_rows - 1 - empty_col;
1620
1621 return max_pages;
1622 }
1623
1624 static void
draw_core_tab_row(uint64_t current_row,uint8_t item_index)1625 draw_core_tab_row(uint64_t current_row, uint8_t item_index)
1626 {
1627 struct col_desc *col_desc = g_col_desc[CORES_TAB];
1628 float res = 0.0;
1629 uint16_t col = 1;
1630 uint64_t irq_tmp, usr_tmp, sys_tmp, busy_tmp, idle_tmp;
1631 int color_attr = COLOR_PAIR(6);
1632 char core[MAX_CORE_STR_LEN], threads_number[MAX_THREAD_COUNT_STR_LEN], cpu_usage[MAX_FLOAT_STR_LEN],
1633 pollers_number[MAX_POLLER_COUNT_STR_LEN], idle_time[MAX_TIME_STR_LEN],
1634 busy_time[MAX_TIME_STR_LEN], core_freq[MAX_CORE_FREQ_STR_LEN],
1635 in_interrupt[MAX_INTR_LEN], *status_str, sys_str[MAX_FLOAT_STR_LEN],
1636 irq_str[MAX_FLOAT_STR_LEN], cpu_str[MAX_FLOAT_STR_LEN];
1637
1638 snprintf(threads_number, MAX_THREAD_COUNT_STR_LEN, "%ld",
1639 g_cores_info[current_row].threads.threads_count);
1640 snprintf(pollers_number, MAX_POLLER_COUNT_STR_LEN, "%ld", g_cores_info[current_row].pollers_count);
1641
1642 if (!col_desc[COL_CORES_CORE].disabled) {
1643 snprintf(core, MAX_CORE_STR_LEN, "%d", g_cores_info[current_row].lcore);
1644 print_max_len(g_tabs[CORES_TAB], TABS_DATA_START_ROW + item_index, col,
1645 col_desc[COL_CORES_CORE].max_data_string, ALIGN_RIGHT, core);
1646 col += col_desc[COL_CORES_CORE].max_data_string + 2;
1647 }
1648
1649 if (!col_desc[COL_CORES_THREADS].disabled) {
1650 print_max_len(g_tabs[CORES_TAB], TABS_DATA_START_ROW + item_index,
1651 col + (col_desc[COL_CORES_THREADS].name_len / 2), col_desc[COL_CORES_THREADS].max_data_string,
1652 ALIGN_LEFT, threads_number);
1653 col += col_desc[COL_CORES_THREADS].max_data_string + 2;
1654 }
1655
1656 if (!col_desc[COL_CORES_POLLERS].disabled) {
1657 print_max_len(g_tabs[CORES_TAB], TABS_DATA_START_ROW + item_index,
1658 col + (col_desc[COL_CORES_POLLERS].name_len / 2), col_desc[COL_CORES_POLLERS].max_data_string,
1659 ALIGN_LEFT, pollers_number);
1660 col += col_desc[COL_CORES_POLLERS].max_data_string;
1661 }
1662
1663 uint64_t idle_period = g_cores_info[current_row].idle - g_cores_info[current_row].last_idle;
1664 uint64_t busy_period = g_cores_info[current_row].busy - g_cores_info[current_row].last_busy;
1665 if (!col_desc[COL_CORES_IDLE_TIME].disabled) {
1666 if (g_interval_data == true) {
1667 get_time_str(idle_period, idle_time);
1668 } else {
1669 get_time_str(g_cores_info[current_row].idle, idle_time);
1670 }
1671 print_max_len(g_tabs[CORES_TAB], TABS_DATA_START_ROW + item_index, col,
1672 col_desc[COL_CORES_IDLE_TIME].max_data_string, ALIGN_RIGHT, idle_time);
1673 col += col_desc[COL_CORES_IDLE_TIME].max_data_string + 2;
1674 }
1675
1676 if (!col_desc[COL_CORES_BUSY_TIME].disabled) {
1677 if (g_interval_data == true) {
1678 get_time_str(busy_period, busy_time);
1679 } else {
1680 get_time_str(g_cores_info[current_row].busy, busy_time);
1681 }
1682 print_max_len(g_tabs[CORES_TAB], TABS_DATA_START_ROW + item_index, col,
1683 col_desc[COL_CORES_BUSY_TIME].max_data_string, ALIGN_RIGHT, busy_time);
1684 col += col_desc[COL_CORES_BUSY_TIME].max_data_string + 2;
1685 }
1686
1687 if (!col_desc[COL_CORES_BUSY_PCT].disabled) {
1688 get_cpu_usage_str(busy_period, busy_period + idle_period, cpu_usage);
1689 print_max_len(g_tabs[CORES_TAB], TABS_DATA_START_ROW + item_index, col,
1690 col_desc[COL_CORES_BUSY_PCT].max_data_string, ALIGN_RIGHT, cpu_usage);
1691 col += col_desc[COL_CORES_BUSY_PCT].max_data_string + 1;
1692 }
1693
1694 if (!col_desc[COL_CORES_STATUS].disabled) {
1695 if (busy_period > idle_period) {
1696 if (item_index != g_selected_row) {
1697 color_attr = COLOR_PAIR(6);
1698 } else {
1699 color_attr = COLOR_PAIR(8);
1700 }
1701 status_str = "Busy";
1702 } else {
1703 if (item_index != g_selected_row) {
1704 color_attr = COLOR_PAIR(7);
1705 } else {
1706 color_attr = COLOR_PAIR(9);
1707 }
1708 status_str = "Idle";
1709 }
1710 wattron(g_tabs[CORES_TAB], color_attr);
1711 print_max_len(g_tabs[CORES_TAB], TABS_DATA_START_ROW + item_index, col,
1712 col_desc[COL_CORES_STATUS].max_data_string, ALIGN_RIGHT, status_str);
1713 wattroff(g_tabs[CORES_TAB], color_attr);
1714 if (item_index == g_selected_row) {
1715 wattron(g_tabs[CORES_TAB], COLOR_PAIR(2));
1716 } else {
1717 wattron(g_tabs[CORES_TAB], COLOR_PAIR(0));
1718 }
1719 col += col_desc[COL_CORES_STATUS].max_data_string + 1;
1720 }
1721
1722 if (!col_desc[COL_CORES_INTR].disabled) {
1723 snprintf(in_interrupt, MAX_INTR_LEN, "%s",
1724 g_cores_info[current_row].in_interrupt ? "Yes" : "No");
1725 print_max_len(g_tabs[CORES_TAB], TABS_DATA_START_ROW + item_index,
1726 col + (col_desc[COL_CORES_INTR].name_len / 2),
1727 col_desc[COL_CORES_INTR].max_data_string,
1728 ALIGN_LEFT, in_interrupt);
1729 col += col_desc[COL_CORES_INTR].max_data_string + 1;
1730 }
1731
1732 /* System stats */
1733 col += 2;
1734
1735 if (g_interval_data == true) {
1736 irq_tmp = g_cores_info[current_row].irq - g_cores_info[current_row].last_irq;
1737 usr_tmp = g_cores_info[current_row].usr - g_cores_info[current_row].last_usr;
1738 sys_tmp = g_cores_info[current_row].sys - g_cores_info[current_row].last_sys;
1739 idle_tmp = idle_period;
1740 busy_tmp = busy_period;
1741 } else {
1742 irq_tmp = g_cores_info[current_row].irq;
1743 usr_tmp = g_cores_info[current_row].usr;
1744 sys_tmp = g_cores_info[current_row].sys;
1745 idle_tmp = g_cores_info[current_row].idle;
1746 busy_tmp = g_cores_info[current_row].busy;
1747 }
1748
1749 if (!col_desc[COL_CORES_SYS_PCT].disabled) {
1750 if (usr_tmp + sys_tmp > 0) {
1751 res = (((float)sys_tmp /
1752 (usr_tmp + sys_tmp)) * 100);
1753 }
1754 snprintf(sys_str, sizeof(sys_str), "%.2f", res);
1755 print_max_len(g_tabs[CORES_TAB], TABS_DATA_START_ROW + item_index, col,
1756 col_desc[COL_CORES_SYS_PCT].max_data_string, ALIGN_RIGHT, sys_str);
1757 col += col_desc[COL_CORES_SYS_PCT].max_data_string + 1;
1758 }
1759
1760 if (!col_desc[COL_CORES_IRQ_PCT].disabled) {
1761 res = 0.0;
1762 if (usr_tmp + sys_tmp + irq_tmp > 0) {
1763 res = (((float)irq_tmp / (usr_tmp + sys_tmp + irq_tmp)) * 100);
1764 }
1765 snprintf(irq_str, sizeof(irq_str), "%.2f", res);
1766 print_max_len(g_tabs[CORES_TAB], TABS_DATA_START_ROW + item_index, col,
1767 col_desc[COL_CORES_IRQ_PCT].max_data_string, ALIGN_RIGHT, irq_str);
1768 col += col_desc[COL_CORES_IRQ_PCT].max_data_string + 1;
1769 }
1770
1771 if (!col_desc[COL_CORES_CPU_PCT].disabled) {
1772 res = 0.0;
1773 if (busy_tmp + idle_tmp + sys_tmp + irq_tmp > 0) {
1774 res = (((float)(busy_tmp + irq_tmp + sys_tmp) /
1775 (busy_tmp + idle_tmp + sys_tmp + irq_tmp)) * 100);
1776 }
1777 snprintf(cpu_str, sizeof(cpu_str), "%.2f", res);
1778 print_max_len(g_tabs[CORES_TAB], TABS_DATA_START_ROW + item_index, col,
1779 col_desc[COL_CORES_CPU_PCT].max_data_string, ALIGN_RIGHT, cpu_str);
1780 col += col_desc[COL_CORES_CPU_PCT].max_data_string + 1;
1781 }
1782
1783 if (!col_desc[COL_CORES_CORE_FREQ].disabled) {
1784 if (!g_cores_info[current_row].core_freq) {
1785 snprintf(core_freq, MAX_CORE_FREQ_STR_LEN, "%s", "N/A");
1786 } else {
1787 snprintf(core_freq, MAX_CORE_FREQ_STR_LEN, "%" PRIu32,
1788 g_cores_info[current_row].core_freq);
1789 }
1790 print_max_len(g_tabs[CORES_TAB], TABS_DATA_START_ROW + item_index, col,
1791 col_desc[COL_CORES_CORE_FREQ].max_data_string, ALIGN_RIGHT, core_freq);
1792 }
1793 }
1794
1795 static uint8_t
refresh_cores_tab(uint8_t current_page)1796 refresh_cores_tab(uint8_t current_page)
1797 {
1798 uint64_t i;
1799 uint16_t count = 0;
1800 uint8_t max_pages, item_index;
1801
1802 count = g_last_cores_count;
1803
1804 max_pages = (count + g_max_row - WINDOW_HEADER - 1) / (g_max_row - WINDOW_HEADER);
1805
1806 for (i = current_page * g_max_data_rows;
1807 i < spdk_min(count, (uint64_t)((current_page + 1) * g_max_data_rows));
1808 i++) {
1809 item_index = i - (current_page * g_max_data_rows);
1810
1811 draw_row_background(item_index, CORES_TAB);
1812 draw_core_tab_row(i, item_index);
1813
1814 if (item_index == g_selected_row) {
1815 wattroff(g_tabs[CORES_TAB], COLOR_PAIR(2));
1816 }
1817 }
1818
1819 g_max_selected_row = i - current_page * g_max_data_rows - 1;
1820
1821 return max_pages;
1822 }
1823
1824 static uint8_t
refresh_tab(enum tabs tab,uint8_t current_page)1825 refresh_tab(enum tabs tab, uint8_t current_page)
1826 {
1827 uint8_t (*refresh_function[NUMBER_OF_TABS])(uint8_t current_page) = {refresh_threads_tab, refresh_pollers_tab, refresh_cores_tab};
1828 int color_pair[NUMBER_OF_TABS] = {COLOR_PAIR(2), COLOR_PAIR(2), COLOR_PAIR(2)};
1829 int i;
1830 uint8_t max_pages = 0;
1831
1832 color_pair[tab] = COLOR_PAIR(1);
1833
1834 for (i = 0; i < NUMBER_OF_TABS; i++) {
1835 wbkgd(g_tab_win[i], color_pair[i]);
1836 }
1837
1838 max_pages = (*refresh_function[tab])(current_page);
1839
1840 for (i = 0; i < NUMBER_OF_TABS; i++) {
1841 wnoutrefresh(g_tab_win[i]);
1842 }
1843 draw_menu_win();
1844
1845 return max_pages;
1846 }
1847
1848 static void
print_in_middle(WINDOW * win,int starty,int startx,int width,char * string,chtype color)1849 print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color)
1850 {
1851 int length, temp;
1852
1853 length = strlen(string);
1854 temp = (width - length) / 2;
1855 wattron(win, color);
1856 mvwprintw(win, starty, startx + temp, "%s", string);
1857 wattroff(win, color);
1858 }
1859
1860 static void
print_left(WINDOW * win,int starty,int startx,int width,const char * string,chtype color)1861 print_left(WINDOW *win, int starty, int startx, int width, const char *string, chtype color)
1862 {
1863 wattron(win, color);
1864 mvwprintw(win, starty, startx, "%s", string);
1865 wattroff(win, color);
1866 }
1867
1868 static void
apply_filters(enum tabs tab)1869 apply_filters(enum tabs tab)
1870 {
1871 wclear(g_tabs[tab]);
1872 draw_tabs(tab, g_current_sort_col[tab], g_current_sort_col2[tab]);
1873 }
1874
1875 static ITEM **
draw_filtering_menu(uint8_t position,WINDOW * filter_win,uint8_t tab,MENU ** my_menu)1876 draw_filtering_menu(uint8_t position, WINDOW *filter_win, uint8_t tab, MENU **my_menu)
1877 {
1878 const int ADDITIONAL_ELEMENTS = 3;
1879 const int ROW_PADDING = 6;
1880 const int WINDOW_START_X = 1;
1881 const int WINDOW_START_Y = 3;
1882 const int WINDOW_COLUMNS = 2;
1883 struct col_desc *col_desc = g_col_desc[tab];
1884 ITEM **my_items;
1885 MENU *menu;
1886 int i, elements;
1887 uint8_t len = 0;
1888
1889 for (i = 0; col_desc[i].name != NULL; ++i) {
1890 len = spdk_max(col_desc[i].name_len, len);
1891 }
1892
1893 elements = i;
1894
1895 my_items = (ITEM **)calloc(elements * WINDOW_COLUMNS + ADDITIONAL_ELEMENTS, sizeof(ITEM *));
1896 if (my_items == NULL) {
1897 fprintf(stderr, "Unable to allocate an item list in draw_filtering_menu.\n");
1898 return NULL;
1899 }
1900
1901 for (i = 0; i < elements * 2; i++) {
1902 my_items[i] = new_item(col_desc[i / WINDOW_COLUMNS].name, NULL);
1903 i++;
1904 my_items[i] = new_item(col_desc[i / WINDOW_COLUMNS].disabled ? "[ ]" : "[*]", NULL);
1905 }
1906
1907 my_items[i] = new_item(" CLOSE", NULL);
1908 set_item_userptr(my_items[i], apply_filters);
1909
1910 menu = new_menu((ITEM **)my_items);
1911
1912 menu_opts_off(menu, O_SHOWDESC);
1913 set_menu_format(menu, elements + 1, WINDOW_COLUMNS);
1914
1915 set_menu_win(menu, filter_win);
1916 set_menu_sub(menu, derwin(filter_win, elements + 1, len + ROW_PADDING, WINDOW_START_Y,
1917 WINDOW_START_X));
1918
1919 *my_menu = menu;
1920
1921 post_menu(menu);
1922 refresh();
1923 wrefresh(filter_win);
1924
1925 for (i = 0; i < position / WINDOW_COLUMNS; i++) {
1926 menu_driver(menu, REQ_DOWN_ITEM);
1927 }
1928
1929 return my_items;
1930 }
1931
1932 static void
delete_filtering_menu(MENU * my_menu,ITEM ** my_items,uint8_t elements)1933 delete_filtering_menu(MENU *my_menu, ITEM **my_items, uint8_t elements)
1934 {
1935 int i;
1936
1937 unpost_menu(my_menu);
1938 free_menu(my_menu);
1939 for (i = 0; i < elements * 2 + 2; ++i) {
1940 free_item(my_items[i]);
1941 }
1942 free(my_items);
1943 }
1944
1945 static ITEM **
refresh_filtering_menu(MENU ** my_menu,WINDOW * filter_win,uint8_t tab,ITEM ** my_items,uint8_t elements,uint8_t position)1946 refresh_filtering_menu(MENU **my_menu, WINDOW *filter_win, uint8_t tab, ITEM **my_items,
1947 uint8_t elements, uint8_t position)
1948 {
1949 delete_filtering_menu(*my_menu, my_items, elements);
1950 return draw_filtering_menu(position, filter_win, tab, my_menu);
1951 }
1952
1953 static void
filter_columns(uint8_t tab)1954 filter_columns(uint8_t tab)
1955 {
1956 const int WINDOW_HEADER_LEN = 5;
1957 const int WINDOW_BORDER_LEN = 8;
1958 const int WINDOW_HEADER_END_LINE = 2;
1959 const int WINDOW_COLUMNS = 2;
1960 struct col_desc *col_desc = g_col_desc[tab];
1961 PANEL *filter_panel;
1962 WINDOW *filter_win;
1963 ITEM **my_items;
1964 MENU *my_menu = NULL;
1965 int i, c, elements;
1966 bool stop_loop = false;
1967 ITEM *cur;
1968 void (*p)(enum tabs tab);
1969 uint8_t current_index, len = 0;
1970 bool disabled[TABS_COL_COUNT];
1971
1972 for (i = 0; col_desc[i].name != NULL; ++i) {
1973 len = spdk_max(col_desc[i].name_len, len);
1974 }
1975
1976 elements = i;
1977
1978 filter_win = newwin(elements + WINDOW_HEADER_LEN, len + WINDOW_BORDER_LEN,
1979 (g_max_row - elements - 1) / 2, (g_max_col - len) / 2);
1980 assert(filter_win != NULL);
1981 keypad(filter_win, TRUE);
1982 filter_panel = new_panel(filter_win);
1983 assert(filter_panel != NULL);
1984
1985 top_panel(filter_panel);
1986 update_panels();
1987 doupdate();
1988
1989 box(filter_win, 0, 0);
1990
1991 print_in_middle(filter_win, 1, 0, len + WINDOW_BORDER_LEN, "Filtering", COLOR_PAIR(3));
1992 mvwaddch(filter_win, WINDOW_HEADER_END_LINE, 0, ACS_LTEE);
1993 mvwhline(filter_win, WINDOW_HEADER_END_LINE, 1, ACS_HLINE, len + WINDOW_BORDER_LEN - 2);
1994 mvwaddch(filter_win, WINDOW_HEADER_END_LINE, len + WINDOW_BORDER_LEN - 1, ACS_RTEE);
1995
1996 my_items = draw_filtering_menu(0, filter_win, tab, &my_menu);
1997 if (my_items == NULL || my_menu == NULL) {
1998 goto fail;
1999 }
2000
2001 for (int i = 0; i < TABS_COL_COUNT; i++) {
2002 disabled[i] = col_desc[i].disabled;
2003 }
2004
2005 while (!stop_loop) {
2006 c = wgetch(filter_win);
2007
2008 switch (c) {
2009 case KEY_DOWN:
2010 menu_driver(my_menu, REQ_DOWN_ITEM);
2011 break;
2012 case KEY_UP:
2013 menu_driver(my_menu, REQ_UP_ITEM);
2014 break;
2015 case 27: /* ESC */
2016 case 'q':
2017 for (int i = 0; i < TABS_COL_COUNT; i++) {
2018 cur = current_item(my_menu);
2019 col_desc[i].disabled = disabled[i];
2020
2021 my_items = refresh_filtering_menu(&my_menu, filter_win, tab, my_items, elements,
2022 item_index(cur) + 1);
2023 if (my_items == NULL || my_menu == NULL) {
2024 goto fail;
2025 }
2026 }
2027
2028 stop_loop = true;
2029 break;
2030 case ' ': /* Space */
2031 cur = current_item(my_menu);
2032 current_index = item_index(cur) / WINDOW_COLUMNS;
2033 col_desc[current_index].disabled = !col_desc[current_index].disabled;
2034 my_items = refresh_filtering_menu(&my_menu, filter_win, tab, my_items, elements,
2035 item_index(cur) + 1);
2036 if (my_items == NULL || my_menu == NULL) {
2037 goto fail;
2038 }
2039 break;
2040 case 10: /* Enter */
2041 cur = current_item(my_menu);
2042 current_index = item_index(cur) / WINDOW_COLUMNS;
2043 if (current_index == elements) {
2044 stop_loop = true;
2045 p = item_userptr(cur);
2046 p(tab);
2047 } else {
2048 col_desc[current_index].disabled = !col_desc[current_index].disabled;
2049 my_items = refresh_filtering_menu(&my_menu, filter_win, tab, my_items, elements,
2050 item_index(cur) + 1);
2051 if (my_items == NULL || my_menu == NULL) {
2052 goto fail;
2053 }
2054 }
2055 break;
2056 }
2057 wrefresh(filter_win);
2058 }
2059
2060 delete_filtering_menu(my_menu, my_items, elements);
2061
2062 del_panel(filter_panel);
2063 delwin(filter_win);
2064
2065 wclear(g_menu_win);
2066 draw_menu_win();
2067 return;
2068
2069 fail:
2070 fprintf(stderr, "Unable to filter the columns due to allocation failure.\n");
2071 assert(false);
2072 }
2073
2074 static void
sort_type(enum tabs tab,int item_index)2075 sort_type(enum tabs tab, int item_index)
2076 {
2077 g_current_sort_col[tab] = item_index;
2078 }
2079
2080 static void
sort_type2(enum tabs tab,int item_index)2081 sort_type2(enum tabs tab, int item_index)
2082 {
2083 g_current_sort_col2[tab] = item_index;
2084 }
2085
2086 static void
change_sorting(uint8_t tab,int winnum,bool * pstop_loop)2087 change_sorting(uint8_t tab, int winnum, bool *pstop_loop)
2088 {
2089 const int WINDOW_HEADER_LEN = 4;
2090 const int WINDOW_BORDER_LEN = 3;
2091 const int WINDOW_START_X = 1;
2092 const int WINDOW_START_Y = 4;
2093 const int WINDOW_HEADER_END_LINE = 3;
2094 const int WINDOW_MIN_WIDTH = 31;
2095 PANEL *sort_panel;
2096 WINDOW *sort_win;
2097 ITEM **my_items;
2098 MENU *my_menu;
2099 int i, c, elements;
2100 bool stop_loop = false;
2101 ITEM *cur;
2102 char *name;
2103 char *help;
2104 void (*p)(enum tabs tab, int item_index);
2105 uint8_t len = WINDOW_MIN_WIDTH;
2106
2107 for (i = 0; g_col_desc[tab][i].name != NULL; ++i) {
2108 len = spdk_max(len, g_col_desc[tab][i].name_len);
2109 }
2110
2111 elements = i;
2112
2113 my_items = (ITEM **)calloc(elements + 1, sizeof(ITEM *));
2114 if (my_items == NULL) {
2115 fprintf(stderr, "Unable to allocate an item list in change_sorting.\n");
2116 return;
2117 }
2118
2119 for (i = 0; i < elements; ++i) {
2120 my_items[i] = new_item(g_col_desc[tab][i].name, NULL);
2121 set_item_userptr(my_items[i], (winnum == 0) ? sort_type : sort_type2);
2122 }
2123
2124 my_menu = new_menu((ITEM **)my_items);
2125
2126 menu_opts_off(my_menu, O_SHOWDESC);
2127
2128 sort_win = newwin(elements + WINDOW_HEADER_LEN + 1, len + WINDOW_BORDER_LEN,
2129 (g_max_row - elements) / 2,
2130 (g_max_col - len) / 2 - len + len * winnum);
2131 assert(sort_win != NULL);
2132 keypad(sort_win, TRUE);
2133 sort_panel = new_panel(sort_win);
2134 assert(sort_panel != NULL);
2135
2136 top_panel(sort_panel);
2137 update_panels();
2138 doupdate();
2139
2140 set_menu_win(my_menu, sort_win);
2141 set_menu_sub(my_menu, derwin(sort_win, elements, len + 1, WINDOW_START_Y, WINDOW_START_X));
2142 box(sort_win, 0, 0);
2143
2144 if (winnum == 0) {
2145 name = "Sorting #1";
2146 help = "Right key for second sorting";
2147 } else {
2148 name = "Sorting #2";
2149 help = "Left key for first sorting";
2150 }
2151
2152 print_in_middle(sort_win, 1, 0, len + WINDOW_BORDER_LEN, name, COLOR_PAIR(3));
2153 print_in_middle(sort_win, 2, 0, len + WINDOW_BORDER_LEN, help, COLOR_PAIR(3));
2154 mvwaddch(sort_win, WINDOW_HEADER_END_LINE, 0, ACS_LTEE);
2155 mvwhline(sort_win, WINDOW_HEADER_END_LINE, 1, ACS_HLINE, len + 1);
2156 mvwaddch(sort_win, WINDOW_HEADER_END_LINE, len + WINDOW_BORDER_LEN - 1, ACS_RTEE);
2157
2158 post_menu(my_menu);
2159 refresh();
2160 wrefresh(sort_win);
2161
2162 while (!stop_loop) {
2163 c = wgetch(sort_win);
2164 /*
2165 * First sorting window:
2166 * Up/Down - select first sorting column;
2167 * Enter - apply current column;
2168 * Right - open second sorting window.
2169 * Second sorting window:
2170 * Up/Down - select second sorting column;
2171 * Enter - apply current column of both sorting windows;
2172 * Left - exit second window and reset second sorting key;
2173 * Right - do nothing.
2174 */
2175 switch (c) {
2176 case KEY_DOWN:
2177 menu_driver(my_menu, REQ_DOWN_ITEM);
2178 break;
2179 case KEY_UP:
2180 menu_driver(my_menu, REQ_UP_ITEM);
2181 break;
2182 case KEY_RIGHT:
2183 if (winnum > 0) {
2184 break;
2185 }
2186 change_sorting(tab, winnum + 1, &stop_loop);
2187 /* Restore input. */
2188 keypad(sort_win, TRUE);
2189 post_menu(my_menu);
2190 refresh();
2191 wrefresh(sort_win);
2192 redrawwin(sort_win);
2193 if (winnum == 0) {
2194 cur = current_item(my_menu);
2195 p = item_userptr(cur);
2196 p(tab, item_index(cur));
2197 }
2198 break;
2199 case 27: /* ESC */
2200 stop_loop = true;
2201 break;
2202 case KEY_LEFT:
2203 if (winnum > 0) {
2204 sort_type2(tab, COL_THREADS_NONE);
2205 }
2206 /* FALLTHROUGH */
2207 case 10: /* Enter */
2208 stop_loop = true;
2209 if (winnum > 0 && c == 10) {
2210 *pstop_loop = true;
2211 }
2212 if (c == 10) {
2213 cur = current_item(my_menu);
2214 p = item_userptr(cur);
2215 p(tab, item_index(cur));
2216 }
2217 break;
2218 }
2219 wrefresh(sort_win);
2220 }
2221
2222 if (winnum == 0) {
2223 wclear(g_tabs[tab]);
2224 draw_tabs(tab, g_current_sort_col[tab], g_current_sort_col2[tab]);
2225 }
2226
2227 unpost_menu(my_menu);
2228 free_menu(my_menu);
2229
2230 for (i = 0; i < elements; ++i) {
2231 free_item(my_items[i]);
2232 }
2233
2234 free(my_items);
2235
2236 del_panel(sort_panel);
2237 delwin(sort_win);
2238
2239 if (winnum == 0) {
2240 wclear(g_menu_win);
2241 draw_menu_win();
2242 }
2243 }
2244
2245 static int
check_resize_interface(uint8_t active_tab,uint8_t * current_page)2246 check_resize_interface(uint8_t active_tab, uint8_t *current_page)
2247 {
2248 int max_row, max_col;
2249 uint16_t required_size = WINDOW_HEADER + 1;
2250
2251 /* Check if interface has to be resized (terminal size changed) */
2252 getmaxyx(stdscr, max_row, max_col);
2253
2254 if (max_row != g_max_row || max_col != g_max_col) {
2255 if (max_row != g_max_row) {
2256 *current_page = 0;
2257 }
2258 g_max_row = spdk_max(max_row, required_size);
2259 g_max_col = max_col;
2260 g_data_win_size = g_max_row - required_size + 1;
2261 g_max_data_rows = g_max_row - WINDOW_HEADER;
2262 resize_interface(active_tab);
2263
2264 return 1;
2265 }
2266
2267 return 0;
2268 }
2269
2270 static void
change_refresh_rate(void)2271 change_refresh_rate(void)
2272 {
2273 const int WINDOW_HEADER_END_LINE = 2;
2274 PANEL *refresh_panel;
2275 WINDOW *refresh_win;
2276 int c;
2277 bool stop_loop = false;
2278 uint32_t rr_tmp, refresh_rate = 0;
2279 char refresh_rate_str[MAX_STRING_LEN];
2280
2281 refresh_win = newwin(RR_WIN_HEIGHT, RR_WIN_WIDTH, (g_max_row - RR_WIN_HEIGHT - 1) / 2,
2282 (g_max_col - RR_WIN_WIDTH) / 2);
2283 assert(refresh_win != NULL);
2284 keypad(refresh_win, TRUE);
2285 refresh_panel = new_panel(refresh_win);
2286 assert(refresh_panel != NULL);
2287
2288 top_panel(refresh_panel);
2289 update_panels();
2290 doupdate();
2291
2292 box(refresh_win, 0, 0);
2293
2294 print_in_middle(refresh_win, 1, 0, RR_WIN_WIDTH + 1, "Enter refresh rate value [s]", COLOR_PAIR(3));
2295 mvwaddch(refresh_win, WINDOW_HEADER_END_LINE, 0, ACS_LTEE);
2296 mvwhline(refresh_win, WINDOW_HEADER_END_LINE, 1, ACS_HLINE, RR_WIN_WIDTH - 2);
2297 mvwaddch(refresh_win, WINDOW_HEADER_END_LINE, RR_WIN_WIDTH, ACS_RTEE);
2298 mvwprintw(refresh_win, WINDOW_HEADER_END_LINE + 1, (RR_WIN_WIDTH - 1) / 2, "%d", refresh_rate);
2299
2300 refresh();
2301 wrefresh(refresh_win);
2302
2303 while (!stop_loop) {
2304 c = wgetch(refresh_win);
2305
2306 switch (c) {
2307 case '0':
2308 case '1':
2309 case '2':
2310 case '3':
2311 case '4':
2312 case '5':
2313 case '6':
2314 case '7':
2315 case '8':
2316 case '9':
2317 rr_tmp = refresh_rate * 10 + c - '0';
2318
2319 if (rr_tmp <= RR_MAX_VALUE) {
2320 refresh_rate = rr_tmp;
2321 snprintf(refresh_rate_str, MAX_STRING_LEN - 1, "%d", refresh_rate);
2322 mvwprintw(refresh_win, WINDOW_HEADER_END_LINE + 1,
2323 (RR_WIN_WIDTH - 1 - strlen(refresh_rate_str)) / 2, "%d", refresh_rate);
2324 refresh();
2325 wrefresh(refresh_win);
2326 }
2327 break;
2328 case KEY_BACKSPACE:
2329 case 127:
2330 case '\b':
2331 refresh_rate = refresh_rate / 10;
2332 snprintf(refresh_rate_str, MAX_STRING_LEN - 1, "%d", refresh_rate);
2333 mvwprintw(refresh_win, WINDOW_HEADER_END_LINE + 1,
2334 (RR_WIN_WIDTH - 1 - strlen(refresh_rate_str) - 2) / 2, " ");
2335 mvwprintw(refresh_win, WINDOW_HEADER_END_LINE + 1,
2336 (RR_WIN_WIDTH - 1 - strlen(refresh_rate_str)) / 2, "%d", refresh_rate);
2337 refresh();
2338 wrefresh(refresh_win);
2339 break;
2340 case 27: /* ESC */
2341 case 'q':
2342 stop_loop = true;
2343 break;
2344 case 10: /* Enter */
2345 pthread_mutex_lock(&g_thread_lock);
2346 g_sleep_time = refresh_rate;
2347 pthread_mutex_unlock(&g_thread_lock);
2348 stop_loop = true;
2349 break;
2350 }
2351 wrefresh(refresh_win);
2352 }
2353
2354 del_panel(refresh_panel);
2355 delwin(refresh_win);
2356 }
2357
2358 static void
free_poller_history(void)2359 free_poller_history(void)
2360 {
2361 struct run_counter_history *history, *tmp;
2362
2363 TAILQ_FOREACH_SAFE(history, &g_run_counter_history, link, tmp) {
2364 TAILQ_REMOVE(&g_run_counter_history, history, link);
2365 free(history);
2366 }
2367 }
2368
2369 static uint64_t
get_position_for_window(uint64_t window_size,uint64_t max_size)2370 get_position_for_window(uint64_t window_size, uint64_t max_size)
2371 {
2372 /* This function calculates position for pop-up detail window.
2373 * Since horizontal and vertical positions are calculated the same way
2374 * there is no need for separate functions. */
2375 window_size = spdk_min(window_size, max_size);
2376
2377 return (max_size - window_size) / 2;
2378 }
2379
2380 static void
print_bottom_message(char * msg)2381 print_bottom_message(char *msg)
2382 {
2383 uint64_t i;
2384
2385 for (i = 1; i < (uint64_t)g_max_col - 1; i++) {
2386 mvprintw(g_max_row - 1, i, " ");
2387 }
2388 mvprintw(g_max_row - 1, g_max_col - strlen(msg) - 2, "%s", msg);
2389 }
2390
2391 static void
draw_thread_win_content(WINDOW * thread_win,struct rpc_thread_info * thread_info)2392 draw_thread_win_content(WINDOW *thread_win, struct rpc_thread_info *thread_info)
2393 {
2394 uint64_t current_row, i, time;
2395 char idle_time[MAX_TIME_STR_LEN], busy_time[MAX_TIME_STR_LEN];
2396
2397 box(thread_win, 0, 0);
2398
2399 print_in_middle(thread_win, 1, 0, THREAD_WIN_WIDTH, thread_info->name,
2400 COLOR_PAIR(3));
2401 mvwhline(thread_win, 2, 1, ACS_HLINE, THREAD_WIN_WIDTH - 2);
2402 mvwaddch(thread_win, 2, THREAD_WIN_WIDTH, ACS_RTEE);
2403
2404 print_left(thread_win, 3, THREAD_WIN_FIRST_COL, THREAD_WIN_WIDTH,
2405 "Core: Idle [us]: Busy [us]:", COLOR_PAIR(5));
2406 mvwprintw(thread_win, 3, THREAD_WIN_FIRST_COL + 6, "%d",
2407 thread_info->core_num);
2408
2409 if (g_interval_data) {
2410 get_time_str(thread_info->idle - thread_info->last_idle, idle_time);
2411 mvwprintw(thread_win, 3, THREAD_WIN_FIRST_COL + 32, "%s", idle_time);
2412 get_time_str(thread_info->busy - thread_info->last_busy, busy_time);
2413 mvwprintw(thread_win, 3, THREAD_WIN_FIRST_COL + 54, "%s", busy_time);
2414 } else {
2415 get_time_str(thread_info->idle, idle_time);
2416 mvwprintw(thread_win, 3, THREAD_WIN_FIRST_COL + 32, "%s", idle_time);
2417 get_time_str(thread_info->busy, busy_time);
2418 mvwprintw(thread_win, 3, THREAD_WIN_FIRST_COL + 54, "%s", busy_time);
2419 }
2420
2421 print_left(thread_win, 4, THREAD_WIN_FIRST_COL, THREAD_WIN_WIDTH,
2422 "Active pollers: Timed pollers: Paused pollers:", COLOR_PAIR(5));
2423 mvwprintw(thread_win, 4, THREAD_WIN_FIRST_COL + 17, "%" PRIu64,
2424 thread_info->active_pollers_count);
2425 mvwprintw(thread_win, 4, THREAD_WIN_FIRST_COL + 36, "%" PRIu64,
2426 thread_info->timed_pollers_count);
2427 mvwprintw(thread_win, 4, THREAD_WIN_FIRST_COL + 59, "%" PRIu64,
2428 thread_info->paused_pollers_count);
2429
2430 mvwhline(thread_win, 5, 1, ACS_HLINE, THREAD_WIN_WIDTH - 2);
2431
2432 print_in_middle(thread_win, 6, 0, THREAD_WIN_WIDTH,
2433 "Pollers Type Total run count Period", COLOR_PAIR(5));
2434
2435 mvwhline(thread_win, 7, 1, ACS_HLINE, THREAD_WIN_WIDTH - 2);
2436
2437 current_row = 8;
2438
2439 for (i = 0; i < g_last_pollers_count; i++) {
2440 if (g_pollers_info[i].thread_id == thread_info->id) {
2441 mvwprintw(thread_win, current_row, THREAD_WIN_FIRST_COL, "%s", g_pollers_info[i].name);
2442 if (g_pollers_info[i].type == SPDK_ACTIVE_POLLER) {
2443 mvwprintw(thread_win, current_row, THREAD_WIN_FIRST_COL + 33, "Active");
2444 } else if (g_pollers_info[i].type == SPDK_TIMED_POLLER) {
2445 mvwprintw(thread_win, current_row, THREAD_WIN_FIRST_COL + 33, "Timed");
2446 } else {
2447 mvwprintw(thread_win, current_row, THREAD_WIN_FIRST_COL + 33, "Paused");
2448 }
2449 mvwprintw(thread_win, current_row, THREAD_WIN_FIRST_COL + 41, "%" PRIu64,
2450 g_pollers_info[i].run_count);
2451 if (g_pollers_info[i].period_ticks) {
2452 time = g_pollers_info[i].period_ticks * SPDK_SEC_TO_USEC / g_tick_rate;
2453 mvwprintw(thread_win, current_row, THREAD_WIN_FIRST_COL + 59, "%" PRIu64, time);
2454 }
2455 current_row++;
2456 }
2457 }
2458
2459 wnoutrefresh(thread_win);
2460 }
2461
2462 static int
get_single_thread_info(uint64_t thread_id,struct rpc_thread_info * thread_info)2463 get_single_thread_info(uint64_t thread_id, struct rpc_thread_info *thread_info)
2464 {
2465 uint64_t i;
2466
2467 for (i = 0; i < g_last_threads_count; i++) {
2468 if (g_threads_info[i].id == thread_id) {
2469 memcpy(thread_info, &g_threads_info[i], sizeof(struct rpc_thread_info));
2470 thread_info->name = strdup(g_threads_info[i].name);
2471 thread_info->cpumask = strdup(g_threads_info[i].cpumask);
2472
2473 if (thread_info->name == NULL || thread_info->cpumask == NULL) {
2474 print_bottom_message("Unable to allocate memory for thread name and cpumask. Exiting pop-up.");
2475 return -1;
2476 }
2477
2478 return 0;
2479 }
2480 }
2481
2482 print_bottom_message("Selected thread no longer exists. Exiting pop-up.");
2483 return -1;
2484 }
2485
2486 static void
draw_core_win_content(WINDOW * core_win,struct rpc_core_info * core_info)2487 draw_core_win_content(WINDOW *core_win, struct rpc_core_info *core_info)
2488 {
2489 uint64_t i;
2490 char core_win_title[25];
2491 char idle_time[MAX_TIME_STR_LEN], busy_time[MAX_TIME_STR_LEN];
2492
2493 box(core_win, 0, 0);
2494 snprintf(core_win_title, sizeof(core_win_title), "Core %" PRIu32 " details",
2495 core_info->lcore);
2496 print_in_middle(core_win, 1, 0, CORE_WIN_WIDTH, core_win_title, COLOR_PAIR(3));
2497
2498 mvwhline(core_win, 2, 1, ACS_HLINE, CORE_WIN_WIDTH - 2);
2499 print_left(core_win, 3, 1, CORE_WIN_WIDTH - (CORE_WIN_WIDTH / 3),
2500 "Frequency: Intr:", COLOR_PAIR(5));
2501 if (core_info->core_freq) {
2502 mvwprintw(core_win, 3, CORE_WIN_FIRST_COL - 3, "%" PRIu32,
2503 core_info->core_freq);
2504 } else {
2505 mvwprintw(core_win, 3, CORE_WIN_FIRST_COL - 3, "%s", "N/A");
2506 }
2507
2508 mvwprintw(core_win, 3, CORE_WIN_FIRST_COL + 15, "%s",
2509 core_info->in_interrupt ? "Yes" : "No");
2510
2511 mvwhline(core_win, 4, 1, ACS_HLINE, CORE_WIN_WIDTH - 2);
2512 print_left(core_win, 5, 1, CORE_WIN_WIDTH, "Thread count: Idle time:", COLOR_PAIR(5));
2513
2514 mvwprintw(core_win, 5, CORE_WIN_FIRST_COL, "%" PRIu64,
2515 core_info->threads.threads_count);
2516
2517 if (g_interval_data == true) {
2518 get_time_str(core_info->idle - core_info->last_idle, idle_time);
2519 get_time_str(core_info->busy - core_info->last_busy, busy_time);
2520 } else {
2521 get_time_str(core_info->idle, idle_time);
2522 get_time_str(core_info->busy, busy_time);
2523 }
2524 mvwprintw(core_win, 5, CORE_WIN_FIRST_COL + 20, "%s", idle_time);
2525 mvwhline(core_win, 6, 1, ACS_HLINE, CORE_WIN_WIDTH - 2);
2526
2527 print_left(core_win, 7, 1, CORE_WIN_WIDTH, "Poller count: Busy time:", COLOR_PAIR(5));
2528 mvwprintw(core_win, 7, CORE_WIN_FIRST_COL, "%" PRIu64,
2529 core_info->pollers_count);
2530
2531 mvwprintw(core_win, 7, CORE_WIN_FIRST_COL + 20, "%s", busy_time);
2532
2533 mvwhline(core_win, 8, 1, ACS_HLINE, CORE_WIN_WIDTH - 2);
2534 print_left(core_win, 9, 1, CORE_WIN_WIDTH, "Threads on this core", COLOR_PAIR(5));
2535
2536 for (i = 0; i < core_info->threads.threads_count; i++) {
2537 mvwprintw(core_win, i + CORE_WIN_HEIGHT - 1, 1, "%s", core_info->threads.thread[i].name);
2538 }
2539 pthread_mutex_unlock(&g_thread_lock);
2540
2541 wnoutrefresh(core_win);
2542 }
2543
2544 static void
display_thread(uint64_t thread_id,uint8_t current_page,uint8_t active_tab,WINDOW * core_popup,struct rpc_core_info * core_info)2545 display_thread(uint64_t thread_id, uint8_t current_page, uint8_t active_tab,
2546 WINDOW *core_popup, struct rpc_core_info *core_info)
2547 {
2548 PANEL *thread_panel = NULL;
2549 WINDOW *thread_win = NULL;
2550 struct rpc_thread_info thread_info;
2551 uint64_t pollers_count, threads_count, last_pollers_count = 0;
2552 int c;
2553 bool stop_loop = false;
2554 long int time_last, time_dif;
2555 struct timespec time_now;
2556
2557 clock_gettime(CLOCK_MONOTONIC, &time_now);
2558 time_last = time_now.tv_sec;
2559
2560 memset(&thread_info, 0, sizeof(thread_info));
2561
2562 while (!stop_loop) {
2563 pthread_mutex_lock(&g_thread_lock);
2564 if (get_single_thread_info(thread_id, &thread_info)) {
2565 pthread_mutex_unlock(&g_thread_lock);
2566 free(thread_info.name);
2567 free(thread_info.cpumask);
2568 thread_info.name = NULL;
2569 thread_info.cpumask = NULL;
2570 return;
2571 }
2572 pollers_count = thread_info.active_pollers_count +
2573 thread_info.timed_pollers_count +
2574 thread_info.paused_pollers_count;
2575 if (pollers_count != last_pollers_count) {
2576 if (thread_win != NULL) {
2577 assert(thread_panel != NULL);
2578 del_panel(thread_panel);
2579 delwin(thread_win);
2580 }
2581
2582 thread_win = newwin(pollers_count + THREAD_WIN_HEIGHT, THREAD_WIN_WIDTH,
2583 get_position_for_window(THREAD_WIN_HEIGHT + pollers_count, g_max_row),
2584 get_position_for_window(THREAD_WIN_WIDTH, g_max_col));
2585 keypad(thread_win, TRUE);
2586 thread_panel = new_panel(thread_win);
2587
2588 top_panel(thread_panel);
2589 update_panels();
2590 doupdate();
2591 draw_thread_win_content(thread_win, &thread_info);
2592 refresh();
2593 }
2594 pthread_mutex_unlock(&g_thread_lock);
2595
2596 if (check_resize_interface(active_tab, ¤t_page)) {
2597 /* This clear is to avoid remaining artifacts after window has been moved */
2598 wclear(thread_win);
2599 wclear(core_popup);
2600 resize_interface(active_tab);
2601 draw_tabs(active_tab, g_current_sort_col[active_tab], g_current_sort_col2[active_tab]);
2602 if (core_popup != NULL) {
2603 pthread_mutex_lock(&g_thread_lock);
2604 threads_count = g_cores_info[core_info->lcore].threads.threads_count;
2605 pthread_mutex_unlock(&g_thread_lock);
2606 mvwin(core_popup, get_position_for_window(CORE_WIN_HEIGHT + threads_count, g_max_row),
2607 get_position_for_window(CORE_WIN_WIDTH, g_max_col));
2608 }
2609 mvwin(thread_win, get_position_for_window(THREAD_WIN_HEIGHT + pollers_count, g_max_row),
2610 get_position_for_window(THREAD_WIN_WIDTH, g_max_col));
2611 }
2612
2613 c = getch();
2614
2615 switch (c) {
2616 case 27: /* ESC */
2617 stop_loop = true;
2618 break;
2619 default:
2620 break;
2621 }
2622
2623 clock_gettime(CLOCK_MONOTONIC, &time_now);
2624 time_dif = time_now.tv_sec - time_last;
2625
2626 if (time_dif >= g_sleep_time) {
2627 time_last = time_now.tv_sec;
2628 pthread_mutex_lock(&g_thread_lock);
2629 refresh_tab(active_tab, current_page);
2630 if (core_popup != NULL) {
2631 draw_core_win_content(core_popup, core_info);
2632 }
2633 draw_thread_win_content(thread_win, &thread_info);
2634 refresh();
2635 pthread_mutex_unlock(&g_thread_lock);
2636 }
2637
2638 last_pollers_count = pollers_count;
2639 free(thread_info.name);
2640 free(thread_info.cpumask);
2641 thread_info.name = NULL;
2642 thread_info.cpumask = NULL;
2643 }
2644
2645 del_panel(thread_panel);
2646 delwin(thread_win);
2647 }
2648
2649 static void
show_thread(uint8_t current_page,uint8_t active_tab)2650 show_thread(uint8_t current_page, uint8_t active_tab)
2651 {
2652 uint64_t thread_number = current_page * g_max_data_rows + g_selected_row;
2653 uint64_t thread_id;
2654
2655 pthread_mutex_lock(&g_thread_lock);
2656 assert(thread_number < g_last_threads_count);
2657 thread_id = g_threads_info[thread_number].id;
2658 pthread_mutex_unlock(&g_thread_lock);
2659
2660 display_thread(thread_id, current_page, active_tab, NULL, NULL);
2661 }
2662
2663 static void
show_single_thread(uint64_t thread_id,uint8_t current_page,uint8_t active_tab,WINDOW * core_popup,struct rpc_core_info * core_info)2664 show_single_thread(uint64_t thread_id, uint8_t current_page, uint8_t active_tab, WINDOW *core_popup,
2665 struct rpc_core_info *core_info)
2666 {
2667 uint64_t i;
2668
2669 pthread_mutex_lock(&g_thread_lock);
2670 for (i = 0; i < g_last_threads_count; i++) {
2671 if (g_threads_info[i].id == thread_id) {
2672 pthread_mutex_unlock(&g_thread_lock);
2673 display_thread(thread_id, current_page, active_tab, core_popup, core_info);
2674 return;
2675 }
2676 }
2677 pthread_mutex_unlock(&g_thread_lock);
2678 }
2679
2680 static void
show_core(uint8_t current_page,uint8_t active_tab)2681 show_core(uint8_t current_page, uint8_t active_tab)
2682 {
2683 PANEL *core_panel;
2684 WINDOW *core_win;
2685 uint64_t core_number = current_page * g_max_data_rows + g_selected_row;
2686 struct rpc_core_info *core_info = &g_cores_info[core_number];
2687 uint64_t threads_count, i;
2688 uint64_t thread_id = 0;
2689 uint16_t current_threads_row;
2690 int c;
2691 long int time_last, time_dif;
2692 struct timespec time_now;
2693
2694 clock_gettime(CLOCK_MONOTONIC, &time_now);
2695 time_last = time_now.tv_sec;
2696
2697 bool stop_loop = false;
2698
2699 pthread_mutex_lock(&g_thread_lock);
2700 assert(core_number < g_last_cores_count);
2701
2702 threads_count = g_cores_info[core_number].threads.threads_count;
2703
2704 core_win = newwin(threads_count + CORE_WIN_HEIGHT, CORE_WIN_WIDTH,
2705 get_position_for_window(CORE_WIN_HEIGHT + threads_count, g_max_row),
2706 get_position_for_window(CORE_WIN_WIDTH, g_max_col));
2707
2708 keypad(core_win, TRUE);
2709 core_panel = new_panel(core_win);
2710
2711 top_panel(core_panel);
2712 update_panels();
2713 doupdate();
2714 draw_core_win_content(core_win, core_info);
2715 refresh();
2716
2717 current_threads_row = 0;
2718
2719 while (!stop_loop) {
2720 pthread_mutex_lock(&g_thread_lock);
2721 for (i = 0; i < core_info->threads.threads_count; i++) {
2722 if (i != current_threads_row) {
2723 mvwprintw(core_win, i + CORE_WIN_HEIGHT - 1, 1,
2724 "%s", core_info->threads.thread[i].name);
2725 } else {
2726 print_left(core_win, i + CORE_WIN_HEIGHT - 1, 1, CORE_WIN_WIDTH - 2,
2727 core_info->threads.thread[i].name, COLOR_PAIR(2));
2728 }
2729 }
2730 pthread_mutex_unlock(&g_thread_lock);
2731
2732 wrefresh(core_win);
2733 if (check_resize_interface(active_tab, ¤t_page)) {
2734 wclear(core_win);
2735 resize_interface(active_tab);
2736 draw_tab_win(active_tab);
2737 mvwin(core_win, get_position_for_window(CORE_WIN_HEIGHT + threads_count, g_max_row),
2738 get_position_for_window(CORE_WIN_WIDTH, g_max_col));
2739 }
2740
2741 c = getch();
2742 switch (c) {
2743 case 10: /* ENTER */
2744 pthread_mutex_lock(&g_thread_lock);
2745 if (core_info->threads.threads_count > 0) {
2746 thread_id = core_info->threads.thread[current_threads_row].id;
2747 }
2748 pthread_mutex_unlock(&g_thread_lock);
2749
2750 if (thread_id != 0) {
2751 show_single_thread(thread_id, current_page, active_tab, core_win, core_info);
2752 }
2753
2754 /* This refreshes tab and core_pop-up after exiting threads pop-up. */
2755 pthread_mutex_lock(&g_thread_lock);
2756 refresh_tab(active_tab, current_page);
2757 wnoutrefresh(core_win);
2758 refresh();
2759 pthread_mutex_unlock(&g_thread_lock);
2760 break;
2761 case 27: /* ESC */
2762 stop_loop = true;
2763 break;
2764 case KEY_UP:
2765 if (current_threads_row != 0) {
2766 current_threads_row--;
2767 }
2768 break;
2769 case KEY_DOWN:
2770 pthread_mutex_lock(&g_thread_lock);
2771 if (current_threads_row != core_info->threads.threads_count - 1) {
2772 current_threads_row++;
2773 }
2774 pthread_mutex_unlock(&g_thread_lock);
2775 break;
2776 default:
2777 break;
2778 }
2779
2780 clock_gettime(CLOCK_MONOTONIC, &time_now);
2781 time_dif = time_now.tv_sec - time_last;
2782
2783 if (time_dif >= g_sleep_time) {
2784 time_last = time_now.tv_sec;
2785 pthread_mutex_lock(&g_thread_lock);
2786 refresh_tab(active_tab, current_page);
2787 draw_core_win_content(core_win, core_info);
2788 refresh();
2789 pthread_mutex_unlock(&g_thread_lock);
2790 }
2791 }
2792
2793 del_panel(core_panel);
2794 delwin(core_win);
2795 }
2796
2797 static void
draw_poller_win_content(WINDOW * poller_win,struct rpc_poller_info * poller_info)2798 draw_poller_win_content(WINDOW *poller_win, struct rpc_poller_info *poller_info)
2799 {
2800 uint64_t last_run_counter, last_busy_counter, busy_count;
2801 char poller_period[MAX_TIME_STR_LEN];
2802
2803 box(poller_win, 0, 0);
2804
2805 print_in_middle(poller_win, 1, 0, POLLER_WIN_WIDTH, poller_info->name, COLOR_PAIR(3));
2806 mvwhline(poller_win, 2, 1, ACS_HLINE, POLLER_WIN_WIDTH - 2);
2807 mvwaddch(poller_win, 2, POLLER_WIN_WIDTH, ACS_RTEE);
2808
2809 print_left(poller_win, 3, 2, POLLER_WIN_WIDTH, "Type: On thread:", COLOR_PAIR(5));
2810 mvwprintw(poller_win, 3, POLLER_WIN_FIRST_COL, "%s",
2811 poller_type_str[poller_info->type]);
2812 mvwprintw(poller_win, 3, POLLER_WIN_FIRST_COL + 23, "%s", poller_info->thread_name);
2813
2814 print_left(poller_win, 4, 2, POLLER_WIN_WIDTH, "Run count:", COLOR_PAIR(5));
2815
2816 last_run_counter = get_last_run_counter(poller_info->id, poller_info->thread_id);
2817 last_busy_counter = get_last_busy_counter(poller_info->id, poller_info->thread_id);
2818 if (g_interval_data) {
2819 mvwprintw(poller_win, 4, POLLER_WIN_FIRST_COL, "%" PRIu64,
2820 poller_info->run_count - last_run_counter);
2821 } else {
2822 mvwprintw(poller_win, 4, POLLER_WIN_FIRST_COL, "%" PRIu64, poller_info->run_count);
2823 }
2824
2825 if (poller_info->period_ticks != 0) {
2826 print_left(poller_win, 4, 28, POLLER_WIN_WIDTH, "Period:", COLOR_PAIR(5));
2827 get_time_str(poller_info->period_ticks, poller_period);
2828 mvwprintw(poller_win, 4, POLLER_WIN_FIRST_COL + 23, "%s", poller_period);
2829 }
2830 mvwhline(poller_win, 5, 1, ACS_HLINE, POLLER_WIN_WIDTH - 2);
2831
2832 busy_count = g_interval_data ? poller_info->busy_count - last_busy_counter :
2833 poller_info->busy_count;
2834 if (busy_count != 0) {
2835 print_left(poller_win, 6, 2, POLLER_WIN_WIDTH, "Status: Busy count:", COLOR_PAIR(5));
2836
2837 if (g_interval_data == false && poller_info->busy_count == last_busy_counter) {
2838 print_left(poller_win, 6, POLLER_WIN_FIRST_COL, POLLER_WIN_WIDTH, "Idle", COLOR_PAIR(7));
2839 } else {
2840 print_left(poller_win, 6, POLLER_WIN_FIRST_COL, POLLER_WIN_WIDTH, "Busy", COLOR_PAIR(6));
2841 }
2842
2843 mvwprintw(poller_win, 6, POLLER_WIN_FIRST_COL + 23, "%" PRIu64, busy_count);
2844 } else {
2845 print_in_middle(poller_win, 6, 1, POLLER_WIN_WIDTH - 7, "Status:", COLOR_PAIR(5));
2846 print_in_middle(poller_win, 6, 1, POLLER_WIN_WIDTH + 6, "Idle", COLOR_PAIR(7));
2847 }
2848
2849 wnoutrefresh(poller_win);
2850 }
2851
2852 static void
show_poller(uint8_t current_page,uint8_t active_tab)2853 show_poller(uint8_t current_page, uint8_t active_tab)
2854 {
2855 PANEL *poller_panel;
2856 WINDOW *poller_win;
2857 uint64_t poller_number = current_page * g_max_data_rows + g_selected_row;
2858 struct rpc_poller_info *poller;
2859 bool stop_loop = false;
2860 int c;
2861 long int time_last, time_dif;
2862 struct timespec time_now;
2863
2864 clock_gettime(CLOCK_MONOTONIC, &time_now);
2865 time_last = time_now.tv_sec;
2866
2867
2868 pthread_mutex_lock(&g_thread_lock);
2869
2870 assert(poller_number < g_last_pollers_count);
2871 poller = &g_pollers_info[poller_number];
2872
2873 poller_win = newwin(POLLER_WIN_HEIGHT, POLLER_WIN_WIDTH,
2874 get_position_for_window(POLLER_WIN_HEIGHT, g_max_row),
2875 get_position_for_window(POLLER_WIN_WIDTH, g_max_col));
2876
2877 keypad(poller_win, TRUE);
2878 poller_panel = new_panel(poller_win);
2879
2880 top_panel(poller_panel);
2881 update_panels();
2882 doupdate();
2883 draw_poller_win_content(poller_win, poller);
2884 refresh();
2885
2886 pthread_mutex_unlock(&g_thread_lock);
2887 while (!stop_loop) {
2888 if (check_resize_interface(active_tab, ¤t_page)) {
2889 /* This clear is to avoid remaining artifacts after window has been moved */
2890 wclear(poller_win);
2891 resize_interface(active_tab);
2892 draw_tabs(active_tab, g_current_sort_col[active_tab], g_current_sort_col2[active_tab]);
2893 mvwin(poller_win, get_position_for_window(POLLER_WIN_HEIGHT, g_max_row),
2894 get_position_for_window(POLLER_WIN_WIDTH, g_max_col));
2895 }
2896 c = getch();
2897 switch (c) {
2898 case 27: /* ESC */
2899 stop_loop = true;
2900 break;
2901 default:
2902 break;
2903 }
2904
2905 clock_gettime(CLOCK_MONOTONIC, &time_now);
2906 time_dif = time_now.tv_sec - time_last;
2907
2908 if (time_dif >= g_sleep_time) {
2909 time_last = time_now.tv_sec;
2910 pthread_mutex_lock(&g_thread_lock);
2911 refresh_tab(active_tab, current_page);
2912 draw_poller_win_content(poller_win, poller);
2913 refresh();
2914 pthread_mutex_unlock(&g_thread_lock);
2915 }
2916 }
2917
2918 del_panel(poller_panel);
2919 delwin(poller_win);
2920 }
2921
2922 static uint64_t
get_max_scheduler_win_width(uint8_t sched_name_label_len,uint8_t sched_period_label_len,uint8_t gov_name_label_len)2923 get_max_scheduler_win_width(uint8_t sched_name_label_len, uint8_t sched_period_label_len,
2924 uint8_t gov_name_label_len)
2925 {
2926 uint8_t window_borders = 4;
2927 uint64_t s_name, s_period, g_name, max_name_len;
2928 char scheduler_period[MAX_SCHEDULER_PERIOD_STR_LEN];
2929
2930 snprintf(scheduler_period, MAX_SCHEDULER_PERIOD_STR_LEN, "%" PRIu64,
2931 g_scheduler_info.scheduler_period);
2932
2933 s_name = strlen(g_scheduler_info.scheduler_name) + sched_name_label_len;
2934 if (g_scheduler_info.governor_name != NULL) {
2935 g_name = strlen(g_scheduler_info.governor_name) + gov_name_label_len;
2936 } else {
2937 g_name = strlen("none") + gov_name_label_len;
2938 }
2939 s_period = strlen(scheduler_period) + sched_period_label_len;
2940
2941 max_name_len = spdk_max(s_name, g_name);
2942 /* This function relies on the fact that scheduler/governor
2943 * names will not change during runtime. Otherwise the scheduler
2944 * pop-up would need dynamic resizing. */
2945
2946 return spdk_max(max_name_len, s_period) + window_borders;
2947 }
2948
2949 static void
draw_scheduler_popup(WINDOW * scheduler_win,uint64_t scheduler_win_width,uint8_t active_tab,uint8_t current_page,const char * scheduler_name_label,const char * scheduler_period_label,const char * governor_name_label)2950 draw_scheduler_popup(WINDOW *scheduler_win, uint64_t scheduler_win_width, uint8_t active_tab,
2951 uint8_t current_page, const char *scheduler_name_label,
2952 const char *scheduler_period_label, const char *governor_name_label)
2953 {
2954 char scheduler_period[MAX_SCHEDULER_PERIOD_STR_LEN];
2955
2956 box(scheduler_win, 0, 0);
2957
2958 print_left(scheduler_win, 1, SCHEDULER_WIN_FIRST_COL, scheduler_win_width, scheduler_name_label,
2959 COLOR_PAIR(5));
2960 print_left(scheduler_win, 1, SCHEDULER_WIN_FIRST_COL + strlen(scheduler_name_label),
2961 scheduler_win_width,
2962 g_scheduler_info.scheduler_name, COLOR_PAIR(3));
2963
2964 mvwhline(scheduler_win, 2, 1, ACS_HLINE, scheduler_win_width - 2);
2965 mvwaddch(scheduler_win, 2, scheduler_win_width, ACS_RTEE);
2966
2967 print_left(scheduler_win, 3, SCHEDULER_WIN_FIRST_COL, scheduler_win_width, scheduler_period_label,
2968 COLOR_PAIR(5));
2969 snprintf(scheduler_period, MAX_SCHEDULER_PERIOD_STR_LEN, "%" PRIu64,
2970 g_scheduler_info.scheduler_period);
2971 mvwprintw(scheduler_win, 3, SCHEDULER_WIN_FIRST_COL + strlen(scheduler_period_label), "%s",
2972 scheduler_period);
2973
2974 mvwhline(scheduler_win, 4, 1, ACS_HLINE, scheduler_win_width - 2);
2975 mvwaddch(scheduler_win, 4, scheduler_win_width, ACS_RTEE);
2976
2977 print_left(scheduler_win, 5, SCHEDULER_WIN_FIRST_COL, scheduler_win_width, governor_name_label,
2978 COLOR_PAIR(5));
2979
2980 if (g_scheduler_info.governor_name != NULL) {
2981 mvwprintw(scheduler_win, 5, SCHEDULER_WIN_FIRST_COL + strlen(governor_name_label), "%s",
2982 g_scheduler_info.governor_name);
2983 } else {
2984 mvwprintw(scheduler_win, 5, SCHEDULER_WIN_FIRST_COL + strlen(governor_name_label), "%s", "none");
2985 }
2986
2987 refresh_tab(active_tab, current_page);
2988 wnoutrefresh(scheduler_win);
2989 refresh();
2990 }
2991
2992 static void
show_scheduler(uint8_t active_tab,uint8_t current_page)2993 show_scheduler(uint8_t active_tab, uint8_t current_page)
2994 {
2995 PANEL *scheduler_panel;
2996 WINDOW *scheduler_win;
2997 uint64_t scheduler_win_width;
2998 bool stop_loop = false;
2999 int c;
3000 const char *scheduler_name_label = "Scheduler: ";
3001 const char *scheduler_period_label = "Period [us]: ";
3002 const char *governor_name_label = "Governor: ";
3003
3004 pthread_mutex_lock(&g_thread_lock);
3005 scheduler_win_width = get_max_scheduler_win_width(strlen(scheduler_name_label),
3006 strlen(scheduler_period_label),
3007 strlen(governor_name_label));
3008
3009 scheduler_win = newwin(SCHEDULER_WIN_HEIGHT, scheduler_win_width,
3010 get_position_for_window(SCHEDULER_WIN_HEIGHT, g_max_row),
3011 get_position_for_window(scheduler_win_width, g_max_col));
3012
3013 keypad(scheduler_win, TRUE);
3014 scheduler_panel = new_panel(scheduler_win);
3015
3016 top_panel(scheduler_panel);
3017 update_panels();
3018 doupdate();
3019
3020 draw_scheduler_popup(scheduler_win, scheduler_win_width, active_tab, current_page,
3021 scheduler_name_label, scheduler_period_label, governor_name_label);
3022 pthread_mutex_unlock(&g_thread_lock);
3023
3024 while (!stop_loop) {
3025 c = wgetch(scheduler_win);
3026
3027 switch (c) {
3028 case 27: /* ESC */
3029 stop_loop = true;
3030 break;
3031 default:
3032 break;
3033 }
3034 }
3035
3036 del_panel(scheduler_panel);
3037 delwin(scheduler_win);
3038 }
3039
3040 static void *
data_thread_routine(void * arg)3041 data_thread_routine(void *arg)
3042 {
3043 int rc;
3044 uint64_t refresh_rate;
3045
3046 while (1) {
3047 pthread_mutex_lock(&g_thread_lock);
3048 if (g_quit_app) {
3049 pthread_mutex_unlock(&g_thread_lock);
3050 break;
3051 }
3052
3053 if (g_sleep_time == 0) {
3054 /* Give display thread time to redraw all windows */
3055 refresh_rate = SPDK_SEC_TO_USEC / 100;
3056 } else {
3057 refresh_rate = g_sleep_time * SPDK_SEC_TO_USEC;
3058 }
3059 pthread_mutex_unlock(&g_thread_lock);
3060
3061 /* Get data from RPC for each object type.
3062 * Start with cores since their number should not change. */
3063 rc = get_cores_data();
3064 if (rc) {
3065 print_bottom_message("ERROR occurred while getting cores data");
3066 }
3067 rc = get_thread_data();
3068 if (rc) {
3069 print_bottom_message("ERROR occurred while getting threads data");
3070 }
3071
3072 rc = get_pollers_data();
3073 if (rc) {
3074 print_bottom_message("ERROR occurred while getting pollers data");
3075 }
3076 rc = get_scheduler_data();
3077 if (rc) {
3078 print_bottom_message("ERROR occurred while getting scheduler data");
3079 }
3080
3081 usleep(refresh_rate);
3082 }
3083
3084 return NULL;
3085 }
3086
3087 static void
help_window_display(void)3088 help_window_display(void)
3089 {
3090 PANEL *help_panel;
3091 WINDOW *help_win;
3092 bool stop_loop = false;
3093 int c;
3094 uint64_t row = 1, col = 2, desc_second_row_col = 26, header_footer_col = 0;
3095
3096 help_win = newwin(HELP_WIN_HEIGHT, HELP_WIN_WIDTH,
3097 get_position_for_window(HELP_WIN_HEIGHT, g_max_row),
3098 get_position_for_window(HELP_WIN_WIDTH, g_max_col));
3099 help_panel = new_panel(help_win);
3100 top_panel(help_panel);
3101 update_panels();
3102 doupdate();
3103
3104 box(help_win, 0, 0);
3105
3106 /* Header */
3107 print_in_middle(help_win, row, header_footer_col, HELP_WIN_WIDTH, "HELP", COLOR_PAIR(3));
3108 mvwhline(help_win, 2, 1, ACS_HLINE, HELP_WIN_WIDTH - 2);
3109 mvwaddch(help_win, 2, HELP_WIN_WIDTH, ACS_RTEE);
3110 row = 3;
3111
3112 /* Content */
3113 print_left(help_win, row, col, HELP_WIN_WIDTH, "MENU options", COLOR_PAIR(5));
3114 print_left(help_win, ++row, ++col, HELP_WIN_WIDTH, "[q] Quit - quit this application",
3115 COLOR_PAIR(10));
3116 print_left(help_win, ++row, col, HELP_WIN_WIDTH,
3117 "[Tab] Next tab - switch to next tab", COLOR_PAIR(10));
3118 print_left(help_win, ++row, col, HELP_WIN_WIDTH,
3119 "[1-3] Select tab - switch to THREADS, POLLERS or CORES tab", COLOR_PAIR(10));
3120 print_left(help_win, ++row, col, HELP_WIN_WIDTH,
3121 "[PgUp] Previous page - scroll up to previous page", COLOR_PAIR(10));
3122 print_left(help_win, ++row, col, HELP_WIN_WIDTH,
3123 "[PgDown] Next page - scroll down to next page", COLOR_PAIR(10));
3124 print_left(help_win, ++row, col, HELP_WIN_WIDTH,
3125 "[Up] Arrow key - go to previous data row", COLOR_PAIR(10));
3126 print_left(help_win, ++row, col, HELP_WIN_WIDTH,
3127 "[Down] Arrow key - go to next data row", COLOR_PAIR(10));
3128 print_left(help_win, ++row, col, HELP_WIN_WIDTH,
3129 "[Right] Arrow key - go to second sorting window", COLOR_PAIR(10));
3130 print_left(help_win, ++row, col, HELP_WIN_WIDTH,
3131 "[Left] Arrow key - close second sorting window", COLOR_PAIR(10));
3132 print_left(help_win, ++row, col, HELP_WIN_WIDTH,
3133 "[c] Columns - choose data columns to display", COLOR_PAIR(10));
3134 print_left(help_win, ++row, col, HELP_WIN_WIDTH,
3135 "[s] Sorting - change sorting by column", COLOR_PAIR(10));
3136 print_left(help_win, ++row, col, HELP_WIN_WIDTH,
3137 "[r] Refresh rate - set refresh rate <0, 255> in seconds", COLOR_PAIR(10));
3138 print_left(help_win, ++row, desc_second_row_col, HELP_WIN_WIDTH, "that value in seconds",
3139 COLOR_PAIR(10));
3140 print_left(help_win, ++row, col, HELP_WIN_WIDTH,
3141 "[Enter] Item details - show current data row details (Enter to open, Esc to close)",
3142 COLOR_PAIR(10));
3143 print_left(help_win, ++row, col, HELP_WIN_WIDTH,
3144 "[t] Total/Interval - switch to display data measured from the start of SPDK", COLOR_PAIR(10));
3145 print_left(help_win, ++row, desc_second_row_col, HELP_WIN_WIDTH,
3146 "application or last refresh", COLOR_PAIR(10));
3147 print_left(help_win, ++row, col, HELP_WIN_WIDTH,
3148 "[g] Scheduler pop-up - display current scheduler information", COLOR_PAIR(10));
3149 print_left(help_win, ++row, col, HELP_WIN_WIDTH, "[h] Help - show this help window",
3150 COLOR_PAIR(10));
3151
3152 /* Footer */
3153 mvwhline(help_win, HELP_WIN_HEIGHT - 3, 1, ACS_HLINE, HELP_WIN_WIDTH - 2);
3154 mvwaddch(help_win, HELP_WIN_HEIGHT - 3, HELP_WIN_WIDTH, ACS_RTEE);
3155
3156 print_in_middle(help_win, HELP_WIN_HEIGHT - 2, header_footer_col, HELP_WIN_WIDTH,
3157 "[Esc] Close this window", COLOR_PAIR(10));
3158
3159 refresh();
3160 wrefresh(help_win);
3161
3162 while (!stop_loop) {
3163 c = wgetch(help_win);
3164
3165 switch (c) {
3166 case 27: /* ESC */
3167 stop_loop = true;
3168 break;
3169 default:
3170 break;
3171 }
3172 }
3173
3174 del_panel(help_panel);
3175 delwin(help_win);
3176
3177 }
3178
3179 static void
refresh_after_popup(uint8_t active_tab,uint8_t * max_pages,uint8_t current_page)3180 refresh_after_popup(uint8_t active_tab, uint8_t *max_pages, uint8_t current_page)
3181 {
3182 int i;
3183
3184 /* After closing pop-up there would be unrefreshed parts
3185 * of the tab, so this is to refresh them */
3186 draw_tabs(active_tab, g_current_sort_col[active_tab], g_current_sort_col2[active_tab]);
3187 pthread_mutex_lock(&g_thread_lock);
3188 *max_pages = refresh_tab(active_tab, current_page);
3189 pthread_mutex_unlock(&g_thread_lock);
3190 top_panel(g_panels[active_tab]);
3191
3192 for (i = 0; i < NUMBER_OF_TABS; i++) {
3193 wclear(g_tab_win[i]);
3194 wresize(g_tab_win[i], TAB_WIN_HEIGHT,
3195 (g_max_col - (TABS_SPACING * NUMBER_OF_TABS)) / NUMBER_OF_TABS);
3196 mvwin(g_tab_win[i], TAB_WIN_LOCATION_ROW, 1 + (g_max_col / NUMBER_OF_TABS) * i);
3197 draw_tab_win(i);
3198 }
3199
3200 update_panels();
3201 refresh();
3202 }
3203
3204 static void
show_stats(pthread_t * data_thread)3205 show_stats(pthread_t *data_thread)
3206 {
3207 const int CURRENT_PAGE_STR_LEN = 50;
3208 long int time_last, time_dif;
3209 struct timespec time_now;
3210 int c;
3211 uint8_t active_tab = THREADS_TAB;
3212 uint8_t current_page = 0;
3213 uint8_t max_pages = 1;
3214 uint64_t i;
3215 char current_page_str[CURRENT_PAGE_STR_LEN];
3216 bool force_refresh = true;
3217
3218 clock_gettime(CLOCK_MONOTONIC, &time_now);
3219 time_last = time_now.tv_sec;
3220
3221 switch_tab(THREADS_TAB);
3222
3223 while (1) {
3224 check_resize_interface(active_tab, ¤t_page);
3225
3226 clock_gettime(CLOCK_MONOTONIC, &time_now);
3227 time_dif = time_now.tv_sec - time_last;
3228 if (time_dif < 0) {
3229 time_dif = g_sleep_time;
3230 }
3231
3232 if (time_dif >= g_sleep_time || force_refresh) {
3233 time_last = time_now.tv_sec;
3234 pthread_mutex_lock(&g_thread_lock);
3235 max_pages = refresh_tab(active_tab, current_page);
3236 pthread_mutex_unlock(&g_thread_lock);
3237
3238 snprintf(current_page_str, CURRENT_PAGE_STR_LEN - 1, "Page: %d/%d", current_page + 1, max_pages);
3239 mvprintw(g_max_row - 1, 1, "%s", current_page_str);
3240
3241 refresh();
3242 }
3243
3244 c = getch();
3245 if (c == 'q') {
3246 pthread_mutex_lock(&g_thread_lock);
3247 g_quit_app = true;
3248 pthread_mutex_unlock(&g_thread_lock);
3249 break;
3250 }
3251
3252 force_refresh = true;
3253
3254 switch (c) {
3255 case '1':
3256 case '2':
3257 case '3':
3258 active_tab = c - '1';
3259 current_page = 0;
3260 g_selected_row = 0;
3261 switch_tab(active_tab);
3262 break;
3263 case '\t':
3264 if (active_tab < NUMBER_OF_TABS - 1) {
3265 active_tab++;
3266 } else {
3267 active_tab = THREADS_TAB;
3268 }
3269 g_selected_row = 0;
3270 current_page = 0;
3271 switch_tab(active_tab);
3272 break;
3273 case 's':
3274 sort_type2(active_tab, COL_THREADS_NONE);
3275 change_sorting(active_tab, 0, NULL);
3276 break;
3277 case 'c':
3278 filter_columns(active_tab);
3279 break;
3280 case 'r':
3281 change_refresh_rate();
3282 break;
3283 case 't':
3284 g_interval_data = !g_interval_data;
3285 break;
3286 case 'g':
3287 show_scheduler(active_tab, current_page);
3288 break;
3289 case KEY_NPAGE: /* PgDown */
3290 if (current_page + 1 < max_pages) {
3291 current_page++;
3292 }
3293 wclear(g_tabs[active_tab]);
3294 g_selected_row = 0;
3295 draw_tabs(active_tab, g_current_sort_col[active_tab], g_current_sort_col2[active_tab]);
3296 break;
3297 case KEY_PPAGE: /* PgUp */
3298 if (current_page > 0) {
3299 current_page--;
3300 }
3301 wclear(g_tabs[active_tab]);
3302 g_selected_row = 0;
3303 draw_tabs(active_tab, g_current_sort_col[active_tab], g_current_sort_col2[active_tab]);
3304 break;
3305 case KEY_UP: /* Arrow up */
3306 if (g_selected_row > 0) {
3307 g_selected_row--;
3308 } else if (g_selected_row == 0) {
3309 if (current_page > 0) {
3310 current_page--;
3311 g_selected_row = g_max_data_rows - 1;
3312 wclear(g_tabs[active_tab]);
3313 draw_tabs(active_tab, g_current_sort_col[active_tab], g_current_sort_col2[active_tab]);
3314 }
3315 }
3316 break;
3317 case KEY_DOWN: /* Arrow down */
3318 if (g_selected_row < g_max_selected_row) {
3319 g_selected_row++;
3320 } else if (g_selected_row == g_max_selected_row) {
3321 if (current_page + 1 < max_pages) {
3322 current_page++;
3323 g_selected_row = 0;
3324 wclear(g_tabs[active_tab]);
3325 draw_tabs(active_tab, g_current_sort_col[active_tab], g_current_sort_col2[active_tab]);
3326 }
3327 }
3328 break;
3329 case 10: /* Enter */
3330 if (active_tab == THREADS_TAB) {
3331 show_thread(current_page, active_tab);
3332 } else if (active_tab == CORES_TAB) {
3333 show_core(current_page, active_tab);
3334 } else if (active_tab == POLLERS_TAB) {
3335 show_poller(current_page, active_tab);
3336 }
3337 snprintf(current_page_str, CURRENT_PAGE_STR_LEN - 1, "Page: %d/%d", current_page + 1, max_pages);
3338 mvprintw(g_max_row - 1, 1, "%s", current_page_str);
3339 refresh_after_popup(active_tab, &max_pages, current_page);
3340 break;
3341 case 'h':
3342 help_window_display();
3343 refresh_after_popup(active_tab, &max_pages, current_page);
3344 break;
3345 default:
3346 force_refresh = false;
3347 break;
3348 }
3349 }
3350
3351 pthread_join(*data_thread, NULL);
3352
3353 free_poller_history();
3354
3355 /* Free memory holding current data states before quitting application */
3356 for (i = 0; i < g_last_pollers_count; i++) {
3357 free_rpc_poller(&g_pollers_info[i]);
3358 }
3359 for (i = 0; i < g_last_threads_count; i++) {
3360 free_rpc_threads_stats(&g_threads_info[i]);
3361 }
3362 free_rpc_core_info(g_cores_info, g_last_cores_count);
3363 free_rpc_scheduler(&g_scheduler_info);
3364 }
3365
3366 static void
draw_interface(void)3367 draw_interface(void)
3368 {
3369 int i;
3370 uint16_t required_size = WINDOW_HEADER + 1;
3371
3372 getmaxyx(stdscr, g_max_row, g_max_col);
3373 g_max_row = spdk_max(g_max_row, required_size);
3374 g_data_win_size = g_max_row - required_size;
3375 g_max_data_rows = g_max_row - WINDOW_HEADER;
3376
3377 g_menu_win = newwin(MENU_WIN_HEIGHT, g_max_col, g_max_row - MENU_WIN_HEIGHT - 1,
3378 MENU_WIN_LOCATION_COL);
3379 assert(g_menu_win != NULL);
3380 draw_menu_win();
3381
3382 for (i = 0; i < NUMBER_OF_TABS; i++) {
3383 g_tab_win[i] = newwin(TAB_WIN_HEIGHT, g_max_col / NUMBER_OF_TABS - TABS_SPACING,
3384 TAB_WIN_LOCATION_ROW, g_max_col / NUMBER_OF_TABS * i + 1);
3385 assert(g_tab_win[i] != NULL);
3386 draw_tab_win(i);
3387
3388 g_tabs[i] = newwin(g_max_row - MENU_WIN_HEIGHT - TAB_WIN_HEIGHT - 2, g_max_col, TABS_LOCATION_ROW,
3389 TABS_LOCATION_COL);
3390 draw_tabs(i, g_current_sort_col[i], g_current_sort_col2[i]);
3391 g_panels[i] = new_panel(g_tabs[i]);
3392 assert(g_panels[i] != NULL);
3393 }
3394
3395 update_panels();
3396 doupdate();
3397 }
3398
3399 static void
finish(int sig)3400 finish(int sig)
3401 {
3402 /* End ncurses mode */
3403 endwin();
3404 spdk_jsonrpc_client_close(g_rpc_client);
3405 exit(0);
3406 }
3407
3408 static void
setup_ncurses(void)3409 setup_ncurses(void)
3410 {
3411 clear();
3412 noecho();
3413 timeout(1);
3414 curs_set(0);
3415 keypad(stdscr, TRUE);
3416 start_color();
3417 init_pair(1, COLOR_BLACK, COLOR_GREEN);
3418 init_pair(2, COLOR_BLACK, COLOR_WHITE);
3419 init_pair(3, COLOR_YELLOW, COLOR_BLACK);
3420 init_pair(4, COLOR_BLACK, COLOR_YELLOW);
3421 init_pair(5, COLOR_GREEN, COLOR_BLACK);
3422 init_pair(6, COLOR_RED, COLOR_BLACK);
3423 init_pair(7, COLOR_BLUE, COLOR_BLACK);
3424 init_pair(8, COLOR_RED, COLOR_WHITE);
3425 init_pair(9, COLOR_BLUE, COLOR_WHITE);
3426 init_pair(10, COLOR_WHITE, COLOR_BLACK);
3427
3428 if (has_colors() == FALSE) {
3429 endwin();
3430 printf("Your terminal does not support color\n");
3431 exit(1);
3432 }
3433
3434 /* Handle signals to exit gracefully cleaning up ncurses */
3435 (void) signal(SIGINT, finish);
3436 (void) signal(SIGPIPE, finish);
3437 (void) signal(SIGABRT, finish);
3438 }
3439
3440 static void
usage(const char * program_name)3441 usage(const char *program_name)
3442 {
3443 printf("%s [options]", program_name);
3444 printf("\n");
3445 printf("options:\n");
3446 printf(" -r <path> RPC connect address (default: /var/tmp/spdk.sock)\n");
3447 printf(" -h show this usage\n");
3448 }
3449
3450 static int
rpc_decode_tick_rate(struct spdk_json_val * val,uint64_t * tick_rate)3451 rpc_decode_tick_rate(struct spdk_json_val *val, uint64_t *tick_rate)
3452 {
3453 struct t_rate {
3454 uint64_t tr;
3455 };
3456
3457 const struct spdk_json_object_decoder rpc_tick_rate_decoder[] = {
3458 {"tick_rate", offsetof(struct t_rate, tr), spdk_json_decode_uint64}
3459 };
3460
3461 int rc;
3462 struct t_rate tmp;
3463
3464 rc = spdk_json_decode_object_relaxed(val, rpc_tick_rate_decoder,
3465 SPDK_COUNTOF(rpc_tick_rate_decoder), &tmp);
3466
3467 *tick_rate = tmp.tr;
3468
3469 return rc;
3470 }
3471
3472 static int
wait_init(pthread_t * data_thread)3473 wait_init(pthread_t *data_thread)
3474 {
3475 struct spdk_jsonrpc_client_response *json_resp = NULL;
3476 char *uninit_log = "Waiting for SPDK target application to initialize...",
3477 *uninit_error = "Unable to read SPDK application state!";
3478 int c, max_col, rc = 0;
3479 uint64_t tick_rate;
3480
3481 max_col = getmaxx(stdscr);
3482 print_in_middle(stdscr, FIRST_DATA_ROW, 1, max_col, uninit_log, COLOR_PAIR(5));
3483 rc = rpc_send_req("framework_wait_init", &json_resp);
3484 if (rc) {
3485 while (1) {
3486 print_in_middle(stdscr, FIRST_DATA_ROW, 1, max_col, uninit_error, COLOR_PAIR(8));
3487 c = getch();
3488 if (c == 'q') {
3489 return -1;
3490 }
3491 }
3492 }
3493
3494 spdk_jsonrpc_client_free_response(json_resp);
3495
3496 rc = pthread_mutex_init(&g_thread_lock, NULL);
3497 if (rc) {
3498 fprintf(stderr, "mutex lock failed to initialize: %d\n", errno);
3499 return -1;
3500 }
3501
3502 memset(&g_threads_info, 0, sizeof(struct rpc_thread_info) * RPC_MAX_THREADS);
3503 memset(&g_cores_info, 0, sizeof(struct rpc_core_info) * RPC_MAX_CORES);
3504
3505 /* Decode tick rate */
3506 rc = rpc_send_req("framework_get_reactors", &json_resp);
3507 if (rc) {
3508 return rc;
3509 }
3510
3511 if (rpc_decode_tick_rate(json_resp->result, &tick_rate)) {
3512 spdk_jsonrpc_client_free_response(json_resp);
3513 return -EINVAL;
3514 }
3515
3516 spdk_jsonrpc_client_free_response(json_resp);
3517
3518 g_tick_rate = tick_rate;
3519
3520 /* This is to get first batch of data for display functions.
3521 * Since data thread makes RPC calls that take more time than
3522 * startup of display functions on main thread, without these
3523 * calls both threads would be subject to a race condition. */
3524 rc = get_thread_data();
3525 if (rc) {
3526 return -1;
3527 }
3528
3529 rc = get_pollers_data();
3530 if (rc) {
3531 return -1;
3532 }
3533
3534 rc = get_cores_data();
3535 if (rc) {
3536 return -1;
3537 }
3538
3539 rc = pthread_create(data_thread, NULL, &data_thread_routine, NULL);
3540 if (rc) {
3541 fprintf(stderr, "data thread creation failed: %d\n", errno);
3542 return -1;
3543 }
3544 return 0;
3545 }
3546
3547 int
main(int argc,char ** argv)3548 main(int argc, char **argv)
3549 {
3550 int op, rc;
3551 char *socket = SPDK_DEFAULT_RPC_ADDR;
3552 pthread_t data_thread;
3553
3554 while ((op = getopt(argc, argv, "r:h")) != -1) {
3555 switch (op) {
3556 case 'r':
3557 socket = optarg;
3558 break;
3559 default:
3560 usage(argv[0]);
3561 return op == 'h' ? 0 : 1;
3562 }
3563 }
3564
3565 g_rpc_client = spdk_jsonrpc_client_connect(socket, socket[0] == '/' ? AF_UNIX : AF_INET);
3566 if (!g_rpc_client) {
3567 fprintf(stderr, "spdk_jsonrpc_client_connect() failed: %d\n", errno);
3568 return 1;
3569 }
3570
3571 initscr();
3572 init_str_len();
3573 setup_ncurses();
3574 draw_interface();
3575
3576 rc = wait_init(&data_thread);
3577 if (!rc) {
3578 show_stats(&data_thread);
3579 }
3580
3581 finish(0);
3582
3583 return (0);
3584 }
3585