summaryrefslogtreecommitdiffstats
path: root/VNFs/DPPD-PROX/display.c
diff options
context:
space:
mode:
authorDeepak S <deepak.s@linux.intel.com>2017-07-13 21:26:50 -0700
committerDeepak S <deepak.s@linux.intel.com>2017-07-14 04:58:47 -0700
commit7286b2518ec8e4398b512ce95def9166a7af2e4a (patch)
treec93ef65d9e73e8893ccecb720152e16aae96a8b6 /VNFs/DPPD-PROX/display.c
parentadcb79da90176b27224eeb1d00aa0e611ef85a9b (diff)
Adding PROX(Packet pROcessing eXecution engine) VNF to sampleVNF
JIRA: SAMPLEVNF-55 PROX is a DPDK-based application implementing Telco use-cases such as a simplified BRAS/BNG, light-weight AFTR... It also allows configuring finer grained network functions like QoS, Routing, load-balancing... (We are moving PROX version v039 to sampleVNF https://01.org/intel-data-plane-performance-demonstrators/prox-overview) Change-Id: Ia3cb02cf0e49ac5596e922c197ff7e010293d033 Signed-off-by: Deepak S <deepak.s@linux.intel.com>
Diffstat (limited to 'VNFs/DPPD-PROX/display.c')
-rw-r--r--VNFs/DPPD-PROX/display.c994
1 files changed, 994 insertions, 0 deletions
diff --git a/VNFs/DPPD-PROX/display.c b/VNFs/DPPD-PROX/display.c
new file mode 100644
index 00000000..2a351a09
--- /dev/null
+++ b/VNFs/DPPD-PROX/display.c
@@ -0,0 +1,994 @@
+/*
+// Copyright (c) 2010-2017 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#include <curses.h>
+
+#include <rte_cycles.h>
+#include <string.h>
+#include <signal.h>
+#include <math.h>
+#include <signal.h>
+
+#include "display_latency.h"
+#include "display_mempools.h"
+#include "display_ports.h"
+#include "display_priority.h"
+#include "display_rings.h"
+#include "display_pkt_len.h"
+#include "display_l4gen.h"
+#include "display_tasks.h"
+
+#include "cqm.h"
+#include "msr.h"
+#include "display.h"
+#include "log.h"
+#include "commands.h"
+#include "main.h"
+#include "stats.h"
+#include "stats_port.h"
+#include "stats_latency.h"
+#include "stats_global.h"
+#include "stats_core.h"
+#include "prox_cfg.h"
+#include "prox_assert.h"
+#include "version.h"
+#include "quit.h"
+#include "prox_port_cfg.h"
+
+static struct screen_state screen_state = {
+ .pps_unit = 1000,
+ .chosen_screen = -1,
+};
+
+static struct display_screen *display_screens[16];
+static struct display_screen *current_screen;
+static size_t n_screens;
+static size_t longest_title;
+
+void display_set_pps_unit(int val)
+{
+ screen_state.pps_unit = val;
+}
+
+/* Set up the display mutex as recursive. This enables threads to use
+ display_[un]lock() to lock the display when multiple calls to for
+ instance plog_info() need to be made. */
+static pthread_mutex_t disp_mtx = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+
+static void stats_display_layout(uint8_t in_place);
+
+void display_lock(void)
+{
+ pthread_mutex_lock(&disp_mtx);
+}
+
+void display_unlock(void)
+{
+ pthread_mutex_unlock(&disp_mtx);
+}
+
+/* Advanced text output */
+static WINDOW *scr = NULL, *win_txt, *win_general, *win_cmd, *win_stat, *win_title, *win_tabs, *win_help;
+static int win_txt_height = 1;
+static int title_len;
+
+static uint16_t max_n_lines;
+
+static int cmd_cursor_pos;
+static const char *cmd_cmd;
+static int cmd_len;
+
+/* Colors used in the interface */
+enum colors {
+ INVALID_COLOR,
+ NO_COLOR,
+ RED_ON_BLACK,
+ BLACK_ON_CYAN,
+ BLACK_ON_GREEN,
+ BLACK_ON_WHITE,
+ BLACK_ON_YELLOW,
+ YELLOW_ON_BLACK,
+ WHITE_ON_RED,
+ YELLOW_ON_NOTHING,
+ GREEN_ON_NOTHING,
+ RED_ON_NOTHING,
+ BLUE_ON_NOTHING,
+ CYAN_ON_NOTHING,
+ MAGENTA_ON_NOTHING,
+ WHITE_ON_NOTHING,
+};
+
+int display_getch(void)
+{
+ int ret;
+
+ display_lock();
+ ret = wgetch(scr);
+ display_unlock();
+
+ return ret;
+}
+
+void display_cmd(const char *cmd, int cl, int cursor_pos)
+{
+ cmd_len = cl;
+ if (cursor_pos == -1 || cursor_pos > cmd_len)
+ cursor_pos = cmd_len;
+ cmd_cursor_pos = cursor_pos;
+ cmd_cmd = cmd;
+
+ display_lock();
+ werase(win_cmd);
+ if (cursor_pos < cmd_len) {
+ waddnstr(win_cmd, cmd, cursor_pos);
+ wbkgdset(win_cmd, COLOR_PAIR(YELLOW_ON_BLACK));
+ waddnstr(win_cmd, cmd + cursor_pos, 1);
+ wbkgdset(win_cmd, COLOR_PAIR(BLACK_ON_YELLOW));
+ waddnstr(win_cmd, cmd + cursor_pos + 1, cmd_len - (cursor_pos + 1));
+ }
+ else {
+ waddnstr(win_cmd, cmd, cmd_len);
+ wmove(win_cmd, cursor_pos, 0);
+ wbkgdset(win_cmd, COLOR_PAIR(YELLOW_ON_BLACK));
+ waddstr(win_cmd, " ");
+ wbkgdset(win_cmd, COLOR_PAIR(BLACK_ON_YELLOW));
+ }
+
+ wattroff(win_stat, A_UNDERLINE);
+ wrefresh(win_cmd);
+ display_unlock();
+}
+
+static void refresh_cmd_win(void)
+{
+ display_cmd(cmd_cmd, cmd_len, cmd_cursor_pos);
+}
+
+static WINDOW *create_subwindow(int height, int width, int y_pos, int x_pos)
+{
+ WINDOW *win = subwin(scr, height, width, y_pos, x_pos);
+ touchwin(scr);
+ return win;
+}
+
+/* The limit parameter sets the last column that something can be
+ printed. If characters would be printed _past_ the limit, the last
+ character printed within the limit will be a '~' to signify that
+ the string cut off. The limit parameter will be ignored if its
+ value is -1 */
+static inline int mvwaddstrv(WINDOW *win, int y, int x, int limit, const char *fmt, va_list ap)
+{
+ char buf[1024];
+ int ret;
+
+ ret = vsnprintf(buf, sizeof(buf), fmt, ap);
+ int len = ret;
+
+ wmove(win, y, x);
+ if (x > COLS - 1) {
+ return 0;
+ }
+
+ /* To prevent strings from wrapping, cut the string at the end
+ of the screen. */
+ if (x + len > COLS) {
+ buf[COLS - 1 - x] = 0;
+ len = COLS - x;
+ }
+
+ if (limit != -1 && x + len > limit) {
+ int new_len = limit - x;
+
+ if (new_len < 0)
+ return 0;
+ buf[new_len] = '~';
+ buf[new_len + 1] = 0;
+ }
+
+ waddstr(win, buf);
+ return ret;
+}
+
+/* Format string capable [mv]waddstr() wrappers */
+__attribute__((format(printf, 4, 5))) static inline int mvwaddstrf(WINDOW* win, int y, int x, const char *fmt, ...)
+{
+ int ret;
+ va_list ap;
+
+ va_start(ap, fmt);
+ ret = mvwaddstrv(win, y, x, -1, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+__attribute__((format(printf, 5, 6))) static inline int mvwaddstrf_limit(WINDOW* win, int y, int x, int limit, const char *fmt, ...)
+{
+ int ret;
+ va_list ap;
+
+ va_start(ap, fmt);
+ ret = mvwaddstrv(win, y, x, limit, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+// red: link down; Green: link up
+static short link_color(const uint8_t if_port)
+{
+ return COLOR_PAIR(prox_port_cfg[if_port].link_up? GREEN_ON_NOTHING : RED_ON_NOTHING);
+}
+
+static void (*ncurses_sigwinch)(int);
+
+static void sigwinch(int in)
+{
+ if (ncurses_sigwinch)
+ ncurses_sigwinch(in);
+ refresh();
+ stats_display_layout(0);
+}
+
+static void set_signal_handler(void)
+{
+ struct sigaction old;
+
+ sigaction(SIGWINCH, NULL, &old);
+ ncurses_sigwinch = old.sa_handler;
+
+ signal(SIGWINCH, sigwinch);
+}
+
+void display_column_port_ring(const struct display_column *display_column, int row, struct port_queue *ports, int port_count, struct rte_ring **rings, int ring_count)
+{
+ if (row >= max_n_lines)
+ return;
+
+ int pos = display_column->offset;
+ int limit = pos + display_column->width;
+
+ for (int i = 0; i < port_count && pos < limit; i++) {
+ wbkgdset(win_stat, link_color(ports[i].port));
+ pos += mvwaddstrf_limit(win_stat, row + 2, pos, limit, "%u", ports[i].port);
+ wbkgdset(win_stat, COLOR_PAIR(NO_COLOR));
+
+ if (i != port_count - 1)
+ pos += mvwaddstrf_limit(win_stat, row + 2, pos, limit, " ");
+ }
+
+ for (uint8_t ring_id = 0; ring_id < ring_count && pos < limit; ++ring_id) {
+ pos += mvwaddstrf_limit(win_stat, row + 2, pos, limit, "%s", rings[ring_id]->name);
+ }
+}
+
+static void display_add_screen(struct display_screen *screen)
+{
+ display_screens[n_screens++] = screen;
+ if (longest_title < strlen(screen->title))
+ longest_title = strlen(screen->title);
+}
+
+static void display_init_screens(void)
+{
+ if (n_screens)
+ return;
+
+ display_add_screen(display_tasks());
+ display_add_screen(display_ports());
+ display_add_screen(display_mempools());
+ display_add_screen(display_latency());
+ display_add_screen(display_rings());
+ display_add_screen(display_l4gen());
+ display_add_screen(display_pkt_len());
+ display_add_screen(display_priority());
+}
+
+void display_init(void)
+{
+ scr = initscr();
+ start_color();
+ /* Assign default foreground/background colors to color number -1 */
+ use_default_colors();
+
+ init_pair(NO_COLOR, -1, -1);
+ init_pair(RED_ON_BLACK, COLOR_RED, COLOR_BLACK);
+ init_pair(BLACK_ON_CYAN, COLOR_BLACK, COLOR_CYAN);
+ init_pair(BLACK_ON_GREEN, COLOR_BLACK, COLOR_GREEN);
+ init_pair(BLACK_ON_WHITE, COLOR_BLACK, COLOR_WHITE);
+ init_pair(BLACK_ON_YELLOW, COLOR_BLACK, COLOR_YELLOW);
+ init_pair(YELLOW_ON_BLACK, COLOR_YELLOW, COLOR_BLACK);
+ init_pair(WHITE_ON_RED, COLOR_WHITE, COLOR_RED);
+ init_pair(YELLOW_ON_NOTHING, COLOR_YELLOW, -1);
+ init_pair(GREEN_ON_NOTHING, COLOR_GREEN, -1);
+ init_pair(RED_ON_NOTHING, COLOR_RED, -1);
+ init_pair(BLUE_ON_NOTHING, COLOR_BLUE, -1);
+ init_pair(CYAN_ON_NOTHING, COLOR_CYAN, -1);
+ init_pair(MAGENTA_ON_NOTHING, COLOR_MAGENTA, -1);
+ init_pair(WHITE_ON_NOTHING, COLOR_WHITE, -1);
+ /* nodelay(scr, TRUE); */
+ noecho();
+ curs_set(0);
+ /* Create fullscreen log window. When stats are displayed
+ later, it is recreated with appropriate dimensions. */
+ win_txt = create_subwindow(0, 0, 0, 0);
+ wbkgd(win_txt, COLOR_PAIR(0));
+
+ idlok(win_txt, FALSE);
+ /* Get scrolling */
+ scrollok(win_txt, TRUE);
+ /* Leave cursor where it was */
+ leaveok(win_txt, TRUE);
+
+ refresh();
+
+ set_signal_handler();
+
+ max_n_lines = (LINES - 5 - 2 - 3);
+ /* core_port_height = max_n_lines < stats_get_n_tasks_tot()? max_n_lines : stats_get_n_tasks_tot(); */
+
+ display_init_screens();
+ display_screen(0);
+ stats_display_layout(0);
+}
+
+static void display_page_recalc_offsets(struct display_page *display_page)
+{
+ struct display_table *table;
+ struct display_column *col;
+ int total_offset = 0;
+
+ for (int i = 0; i < display_page->n_tables; ++i) {
+ table = &display_page->tables[i];
+
+ if (i != 0)
+ total_offset += 1;
+ table->offset = total_offset;
+ for (int j = 0; j < table->n_cols; ++j) {
+ col = &table->cols[j];
+ col->offset = total_offset;
+ if (j + 1 != table->n_cols)
+ total_offset += 1;
+ total_offset += col->width;
+ }
+ table->width = total_offset - table->offset;
+ }
+}
+
+void display_page_init(struct display_page *display_page)
+{
+ struct display_table *table;
+ struct display_column *col;
+ int table_width = 0;
+ int table_offset = 0;
+
+ memset(display_page, 0, sizeof(*display_page));
+ display_page->n_tables = 0;
+ for (size_t i = 0; i < sizeof(display_page->tables)/sizeof(display_page->tables[0]); ++i) {
+ table = &display_page->tables[i];
+ for (size_t j = 0; j < sizeof(table->cols)/sizeof(table->cols[0]); ++j) {
+ col = &table->cols[j];
+ col->display_page = display_page;
+ }
+ }
+}
+
+struct display_table *display_page_add_table(struct display_page *display_page)
+{
+ struct display_table *table = &display_page->tables[display_page->n_tables];
+
+ display_page->n_tables++;
+ return table;
+}
+
+void display_table_init(struct display_table *table, const char *title)
+{
+ strcpy(table->title, title);
+ table->n_cols = 0;
+}
+
+struct display_column *display_table_add_col(struct display_table *table)
+{
+ struct display_column *col = &table->cols[table->n_cols];
+
+ table->n_cols++;
+ return col;
+}
+
+void display_column_init(struct display_column *display_column, const char *title, unsigned width)
+{
+ if (width < strlen(title))
+ width = strlen(title);
+
+ strcpy(display_column->title, title);
+ display_column->width = width;
+ display_page_recalc_offsets(display_column->display_page);
+}
+
+int display_column_get_width(const struct display_column *display_column)
+{
+ return display_column->width;
+}
+
+void display_page_draw_frame(const struct display_page *display_page, int height)
+{
+ const struct display_table *table;
+ const struct display_column *col;
+
+ wattron(win_stat, A_BOLD);
+ wbkgdset(win_stat, COLOR_PAIR(YELLOW_ON_NOTHING));
+
+ for (int i = 0; i < display_page->n_tables; ++i) {
+ table = &display_page->tables[i];
+
+ if (i != 0)
+ mvwvline(win_stat, 0, table->offset - 1, ACS_VLINE, height + 2);
+
+ mvwaddstrf(win_stat, 0, table->offset + table->width / 2 - strlen(table->title) / 2, "%s", table->title);
+ for (int j = 0; j < table->n_cols; ++j) {
+ col = &table->cols[j];
+
+ if (j != 0)
+ mvwvline(win_stat, 1, col->offset - 1, ACS_VLINE, height + 1);
+ mvwaddstrf(win_stat, 1, col->offset + col->width / 2 - strlen(col->title) / 2, "%s", col->title);
+ }
+
+ if (i + 1 == display_page->n_tables)
+ mvwvline(win_stat, 0, table->offset + table->width, ACS_VLINE, height + 2);
+ }
+ wbkgdset(win_stat, COLOR_PAIR(NO_COLOR));
+ wattroff(win_stat, A_BOLD);
+}
+
+void display_column_print(const struct display_column *display_column, int row, const char *fmt, ...)
+{
+ if (row >= max_n_lines)
+ return;
+
+ va_list ap;
+ char buffer[128] = {0};
+ char *to_print = buffer + 64;
+
+ va_start(ap, fmt);
+ int len = vsnprintf(to_print, sizeof(buffer) - 64, fmt, ap);
+ va_end(ap);
+
+ int offset = 0;
+ /* If column is too long, add ~ at the end. If it is too
+ short, align on the right. */
+ if (len > display_column->width) {
+ to_print[display_column->width - 1] = '~';
+ to_print[display_column->width] = '\0';
+ } else {
+ int diff = display_column->width - len;
+
+ to_print += len;
+ to_print -= display_column->width;
+ for (int i = 0; i < diff; i++)
+ to_print[i] = ' ';
+ }
+
+ mvwaddstrf(win_stat, row + 2, display_column->offset, "%s", to_print);
+}
+
+void display_column_print_core_task(const struct display_column *display_column, int row, struct lcore_cfg *lconf, struct task_args *targ)
+{
+ if (row >= max_n_lines)
+ return;
+
+ if (lconf->n_tasks_run == 0) {
+ wattron(win_stat, A_BOLD);
+ wbkgdset(win_stat, COLOR_PAIR(RED_ON_NOTHING));
+ }
+ if (targ->id == 0)
+ mvwaddstrf(win_stat, row + 2, display_column->offset, "%2u/", lconf->id);
+ if (lconf->n_tasks_run == 0) {
+ wattroff(win_stat, A_BOLD);
+ wbkgdset(win_stat, COLOR_PAIR(NO_COLOR));
+ }
+ if (!lconf_task_is_running(lconf, targ->id)) {
+ wattron(win_stat, A_BOLD);
+ wbkgdset(win_stat, COLOR_PAIR(RED_ON_NOTHING));
+ }
+ mvwaddstrf(win_stat, row + 2, display_column->offset + 3, "%1u", targ->id);
+ if (!lconf_task_is_running(lconf, targ->id)) {
+ wattroff(win_stat, A_BOLD);
+ wbkgdset(win_stat, COLOR_PAIR(NO_COLOR));
+ }
+}
+
+static void redraw_tabs(unsigned screen_id)
+{
+ const size_t len = longest_title + 1;
+
+ for (size_t i = 0; i < n_screens; ++i) {
+ if (i == screen_id)
+ wbkgdset(win_tabs, COLOR_PAIR(BLACK_ON_GREEN));
+
+ mvwaddstrf(win_tabs, 0, i*(len + 3), "%zu ", i+1);
+ if (i != screen_id)
+ wbkgdset(win_tabs, COLOR_PAIR(GREEN_ON_NOTHING));
+ mvwaddstrf(win_tabs, 0, i*(len + 3) + 2, "%s", display_screens[i]->title);
+ for (size_t j = strlen(display_screens[i]->title); j < len - 1; ++j)
+ mvwaddstrf(win_tabs, 0, i*(len + 3) + 2 + j, " ");
+ if (i != screen_id)
+ wbkgdset(win_tabs, COLOR_PAIR(NO_COLOR));
+ if (i == screen_id)
+ wbkgdset(win_tabs, COLOR_PAIR(NO_COLOR));
+ }
+
+ wrefresh(win_tabs);
+}
+
+static void draw_title(void)
+{
+ char title_str[128];
+
+ snprintf(title_str, sizeof(title_str), "%s %s: %s", PROGRAM_NAME, VERSION_STR, prox_cfg.name);
+
+ wbkgd(win_title, COLOR_PAIR(BLACK_ON_GREEN));
+ title_len = strlen(title_str);
+ mvwaddstrf(win_title, 0, (COLS - title_len)/2, "%s", title_str);
+
+ redraw_tabs(screen_state.chosen_screen);
+}
+
+static void draw_general_frame(void)
+{
+ if (screen_state.toggle == 0) {
+ wattron(win_general, A_BOLD);
+ wbkgdset(win_general, COLOR_PAIR(MAGENTA_ON_NOTHING));
+ mvwaddstrf(win_general, 0, 9, "rx: tx: diff: rx: tx: %%:");
+ mvwaddstrf(win_general, 1, 9, "rx: tx: err: rx: tx: err: %%:");
+ wbkgdset(win_general, COLOR_PAIR(NO_COLOR));
+
+ wbkgdset(win_general, COLOR_PAIR(BLUE_ON_NOTHING));
+ mvwaddstrf(win_general, 0, 0, "Host pps ");
+ mvwaddstrf(win_general, 1, 0, "NICs pps ");
+
+ wbkgdset(win_general, COLOR_PAIR(CYAN_ON_NOTHING));
+ mvwaddstrf(win_general, 0, 56, "avg");
+ mvwaddstrf(win_general, 1, 56, "avg");
+ wbkgdset(win_general, COLOR_PAIR(NO_COLOR));
+ wattroff(win_general, A_BOLD);
+ } else {
+ wattron(win_general, A_BOLD);
+ wbkgdset(win_general, COLOR_PAIR(BLUE_ON_NOTHING));
+ mvwaddstrf(win_general, 0, 9, "rx: tx: rx-tx: tx/rx: rx/tx:");
+ mvwaddstrf(win_general, 1, 9, "rx: tx: err: tx/rx: rx/tx:");
+ wbkgdset(win_general, COLOR_PAIR(NO_COLOR));
+
+ wbkgdset(win_general, COLOR_PAIR(CYAN_ON_NOTHING));
+ mvwaddstrf(win_general, 0, 0, "Host tot ");
+ mvwaddstrf(win_general, 1, 0, "NICs tot ");
+ wattroff(win_general, A_BOLD);
+ }
+}
+
+static void draw_status_bar(void)
+{
+ wbkgd(win_help, COLOR_PAIR(BLACK_ON_WHITE));
+ werase(win_help);
+ mvwaddstrf(win_help, 0, 0,
+ "Enter 'help' or command, <ESC> or 'quit' to exit, "
+ "1-%zu to switch screens and 0 to reset stats, '=' to toggle between per-sec and total stats",
+ n_screens);
+ wrefresh(win_help);
+ mvwin(win_help, LINES - 1, 0);
+}
+
+static void draw_log_window(void)
+{
+ idlok(win_txt, FALSE);
+ /* Get scrolling */
+ scrollok(win_txt, TRUE);
+
+ /* Leave cursor where it was */
+ leaveok(win_txt, TRUE);
+ wbkgd(win_txt, COLOR_PAIR(BLACK_ON_CYAN));
+ wrefresh(win_txt);
+}
+
+static void stats_display_layout(uint8_t in_place)
+{
+ uint8_t cur_stats_height;
+
+ cur_stats_height = current_screen->get_height();
+ cur_stats_height = cur_stats_height > max_n_lines? max_n_lines: cur_stats_height;
+
+ display_lock();
+ if (!in_place) {
+ // moving existing windows does not work
+ delwin(win_txt);
+ delwin(win_general);
+ delwin(win_title);
+ delwin(win_tabs);
+ delwin(win_cmd);
+ delwin(win_txt);
+ delwin(win_help);
+
+ clear();
+ }
+
+ if (!in_place) {
+ win_stat = create_subwindow(cur_stats_height + 2, 0, 4, 0);
+ win_tabs = create_subwindow(1, 0, 1, 0);
+ win_general = create_subwindow(2, 0, 2, 0);
+ win_title = create_subwindow(1, 0, 0, 0);
+ win_cmd = create_subwindow(1, 0, cur_stats_height + 2 + 4, 0);
+ win_txt_height = LINES - cur_stats_height - 2 - 3 - 3;
+ win_txt = create_subwindow(win_txt_height, 0, cur_stats_height + 4 + 3, 0);
+ win_help = create_subwindow(1, 0, LINES - 1, 0);
+ }
+
+ draw_title();
+ draw_general_frame();
+ /* Command line */
+ wbkgd(win_cmd, COLOR_PAIR(BLACK_ON_YELLOW));
+ idlok(win_cmd, FALSE);
+ /* Move cursor at insertion point */
+ leaveok(win_cmd, FALSE);
+
+ draw_status_bar();
+ draw_log_window();
+
+ /* Draw everything to the screen */
+ refresh();
+ current_screen->draw_frame(&screen_state);
+ display_unlock();
+
+ refresh_cmd_win();
+ display_stats();
+}
+
+void display_end(void)
+{
+ pthread_mutex_destroy(&disp_mtx);
+
+ if (scr != NULL) {
+ endwin();
+ }
+}
+
+static void pps_print(WINDOW *dst_scr, int y, int x, uint64_t val, int is_blue)
+{
+ uint64_t rx_pps_disp = val;
+ uint64_t rx_pps_disp_frac = 0;
+ uint32_t ten_pow3 = 0;
+ static const char *units = " KMG";
+ char rx_unit = ' ';
+
+ while (rx_pps_disp > 1000) {
+ rx_pps_disp /= 1000;
+ rx_pps_disp_frac = (val - rx_pps_disp*1000) / 10;
+ val /= 1000;
+ ten_pow3++;
+ }
+
+ if (ten_pow3 >= strlen(units)) {
+ wbkgdset(dst_scr, COLOR_PAIR(RED_ON_NOTHING));
+ mvwaddstrf(dst_scr, y, x, "---");
+ wbkgdset(dst_scr, COLOR_PAIR(NO_COLOR));
+ return;
+ }
+
+ rx_unit = units[ten_pow3];
+
+ wattron(dst_scr, A_BOLD);
+ if (is_blue) {
+ wbkgdset(dst_scr, COLOR_PAIR(BLUE_ON_NOTHING));
+ }
+ else
+ wbkgdset(dst_scr, COLOR_PAIR(CYAN_ON_NOTHING));
+
+ mvwaddstrf(dst_scr, y, x, "%3lu", rx_pps_disp);
+ if (rx_unit != ' ') {
+ mvwaddstrf(dst_scr, y, x + 3, ".%02lu", rx_pps_disp_frac);
+ wattroff(dst_scr, A_BOLD);
+ wbkgdset(dst_scr, COLOR_PAIR(WHITE_ON_NOTHING));
+ wattron(dst_scr, A_BOLD);
+ mvwaddstrf(dst_scr, y, x + 6, "%c", rx_unit);
+ wattroff(dst_scr, A_BOLD);
+ wbkgdset(dst_scr, COLOR_PAIR(NO_COLOR));
+ }
+ else {
+ mvwaddstrf(dst_scr, y, x + 3, " ");
+ }
+ wattroff(dst_scr, A_BOLD);
+ wbkgdset(dst_scr, COLOR_PAIR(NO_COLOR));
+}
+
+static void display_stats_general_per_sec(void)
+{
+ struct global_stats_sample *gsl = stats_get_global_stats(1);
+ struct global_stats_sample *gsp = stats_get_global_stats(0);
+
+ uint64_t rx_pps = val_to_rate(gsl->host_rx_packets - gsp->host_rx_packets, gsl->tsc - gsp->tsc);
+ uint64_t tx_pps = val_to_rate(gsl->host_tx_packets - gsp->host_tx_packets, gsl->tsc - gsp->tsc);
+ /* Host: RX, TX, Diff */
+ pps_print(win_general, 0, 12, rx_pps, 1);
+ pps_print(win_general, 0, 25, tx_pps, 1);
+
+ uint64_t diff = 0;
+ if (rx_pps > tx_pps)
+ diff = rx_pps - tx_pps;
+ pps_print(win_general, 0, 40, diff, 1);
+
+ uint64_t nics_rx_pps = val_to_rate(gsl->nics_rx_packets - gsp->nics_rx_packets, gsl->tsc - gsp->tsc);
+ uint64_t nics_tx_pps = val_to_rate(gsl->nics_tx_packets - gsp->nics_tx_packets, gsl->tsc - gsp->tsc);
+ uint64_t nics_ierrors = val_to_rate(gsl->nics_ierrors - gsp->nics_ierrors, gsl->tsc - gsp->tsc);
+ uint64_t nics_imissed = val_to_rate(gsl->nics_imissed - gsp->nics_imissed, gsl->tsc - gsp->tsc);
+
+ /* NIC: RX, TX, Diff */
+ pps_print(win_general, 1, 12, nics_rx_pps, 1);
+ pps_print(win_general, 1, 25, nics_tx_pps, 1);
+ pps_print(win_general, 1, 40, nics_ierrors + nics_imissed, 1);
+
+ wbkgdset(win_general, COLOR_PAIR(CYAN_ON_NOTHING));
+ wattron(win_general, A_BOLD);
+ mvwaddstrf(win_general, 0, 103, "%6.2f", tx_pps > rx_pps? 100 : tx_pps * 100.0 / rx_pps);
+ wattroff(win_general, A_BOLD);
+ wbkgdset(win_general, COLOR_PAIR(NO_COLOR));
+
+ struct global_stats_sample *gsb = stats_get_global_stats_beg();
+ if (gsb) {
+ uint64_t rx_pps = val_to_rate(gsl->host_rx_packets - gsb->host_rx_packets, gsl->tsc - gsb->tsc);
+ uint64_t tx_pps = val_to_rate(gsl->host_tx_packets - gsb->host_tx_packets, gsl->tsc - gsb->tsc);
+
+ uint64_t nics_rx_pps = val_to_rate(gsl->nics_rx_packets - gsb->nics_rx_packets, gsl->tsc - gsb->tsc);
+ uint64_t nics_tx_pps = val_to_rate(gsl->nics_tx_packets - gsb->nics_tx_packets, gsl->tsc - gsb->tsc);
+ uint64_t nics_ierrors = val_to_rate(gsl->nics_ierrors - gsb->nics_ierrors, gsl->tsc - gsb->tsc);
+ uint64_t nics_imissed = val_to_rate(gsl->nics_imissed - gsb->nics_imissed, gsl->tsc - gsb->tsc);
+
+ pps_print(win_general, 0, 64, rx_pps, 0);
+ pps_print(win_general, 0, 77, tx_pps, 0);
+
+ pps_print(win_general, 1, 64, nics_rx_pps, 0);
+ pps_print(win_general, 1, 77, nics_tx_pps, 0);
+ pps_print(win_general, 1, 91, nics_ierrors + nics_imissed, 0);
+
+ wbkgdset(win_general, COLOR_PAIR(CYAN_ON_NOTHING));
+ wattron(win_general, A_BOLD);
+ uint64_t nics_in = gsl->host_rx_packets - gsb->host_rx_packets + gsl->nics_ierrors - gsb->nics_ierrors + gsl->nics_imissed - gsb->nics_imissed;
+ uint64_t nics_out = gsl->host_tx_packets - gsb->host_tx_packets;
+ mvwaddstrf(win_general, 1, 103, "%6.2f", nics_out > nics_in?
+ 100 : nics_out * 100.0 / nics_in);
+ wattron(win_general, A_BOLD);
+ wbkgdset(win_general, COLOR_PAIR(NO_COLOR));
+ }
+}
+
+static void display_stats_general_total(void)
+{
+ struct global_stats_sample *gsl = stats_get_global_stats(1);
+
+ int64_t diff = (int64_t)gsl->host_rx_packets - gsl->host_tx_packets;
+ uint32_t percent;
+
+ /* Host: RX, TX, Diff */
+ mvwaddstrf(win_general, 0, 13, "%16lu", gsl->host_rx_packets);
+ mvwaddstrf(win_general, 0, 35, "%16lu", gsl->host_tx_packets);
+ mvwaddstrf(win_general, 0, 60, "%16"PRId64"", diff);
+ if (gsl->host_rx_packets == 0)
+ percent = 1000000;
+ else
+ percent = gsl->host_tx_packets * 1000000 / gsl->host_rx_packets;
+ mvwaddstrf(win_general, 0, 88, "%3u.%04u%%", percent / 10000, percent % 10000);
+ if (gsl->host_tx_packets == 0)
+ percent = 1000000;
+ else
+ percent = gsl->host_rx_packets * 1000000 / gsl->host_tx_packets;
+ mvwaddstrf(win_general, 0, 106, "%3u.%04u%%", percent / 10000, percent % 10000);
+
+ mvwaddstrf(win_general, 1, 13, "%16lu", gsl->nics_rx_packets);
+ mvwaddstrf(win_general, 1, 35, "%16lu", gsl->nics_tx_packets);
+ mvwaddstrf(win_general, 1, 60, "%16lu", gsl->nics_ierrors + gsl->nics_imissed);
+ if (gsl->nics_rx_packets == 0)
+ percent = 1000000;
+ else
+ percent = gsl->nics_tx_packets * 1000000 / gsl->nics_rx_packets;
+ mvwaddstrf(win_general, 1, 88, "%3u.%04u%%", percent / 10000, percent % 10000);
+ if (gsl->nics_tx_packets == 0)
+ percent = 1000000;
+ else
+ percent = gsl->nics_rx_packets * 1000000 / gsl->nics_tx_packets;
+ mvwaddstrf(win_general, 1, 106, "%3u.%04u%%", percent / 10000, percent % 10000);
+}
+
+static void display_stats_general(void)
+{
+ /* moment when stats were gathered. */
+ uint64_t cur_tsc = stats_get_last_tsc();
+ uint64_t up_time = tsc_to_sec(cur_tsc - stats_global_start_tsc());
+ uint64_t up_time2 = tsc_to_sec(cur_tsc - stats_global_beg_tsc());
+ uint64_t rem_time = -1;
+ char title_str[128] = {0};
+
+ if (stats_global_end_tsc()) {
+ uint64_t rem_tsc = stats_global_end_tsc() > cur_tsc? stats_global_end_tsc() - cur_tsc : 0;
+
+ rem_time = tsc_to_sec(rem_tsc);
+ }
+
+ if (up_time != up_time2 && cur_tsc >= stats_global_beg_tsc()) {
+ if (stats_global_end_tsc())
+ snprintf(title_str, sizeof(title_str), "%5lu (%lu) up, %lu rem", up_time, up_time2, rem_time);
+ else
+ snprintf(title_str, sizeof(title_str), "%5lu (%lu) up", up_time, up_time2);
+ }
+ else {
+ if (stats_global_end_tsc())
+ snprintf(title_str, sizeof(title_str), "%5lu up, %lu rem", up_time, rem_time);
+ else
+ snprintf(title_str, sizeof(title_str), "%5lu up", up_time);
+ }
+
+ /* Only print up time information if there is enough space */
+ if ((int)((COLS + title_len)/2 + strlen(title_str) + 1) < COLS) {
+ mvwaddstrf(win_title, 0, COLS - strlen(title_str), "%s", title_str);
+ wrefresh(win_title);
+ }
+
+ if (screen_state.toggle == 0)
+ display_stats_general_per_sec();
+ else
+ display_stats_general_total();
+
+ wrefresh(win_general);
+}
+
+char *print_time_unit_err_usec(char *dst, struct time_unit_err *t)
+{
+ uint64_t nsec_total = time_unit_to_nsec(&t->time);
+
+ uint64_t usec = nsec_total/1000;
+ uint64_t nsec = nsec_total - usec*1000;
+
+ uint64_t nsec_total_error = time_unit_to_nsec(&t->error);
+
+ uint64_t usec_error = nsec_total_error/1000;
+ uint64_t nsec_error = nsec_total_error - usec_error*1000;
+
+ sprintf(dst, "%4"PRIu64".%03"PRIu64" +/- %2"PRIu64".%03"PRIu64"", usec, nsec, usec_error, nsec_error);
+ return dst;
+}
+
+char *print_time_unit_usec(char *dst, struct time_unit *t)
+{
+ uint64_t nsec_total = time_unit_to_nsec(t);
+
+ uint64_t usec = nsec_total/1000;
+ uint64_t nsec = nsec_total - usec*1000;
+
+ sprintf(dst, "%4"PRIu64".%03"PRIu64"", usec, nsec);
+ return dst;
+}
+
+void toggle_display_screen(void)
+{
+ screen_state.toggle = !screen_state.toggle;
+ stats_display_layout(0);
+}
+
+void display_screen(unsigned screen_id)
+{
+ if (screen_id >= n_screens) {
+ plog_err("Unsupported screen %d\n", screen_id + 1);
+ return;
+ }
+
+ if (screen_state.chosen_screen == screen_id) {
+ stats_display_layout(1);
+ }
+ else {
+ screen_state.chosen_screen = screen_id;
+ current_screen = display_screens[screen_id];
+ stats_display_layout(0);
+ }
+}
+
+void display_page_up(void)
+{
+}
+
+void display_page_down(void)
+{
+}
+
+void display_refresh(void)
+{
+ stats_display_layout(1);
+}
+
+void display_stats(void)
+{
+ display_lock();
+ current_screen->draw_stats(&screen_state);
+ display_stats_general();
+ wrefresh(win_stat);
+ display_unlock();
+}
+
+static char pages[32768] = {0};
+static int cur_idx = 0;
+static size_t pages_len = 0;
+
+void display_print_page(void)
+{
+ int n_lines = 0;
+ int cur_idx_prev = cur_idx;
+
+ if (cur_idx >= (int)pages_len) {
+ return;
+ }
+
+ display_lock();
+ for (size_t i = cur_idx; i < pages_len; ++i) {
+ if (pages[i] == '\n') {
+ n_lines++;
+ if (n_lines == win_txt_height - 2) {
+ pages[i] = 0;
+ cur_idx = i + 1;
+ break;
+ }
+ }
+ }
+
+ waddstr(win_txt, pages + cur_idx_prev);
+ if (cur_idx != cur_idx_prev && cur_idx < (int)pages_len)
+ waddstr(win_txt, "\nPRESS ENTER FOR MORE...\n");
+ else {
+ pages_len = 0;
+ }
+ wrefresh(win_txt);
+ display_unlock();
+}
+
+void display_print(const char *str)
+{
+ display_lock();
+
+ if (scr == NULL) {
+ fputs(str, stdout);
+ fflush(stdout);
+ display_unlock();
+ return;
+ }
+
+ /* Check if the whole string can fit on the screen. */
+ pages_len = strlen(str);
+ int n_lines = 0;
+ memset(pages, 0, sizeof(pages));
+ memcpy(pages, str, pages_len);
+ cur_idx = 0;
+ for (size_t i = 0; i < pages_len; ++i) {
+ if (pages[i] == '\n') {
+ n_lines++;
+ if (n_lines == win_txt_height - 2) {
+ pages[i] = 0;
+ cur_idx = i + 1;
+ break;
+ }
+ }
+ }
+
+ waddstr(win_txt, pages);
+ if (cur_idx != 0)
+ waddstr(win_txt, "\nPRESS ENTER FOR MORE...\n");
+ else
+ pages_len = 0;
+
+ wrefresh(win_txt);
+ display_unlock();
+}