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