From 7286b2518ec8e4398b512ce95def9166a7af2e4a Mon Sep 17 00:00:00 2001 From: Deepak S Date: Thu, 13 Jul 2017 21:26:50 -0700 Subject: 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 --- VNFs/DPPD-PROX/display.c | 994 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 994 insertions(+) create mode 100644 VNFs/DPPD-PROX/display.c (limited to 'VNFs/DPPD-PROX/display.c') 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 + +#include +#include +#include +#include +#include + +#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, 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(); +} -- cgit 1.2.3-korg