/* // Copyright (c) 2010-2019 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_irq.h" #include "display_latency_distr.h" #include "display_rings.h" #include "display_pkt_len.h" #include "display_l4gen.h" #include "display_tasks.h" #include "stats_irq.h" #include "stats_prio_task.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()); #ifdef LATENCY_HISTOGRAM display_add_screen(display_latency_distr()); #endif display_add_screen(display_rings()); display_add_screen(display_l4gen()); display_add_screen(display_pkt_len()); if (stats_get_n_prio_tasks_tot()) display_add_screen(display_priority()); if (stats_get_n_irq_tasks()) display_add_screen(display_irq()); } 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(); } scr = NULL; } 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_renew(void) { stats_display_layout(0); } 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(); }