Commit 21ee6348 authored by Maciej Szwed's avatar Maciej Szwed Committed by Tomasz Zawadzki
Browse files

spdk_top: Add filtering mechanism



Signed-off-by: default avatarMaciej Szwed <maciej.szwed@intel.com>
Change-Id: Ibebf3eef3e29f5f80cc9ab37cbd6b837c62d49b2
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/1133


Community-CI: Mellanox Build Bot
Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: default avatarShuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-by: default avatarJim Harris <james.r.harris@intel.com>
Reviewed-by: default avatarBen Walker <benjamin.walker@intel.com>
parent 57dc867e
Loading
Loading
Loading
Loading
+255 −42
Original line number Diff line number Diff line
@@ -80,6 +80,12 @@ enum spdk_poller_type {
	SPDK_POLLER_TYPES_COUNT,
};

struct col_desc {
	const char *name;
	uint8_t name_len;
	bool disabled;
};

const char *poller_type_str[SPDK_POLLER_TYPES_COUNT] = {"Active", "Timed", "Paused"};
const char *g_tab_title[NUMBER_OF_TABS] = {"[1] THREADS", "[2] POLLERS", "[3] CORES"};
struct spdk_jsonrpc_client *g_rpc_client;
@@ -89,23 +95,20 @@ uint16_t g_max_row, g_max_col;
uint16_t g_data_win_size;
uint32_t g_last_threads_count, g_last_pollers_count, g_last_cores_count;
uint8_t g_current_sort_col[NUMBER_OF_TABS] = {0, 0, 0};
static const char *g_col_desc[NUMBER_OF_TABS][TABS_COL_COUNT] = {
	{
		"     Thread name     ",
		"     Active pollers     ",
		"     Timed pollers     ",
		"     Paused pollers     ",
		(char *)NULL
static struct col_desc g_col_desc[NUMBER_OF_TABS][TABS_COL_COUNT] = {
	{	{.name = "     Thread name     "},
		{.name = "     Active pollers     "},
		{.name = "     Timed pollers     "},
		{.name = "     Paused pollers     "},
		{.name = (char *)NULL}
	},
	{
		"          Poller name          ",
		"     Type     ",
		"     On thread     ",
		(char *)NULL
	{	{.name = "          Poller name          "},
		{.name = "     Type     "},
		{.name = "     On thread     "},
		{.name = (char *)NULL}
	},
	{
		"     Core     ",
		(char *)NULL
	{	{.name = "     Core     "},
		{.name = (char *)NULL}
	}
};

@@ -165,6 +168,18 @@ struct rpc_pollers_stats {
struct rpc_threads_stats g_threads_stats;
struct rpc_pollers_stats g_pollers_stats;

static void
init_str_len(void)
{
	int i, j;

	for (i = 0; i < NUMBER_OF_TABS; i++) {
		for (j = 0; g_col_desc[i][j].name != NULL; j++) {
			g_col_desc[i][j].name_len = strlen(g_col_desc[i][j].name);
		}
	}
}

static void
free_rpc_threads_stats(struct rpc_threads_stats *req)
{
@@ -425,7 +440,7 @@ draw_menu_win(void)
	wbkgd(g_menu_win, COLOR_PAIR(2));
	box(g_menu_win, 0, 0);
	print_max_len(g_menu_win, 1, 1,
		      "   [q] Quit   |   [1-3] TAB selection   |   [PgUp] Previous page   |   [PgDown] Next page   |   [f] Filters   |   [s] Sorting");
		      "   [q] Quit   |   [1-3] TAB selection   |   [PgUp] Previous page   |   [PgDown] Next page   |   [c] Columns   |   [s] Sorting");
}

static void
@@ -443,34 +458,40 @@ draw_tab_win(enum tabs tab)
}

static void
draw_tabs(enum tabs tab, uint8_t sort_col)
draw_tabs(enum tabs tab_index, uint8_t sort_col)
{
	struct col_desc *col_desc = g_col_desc[tab_index];
	WINDOW *tab = g_tabs[tab_index];
	int i, j;
	uint16_t offset;

	for (i = 0; g_col_desc[tab][i] != NULL; i++) {
	for (i = 0; col_desc[i].name != NULL; i++) {
		if (col_desc[i].disabled) {
			continue;
		}

		offset = 1;
		for (j = i; j != 0; j--) {
			offset += strlen(g_col_desc[tab][j - 1]) + 1;
			offset += strlen(col_desc[j - 1].name) + 1;
		}

		if (i == sort_col) {
			wattron(g_tabs[tab], COLOR_PAIR(3));
			print_max_len(g_tabs[tab], 1, offset, g_col_desc[tab][i]);
			wattroff(g_tabs[tab], COLOR_PAIR(3));
			wattron(tab, COLOR_PAIR(3));
			print_max_len(tab, 1, offset, col_desc[i].name);
			wattroff(tab, COLOR_PAIR(3));
		} else {
			print_max_len(g_tabs[tab], 1, offset, g_col_desc[tab][i]);
			print_max_len(tab, 1, offset, col_desc[i].name);
		}

		if (g_col_desc[tab][i + 1] != NULL) {
			print_max_len(g_tabs[tab], 1, offset + strlen(g_col_desc[tab][i]), "|");
		if (col_desc[i + 1].name != NULL) {
			print_max_len(tab, 1, offset + col_desc[i].name_len, "|");
		}
	}

	print_max_len(g_tabs[tab], 2, 1, ""); /* Move to next line */
	whline(g_tabs[tab], ACS_HLINE, MAX_STRING_LEN);
	box(g_tabs[tab], 0, 0);
	wrefresh(g_tabs[tab]);
	print_max_len(tab, 2, 1, ""); /* Move to next line */
	whline(tab, ACS_HLINE, MAX_STRING_LEN);
	box(tab, 0, 0);
	wrefresh(tab);
}

static void
@@ -551,6 +572,7 @@ sort_threads(const void *p1, const void *p2)
static void
refresh_threads_tab(void)
{
	struct col_desc *col_desc = g_col_desc[THREADS_TAB];
	uint64_t i, threads_count;
	uint16_t j;
	uint16_t col;
@@ -578,21 +600,29 @@ refresh_threads_tab(void)

	for (i = 0; i < threads_count; i++) {
		col = TABS_DATA_START_COL;
		if (!col_desc[0].disabled) {
			print_max_len(g_tabs[THREADS_TAB], TABS_DATA_START_ROW + i, col, thread_info[i]->name);
		}

		col += MAX_THREAD_NAME_DISP;
		if (!col_desc[1].disabled) {
			snprintf(pollers_number, MAX_POLLER_COUNT_STR, "%ld", thread_info[i]->active_pollers_count);
			print_max_len(g_tabs[THREADS_TAB], TABS_DATA_START_ROW + i, col, pollers_number);
		}

		col += MAX_POLLER_COUNT_DISP;
		if (!col_desc[2].disabled) {
			snprintf(pollers_number, MAX_POLLER_COUNT_STR, "%ld", thread_info[i]->timed_pollers_count);
			print_max_len(g_tabs[THREADS_TAB], TABS_DATA_START_ROW + i, col, pollers_number);
		}

		col += MAX_POLLER_COUNT_DISP;
		if (!col_desc[3].disabled) {
			snprintf(pollers_number, MAX_POLLER_COUNT_STR, "%ld", thread_info[i]->paused_pollers_count);
			print_max_len(g_tabs[THREADS_TAB], TABS_DATA_START_ROW + i, col, pollers_number);
		}
	}
}

enum sort_type {
	BY_NAME,
@@ -652,6 +682,7 @@ copy_pollers(struct rpc_pollers *pollers, uint64_t pollers_count, enum spdk_poll
static void
refresh_pollers_tab(void)
{
	struct col_desc *col_desc = g_col_desc[POLLERS_TAB];
	struct rpc_poller_thread_info *thread;
	uint64_t i, count = 0;
	uint16_t col, j;
@@ -691,15 +722,23 @@ refresh_pollers_tab(void)
	for (i = 0; i < count; i++) {
		col = TABS_DATA_START_COL;

		if (!col_desc[0].disabled) {
			print_max_len(g_tabs[POLLERS_TAB], TABS_DATA_START_ROW + i, col, pollers[i]->name);
		}

		col += MAX_POLLER_NAME_DISP;

		if (!col_desc[1].disabled) {
			print_max_len(g_tabs[POLLERS_TAB], TABS_DATA_START_ROW + i, col, poller_type_str[pollers[i]->type]);
		}

		col += MAX_POLLER_TYPE_DISP;

		if (!col_desc[2].disabled) {
			print_max_len(g_tabs[POLLERS_TAB], TABS_DATA_START_ROW + i, col, pollers[i]->thread_name);
		}
	}
}

static void
refresh_cores_tab(void)
@@ -741,6 +780,176 @@ print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, ch
	refresh();
}

static void
apply_filters(enum tabs tab)
{
	wclear(g_tabs[tab]);
	draw_tabs(tab, g_current_sort_col[tab]);
}

static ITEM **
draw_filtering_menu(uint8_t position, WINDOW *filter_win, uint8_t tab, MENU **my_menu)
{
	const int ADDITIONAL_ELEMENTS = 3;
	const int ROW_PADDING = 6;
	const int WINDOW_START_X = 1;
	const int WINDOW_START_Y = 3;
	const int WINDOW_COLUMNS = 2;
	struct col_desc *col_desc = g_col_desc[tab];
	ITEM **my_items;
	MENU *menu;
	int i, elements;
	uint8_t len = 0;

	for (i = 0; col_desc[i].name != NULL; ++i) {
		len = spdk_max(col_desc[i].name_len, len);
	}

	elements = i;

	my_items = (ITEM **)calloc(elements * WINDOW_COLUMNS + ADDITIONAL_ELEMENTS, sizeof(ITEM *));

	for (i = 0; i < elements * 2; i++) {
		my_items[i] = new_item(col_desc[i / WINDOW_COLUMNS].name, NULL);
		i++;
		my_items[i] = new_item(col_desc[i / WINDOW_COLUMNS].disabled ? "[ ]" : "[*]", NULL);
	}

	my_items[i] = new_item("CLOSE", NULL);
	set_item_userptr(my_items[i], apply_filters);

	menu = new_menu((ITEM **)my_items);

	menu_opts_off(menu, O_SHOWDESC);
	set_menu_format(menu, elements + 1, WINDOW_COLUMNS);

	set_menu_win(menu, filter_win);
	set_menu_sub(menu, derwin(filter_win, elements + 1, len + ROW_PADDING, WINDOW_START_Y,
				  WINDOW_START_X));

	*my_menu = menu;

	post_menu(menu);
	refresh();
	wrefresh(filter_win);

	for (i = 0; i < position / WINDOW_COLUMNS; i++) {
		menu_driver(menu, REQ_DOWN_ITEM);
	}

	return my_items;
}

static void
delete_filtering_menu(MENU *my_menu, ITEM **my_items, uint8_t elements)
{
	int i;

	unpost_menu(my_menu);
	free_menu(my_menu);
	for (i = 0; i < elements * 2 + 2; ++i) {
		free_item(my_items[i]);
	}
	free(my_items);
}

static ITEM **
refresh_filtering_menu(MENU **my_menu, WINDOW *filter_win, uint8_t tab, ITEM **my_items,
		       uint8_t elements, uint8_t position)
{
	delete_filtering_menu(*my_menu, my_items, elements);
	return draw_filtering_menu(position, filter_win, tab, my_menu);
}

static void
filter_columns(uint8_t tab)
{
	const int WINDOW_HEADER_LEN = 5;
	const int WINDOW_BORDER_LEN = 8;
	const int WINDOW_HEADER_END_LINE = 2;
	const int WINDOW_COLUMNS = 2;
	struct col_desc *col_desc = g_col_desc[tab];
	PANEL *filter_panel;
	WINDOW *filter_win;
	ITEM **my_items;
	MENU *my_menu;
	int i, c, elements;
	bool stop_loop = false;
	ITEM *cur;
	void (*p)(enum tabs tab);
	uint8_t current_index, len = 0;

	for (i = 0; col_desc[i].name != NULL; ++i) {
		len = spdk_max(col_desc[i].name_len, len);
	}

	elements = i;

	filter_win = newwin(elements + WINDOW_HEADER_LEN, len + WINDOW_BORDER_LEN,
			    (g_max_row - elements - 1) / 2, (g_max_col - len) / 2);
	keypad(filter_win, TRUE);
	filter_panel = new_panel(filter_win);

	top_panel(filter_panel);
	update_panels();
	doupdate();

	box(filter_win, 0, 0);

	print_in_middle(filter_win, 1, 0, len + WINDOW_BORDER_LEN, "Filtering", COLOR_PAIR(3));
	mvwaddch(filter_win, WINDOW_HEADER_END_LINE, 0, ACS_LTEE);
	mvwhline(filter_win, WINDOW_HEADER_END_LINE, 1, ACS_HLINE, len + WINDOW_BORDER_LEN - 2);
	mvwaddch(filter_win, WINDOW_HEADER_END_LINE, len + WINDOW_BORDER_LEN - 1, ACS_RTEE);

	my_items = draw_filtering_menu(0, filter_win, tab, &my_menu);

	while (!stop_loop) {
		c = wgetch(filter_win);

		switch (c) {
		case KEY_DOWN:
			menu_driver(my_menu, REQ_DOWN_ITEM);
			break;
		case KEY_UP:
			menu_driver(my_menu, REQ_UP_ITEM);
			break;
		case 27: /* ESC */
		case 'q':
			stop_loop = true;
			break;
		case ' ': /* Space */
			cur = current_item(my_menu);
			current_index = item_index(cur) / WINDOW_COLUMNS;
			col_desc[current_index].disabled = !col_desc[current_index].disabled;
			my_items = refresh_filtering_menu(&my_menu, filter_win, tab, my_items, elements,
							  item_index(cur) + 1);
			break;
		case 10: /* Enter */
			cur = current_item(my_menu);
			current_index = item_index(cur) / WINDOW_COLUMNS;
			if (current_index == elements) {
				stop_loop = true;
				p = item_userptr(cur);
				p(tab);
			} else {
				col_desc[current_index].disabled = !col_desc[current_index].disabled;
				my_items = refresh_filtering_menu(&my_menu, filter_win, tab, my_items, elements,
								  item_index(cur) + 1);
			}
			break;
		}
		wrefresh(filter_win);
	}

	delete_filtering_menu(my_menu, my_items, elements);

	del_panel(filter_panel);
	delwin(filter_win);

	wclear(g_menu_win);
	draw_menu_win();
}

static void
sort_type(enum tabs tab, int item_index)
{
@@ -767,8 +976,8 @@ change_sorting(uint8_t tab)
	void (*p)(enum tabs tab, int item_index);
	uint8_t len = 0;

	for (i = 0; g_col_desc[tab][i] != NULL; ++i) {
		len = spdk_max(len, strlen(g_col_desc[tab][i]));
	for (i = 0; g_col_desc[tab][i].name != NULL; ++i) {
		len = spdk_max(len, g_col_desc[tab][i].name_len);
	}

	elements = i;
@@ -776,7 +985,7 @@ change_sorting(uint8_t tab)
	my_items = (ITEM **)calloc(elements + 1, sizeof(ITEM *));

	for (i = 0; i < elements; ++i) {
		my_items[i] = new_item(g_col_desc[tab][i], NULL);
		my_items[i] = new_item(g_col_desc[tab][i].name, NULL);
		set_item_userptr(my_items[i], sort_type);
	}

@@ -881,6 +1090,9 @@ show_stats(void)
		case 's':
			change_sorting(active_tab);
			break;
		case 'c':
			filter_columns(active_tab);
			break;
		default:
			break;
		}
@@ -979,6 +1191,7 @@ int main(int argc, char **argv)
	}

	initscr();
	init_str_len();
	setup_ncurses();
	draw_interface();
	show_stats();