Added use case "Forwaring Rate At Maximum Offered Load"
[samplevnf.git] / VNFs / DPPD-PROX / display.c
1 /*
2 // Copyright (c) 2010-2017 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //     http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16
17 #include <curses.h>
18
19 #include <rte_cycles.h>
20 #include <string.h>
21 #include <signal.h>
22 #include <math.h>
23 #include <signal.h>
24
25 #include "display_latency.h"
26 #include "display_mempools.h"
27 #include "display_ports.h"
28 #include "display_priority.h"
29 #include "display_irq.h"
30 #include "display_rings.h"
31 #include "display_pkt_len.h"
32 #include "display_l4gen.h"
33 #include "display_tasks.h"
34 #include "stats_irq.h"
35 #include "stats_prio_task.h"
36
37 #include "cqm.h"
38 #include "msr.h"
39 #include "display.h"
40 #include "log.h"
41 #include "commands.h"
42 #include "main.h"
43 #include "stats.h"
44 #include "stats_port.h"
45 #include "stats_latency.h"
46 #include "stats_global.h"
47 #include "stats_core.h"
48 #include "prox_cfg.h"
49 #include "prox_assert.h"
50 #include "version.h"
51 #include "quit.h"
52 #include "prox_port_cfg.h"
53
54 static struct screen_state screen_state = {
55         .pps_unit = 1000,
56         .chosen_screen = -1,
57 };
58
59 static struct display_screen *display_screens[16];
60 static struct display_screen *current_screen;
61 static size_t n_screens;
62 static size_t longest_title;
63
64 void display_set_pps_unit(int val)
65 {
66         screen_state.pps_unit = val;
67 }
68
69 /* Set up the display mutex  as recursive. This enables threads to use
70    display_[un]lock() to lock  the display when multiple  calls to for
71    instance plog_info() need to be made. */
72 static pthread_mutex_t disp_mtx = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
73
74 static void stats_display_layout(uint8_t in_place);
75
76 void display_lock(void)
77 {
78         pthread_mutex_lock(&disp_mtx);
79 }
80
81 void display_unlock(void)
82 {
83         pthread_mutex_unlock(&disp_mtx);
84 }
85
86 /* Advanced text output */
87 static WINDOW *scr = NULL, *win_txt, *win_general, *win_cmd, *win_stat, *win_title, *win_tabs, *win_help;
88 static int win_txt_height = 1;
89 static int title_len;
90
91 static uint16_t max_n_lines;
92
93 static int cmd_cursor_pos;
94 static const char *cmd_cmd;
95 static int cmd_len;
96
97 /* Colors used in the interface */
98 enum colors {
99         INVALID_COLOR,
100         NO_COLOR,
101         RED_ON_BLACK,
102         BLACK_ON_CYAN,
103         BLACK_ON_GREEN,
104         BLACK_ON_WHITE,
105         BLACK_ON_YELLOW,
106         YELLOW_ON_BLACK,
107         WHITE_ON_RED,
108         YELLOW_ON_NOTHING,
109         GREEN_ON_NOTHING,
110         RED_ON_NOTHING,
111         BLUE_ON_NOTHING,
112         CYAN_ON_NOTHING,
113         MAGENTA_ON_NOTHING,
114         WHITE_ON_NOTHING,
115 };
116
117 int display_getch(void)
118 {
119         int ret;
120
121         display_lock();
122         ret = wgetch(scr);
123         display_unlock();
124
125         return ret;
126 }
127
128 void display_cmd(const char *cmd, int cl, int cursor_pos)
129 {
130         cmd_len = cl;
131         if (cursor_pos == -1 || cursor_pos > cmd_len)
132                 cursor_pos = cmd_len;
133         cmd_cursor_pos = cursor_pos;
134         cmd_cmd = cmd;
135
136         display_lock();
137         werase(win_cmd);
138         if (cursor_pos < cmd_len) {
139                 waddnstr(win_cmd, cmd, cursor_pos);
140                 wbkgdset(win_cmd, COLOR_PAIR(YELLOW_ON_BLACK));
141                 waddnstr(win_cmd, cmd + cursor_pos, 1);
142                 wbkgdset(win_cmd, COLOR_PAIR(BLACK_ON_YELLOW));
143                 waddnstr(win_cmd, cmd + cursor_pos + 1, cmd_len - (cursor_pos + 1));
144         }
145         else {
146                 waddnstr(win_cmd, cmd, cmd_len);
147                 wmove(win_cmd, cursor_pos, 0);
148                 wbkgdset(win_cmd, COLOR_PAIR(YELLOW_ON_BLACK));
149                 waddstr(win_cmd, " ");
150                 wbkgdset(win_cmd, COLOR_PAIR(BLACK_ON_YELLOW));
151         }
152
153         wattroff(win_stat, A_UNDERLINE);
154         wrefresh(win_cmd);
155         display_unlock();
156 }
157
158 static void refresh_cmd_win(void)
159 {
160         display_cmd(cmd_cmd, cmd_len, cmd_cursor_pos);
161 }
162
163 static WINDOW *create_subwindow(int height, int width, int y_pos, int x_pos)
164 {
165         WINDOW *win = subwin(scr, height, width, y_pos, x_pos);
166         touchwin(scr);
167         return win;
168 }
169
170 /* The limit parameter sets the last column that something can be
171    printed. If characters would be printed _past_ the limit, the last
172    character printed within the limit will be a '~' to signify that
173    the string cut off. The limit parameter will be ignored if its
174    value is -1 */
175 static inline int mvwaddstrv(WINDOW *win, int y, int x, int limit, const char *fmt, va_list ap)
176 {
177         char buf[1024];
178         int ret;
179
180         ret = vsnprintf(buf, sizeof(buf), fmt, ap);
181         int len = ret;
182
183         wmove(win, y, x);
184         if (x > COLS - 1) {
185                 return 0;
186         }
187
188         /* To prevent strings from wrapping, cut the string at the end
189            of the screen. */
190         if (x + len > COLS) {
191                 buf[COLS - 1 - x] = 0;
192                 len = COLS - x;
193         }
194
195         if (limit != -1 && x + len > limit) {
196                 int new_len = limit - x;
197
198                 if (new_len < 0)
199                         return 0;
200                 buf[new_len] = '~';
201                 buf[new_len + 1] = 0;
202         }
203
204         waddstr(win, buf);
205         return ret;
206 }
207
208 /* Format string capable [mv]waddstr() wrappers */
209 __attribute__((format(printf, 4, 5))) static inline int mvwaddstrf(WINDOW* win, int y, int x, const char *fmt, ...)
210 {
211         int ret;
212         va_list ap;
213
214         va_start(ap, fmt);
215         ret = mvwaddstrv(win, y, x, -1, fmt, ap);
216         va_end(ap);
217         return ret;
218 }
219
220 __attribute__((format(printf, 5, 6))) static inline int mvwaddstrf_limit(WINDOW* win, int y, int x, int limit, const char *fmt, ...)
221 {
222         int ret;
223         va_list ap;
224
225         va_start(ap, fmt);
226         ret = mvwaddstrv(win, y, x, limit, fmt, ap);
227         va_end(ap);
228         return ret;
229 }
230
231 // red: link down; Green: link up
232 static short link_color(const uint8_t if_port)
233 {
234         return COLOR_PAIR(prox_port_cfg[if_port].link_up? GREEN_ON_NOTHING : RED_ON_NOTHING);
235 }
236
237 static void (*ncurses_sigwinch)(int);
238
239 static void sigwinch(int in)
240 {
241         if (ncurses_sigwinch)
242                 ncurses_sigwinch(in);
243         refresh();
244         stats_display_layout(0);
245 }
246
247 static void set_signal_handler(void)
248 {
249         struct sigaction old;
250
251         sigaction(SIGWINCH, NULL, &old);
252         ncurses_sigwinch = old.sa_handler;
253
254         signal(SIGWINCH, sigwinch);
255 }
256
257 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)
258 {
259         if (row >= max_n_lines)
260                 return;
261
262         int pos = display_column->offset;
263         int limit = pos + display_column->width;
264
265         for (int i = 0; i < port_count && pos < limit; i++) {
266                 wbkgdset(win_stat, link_color(ports[i].port));
267                 pos += mvwaddstrf_limit(win_stat, row + 2, pos, limit, "%u", ports[i].port);
268                 wbkgdset(win_stat, COLOR_PAIR(NO_COLOR));
269
270                 if (i != port_count - 1)
271                         pos += mvwaddstrf_limit(win_stat, row + 2, pos, limit, " ");
272         }
273
274         for (uint8_t ring_id = 0; ring_id < ring_count && pos < limit; ++ring_id) {
275                 pos += mvwaddstrf_limit(win_stat, row + 2, pos, limit, "%s", rings[ring_id]->name);
276         }
277 }
278
279 static void display_add_screen(struct display_screen *screen)
280 {
281         display_screens[n_screens++] = screen;
282         if (longest_title < strlen(screen->title))
283                 longest_title = strlen(screen->title);
284 }
285
286 static void display_init_screens(void)
287 {
288         if (n_screens)
289                 return;
290
291         display_add_screen(display_tasks());
292         display_add_screen(display_ports());
293         display_add_screen(display_mempools());
294         display_add_screen(display_latency());
295         display_add_screen(display_rings());
296         display_add_screen(display_l4gen());
297         display_add_screen(display_pkt_len());
298         if (stats_get_n_prio_tasks_tot())
299                 display_add_screen(display_priority());
300         if (stats_get_n_irq_tasks())
301                 display_add_screen(display_irq());
302 }
303
304 void display_init(void)
305 {
306         scr = initscr();
307         start_color();
308         /* Assign default foreground/background colors to color number -1 */
309         use_default_colors();
310
311         init_pair(NO_COLOR,   -1,  -1);
312         init_pair(RED_ON_BLACK,     COLOR_RED,  COLOR_BLACK);
313         init_pair(BLACK_ON_CYAN,   COLOR_BLACK,  COLOR_CYAN);
314         init_pair(BLACK_ON_GREEN,  COLOR_BLACK,  COLOR_GREEN);
315         init_pair(BLACK_ON_WHITE,  COLOR_BLACK,  COLOR_WHITE);
316         init_pair(BLACK_ON_YELLOW, COLOR_BLACK,  COLOR_YELLOW);
317         init_pair(YELLOW_ON_BLACK, COLOR_YELLOW,  COLOR_BLACK);
318         init_pair(WHITE_ON_RED,    COLOR_WHITE,  COLOR_RED);
319         init_pair(YELLOW_ON_NOTHING,   COLOR_YELLOW,  -1);
320         init_pair(GREEN_ON_NOTHING,   COLOR_GREEN,  -1);
321         init_pair(RED_ON_NOTHING,   COLOR_RED,  -1);
322         init_pair(BLUE_ON_NOTHING,  COLOR_BLUE, -1);
323         init_pair(CYAN_ON_NOTHING,  COLOR_CYAN, -1);
324         init_pair(MAGENTA_ON_NOTHING,  COLOR_MAGENTA, -1);
325         init_pair(WHITE_ON_NOTHING,  COLOR_WHITE, -1);
326         /* nodelay(scr, TRUE); */
327         noecho();
328         curs_set(0);
329         /* Create fullscreen log window. When stats are displayed
330            later, it is recreated with appropriate dimensions. */
331         win_txt = create_subwindow(0, 0, 0, 0);
332         wbkgd(win_txt, COLOR_PAIR(0));
333
334         idlok(win_txt, FALSE);
335         /* Get scrolling */
336         scrollok(win_txt, TRUE);
337         /* Leave cursor where it was */
338         leaveok(win_txt, TRUE);
339
340         refresh();
341
342         set_signal_handler();
343
344         max_n_lines = (LINES - 5 - 2 - 3);
345         /* core_port_height = max_n_lines < stats_get_n_tasks_tot()? max_n_lines : stats_get_n_tasks_tot(); */
346
347         display_init_screens();
348         display_screen(0);
349         stats_display_layout(0);
350 }
351
352 static void display_page_recalc_offsets(struct display_page *display_page)
353 {
354         struct display_table *table;
355         struct display_column *col;
356         int total_offset = 0;
357
358         for (int i = 0; i < display_page->n_tables; ++i) {
359                 table = &display_page->tables[i];
360
361                 if (i != 0)
362                         total_offset += 1;
363                 table->offset = total_offset;
364                 for (int j = 0; j < table->n_cols; ++j) {
365                         col = &table->cols[j];
366                         col->offset = total_offset;
367                         if (j + 1 != table->n_cols)
368                                 total_offset += 1;
369                         total_offset += col->width;
370                 }
371                 table->width = total_offset - table->offset;
372         }
373 }
374
375 void display_page_init(struct display_page *display_page)
376 {
377         struct display_table *table;
378         struct display_column *col;
379         int table_width = 0;
380         int table_offset = 0;
381
382         memset(display_page, 0, sizeof(*display_page));
383         display_page->n_tables = 0;
384         for (size_t i = 0; i < sizeof(display_page->tables)/sizeof(display_page->tables[0]); ++i) {
385                 table = &display_page->tables[i];
386                 for (size_t j = 0; j < sizeof(table->cols)/sizeof(table->cols[0]); ++j) {
387                         col = &table->cols[j];
388                         col->display_page = display_page;
389                 }
390         }
391 }
392
393 struct display_table *display_page_add_table(struct display_page *display_page)
394 {
395         struct display_table *table = &display_page->tables[display_page->n_tables];
396
397         display_page->n_tables++;
398         return table;
399 }
400
401 void display_table_init(struct display_table *table, const char *title)
402 {
403         strcpy(table->title, title);
404         table->n_cols = 0;
405 }
406
407 struct display_column *display_table_add_col(struct display_table *table)
408 {
409         struct display_column *col = &table->cols[table->n_cols];
410
411         table->n_cols++;
412         return col;
413 }
414
415 void display_column_init(struct display_column *display_column, const char *title, unsigned width)
416 {
417         if (width < strlen(title))
418                 width = strlen(title);
419
420         strcpy(display_column->title, title);
421         display_column->width = width;
422         display_page_recalc_offsets(display_column->display_page);
423 }
424
425 int display_column_get_width(const struct display_column *display_column)
426 {
427         return display_column->width;
428 }
429
430 void display_page_draw_frame(const struct display_page *display_page, int height)
431 {
432         const struct display_table *table;
433         const struct display_column *col;
434
435         wattron(win_stat, A_BOLD);
436         wbkgdset(win_stat, COLOR_PAIR(YELLOW_ON_NOTHING));
437
438         for (int i = 0; i < display_page->n_tables; ++i) {
439                 table = &display_page->tables[i];
440
441                 if (i != 0)
442                         mvwvline(win_stat, 0, table->offset - 1,  ACS_VLINE, height + 2);
443
444                 mvwaddstrf(win_stat, 0, table->offset + table->width / 2 - strlen(table->title) / 2, "%s", table->title);
445                 for (int j = 0; j < table->n_cols; ++j) {
446                         col = &table->cols[j];
447
448                         if (j != 0)
449                                 mvwvline(win_stat, 1, col->offset - 1, ACS_VLINE, height + 1);
450                         mvwaddstrf(win_stat, 1, col->offset + col->width / 2 - strlen(col->title) / 2, "%s", col->title);
451                 }
452
453                 if (i + 1 == display_page->n_tables)
454                         mvwvline(win_stat, 0, table->offset + table->width,  ACS_VLINE, height + 2);
455         }
456         wbkgdset(win_stat, COLOR_PAIR(NO_COLOR));
457         wattroff(win_stat, A_BOLD);
458 }
459
460 void display_column_print(const struct display_column *display_column, int row, const char *fmt, ...)
461 {
462         if (row >= max_n_lines)
463                 return;
464
465         va_list ap;
466         char buffer[128] = {0};
467         char *to_print = buffer + 64;
468
469         va_start(ap, fmt);
470         int len = vsnprintf(to_print, sizeof(buffer) - 64, fmt, ap);
471         va_end(ap);
472
473         int offset = 0;
474         /* If column is too long, add ~ at the end. If it is too
475            short, align on the right. */
476         if (len > display_column->width) {
477                 to_print[display_column->width - 1] = '~';
478                 to_print[display_column->width] = '\0';
479         } else {
480                 int diff = display_column->width - len;
481
482                 to_print += len;
483                 to_print -= display_column->width;
484                 for (int i = 0; i < diff; i++)
485                         to_print[i] = ' ';
486         }
487
488         mvwaddstrf(win_stat, row + 2, display_column->offset, "%s", to_print);
489 }
490
491 void display_column_print_core_task(const struct display_column *display_column, int row, struct lcore_cfg *lconf, struct task_args *targ)
492 {
493         if (row >= max_n_lines)
494                 return;
495
496         if (lconf->n_tasks_run == 0) {
497                 wattron(win_stat, A_BOLD);
498                 wbkgdset(win_stat, COLOR_PAIR(RED_ON_NOTHING));
499         }
500         if (targ->id == 0)
501                 mvwaddstrf(win_stat, row + 2, display_column->offset, "%2u/", lconf->id);
502         if (lconf->n_tasks_run == 0) {
503                 wattroff(win_stat, A_BOLD);
504                 wbkgdset(win_stat, COLOR_PAIR(NO_COLOR));
505         }
506         if (!lconf_task_is_running(lconf, targ->id)) {
507                 wattron(win_stat, A_BOLD);
508                 wbkgdset(win_stat, COLOR_PAIR(RED_ON_NOTHING));
509         }
510         mvwaddstrf(win_stat, row + 2, display_column->offset + 3, "%1u", targ->id);
511         if (!lconf_task_is_running(lconf, targ->id)) {
512                 wattroff(win_stat, A_BOLD);
513                 wbkgdset(win_stat, COLOR_PAIR(NO_COLOR));
514         }
515 }
516
517 static void redraw_tabs(unsigned screen_id)
518 {
519         const size_t len = longest_title + 1;
520
521         for (size_t i = 0; i < n_screens; ++i) {
522                 if (i == screen_id)
523                         wbkgdset(win_tabs, COLOR_PAIR(BLACK_ON_GREEN));
524
525                 mvwaddstrf(win_tabs, 0, i*(len + 3), "%zu ", i+1);
526                 if (i != screen_id)
527                         wbkgdset(win_tabs, COLOR_PAIR(GREEN_ON_NOTHING));
528                 mvwaddstrf(win_tabs, 0, i*(len + 3) + 2, "%s", display_screens[i]->title);
529                 for (size_t j = strlen(display_screens[i]->title); j < len - 1; ++j)
530                         mvwaddstrf(win_tabs, 0, i*(len + 3) + 2 + j, " ");
531                 if (i != screen_id)
532                         wbkgdset(win_tabs, COLOR_PAIR(NO_COLOR));
533                 if (i == screen_id)
534                         wbkgdset(win_tabs, COLOR_PAIR(NO_COLOR));
535         }
536
537         wrefresh(win_tabs);
538 }
539
540 static void draw_title(void)
541 {
542         char title_str[128];
543
544         snprintf(title_str, sizeof(title_str), "%s %s: %s", PROGRAM_NAME, VERSION_STR, prox_cfg.name);
545
546         wbkgd(win_title, COLOR_PAIR(BLACK_ON_GREEN));
547         title_len = strlen(title_str);
548         mvwaddstrf(win_title, 0, (COLS - title_len)/2, "%s", title_str);
549
550         redraw_tabs(screen_state.chosen_screen);
551 }
552
553 static void draw_general_frame(void)
554 {
555         if (screen_state.toggle == 0) {
556                 wattron(win_general, A_BOLD);
557                 wbkgdset(win_general, COLOR_PAIR(MAGENTA_ON_NOTHING));
558                 mvwaddstrf(win_general, 0, 9, "rx:         tx:          diff:                     rx:          tx:                        %%:");
559                 mvwaddstrf(win_general, 1, 9, "rx:         tx:          err:                      rx:          tx:          err:          %%:");
560                 wbkgdset(win_general, COLOR_PAIR(NO_COLOR));
561
562                 wbkgdset(win_general, COLOR_PAIR(BLUE_ON_NOTHING));
563                 mvwaddstrf(win_general, 0, 0, "Host pps ");
564                 mvwaddstrf(win_general, 1, 0, "NICs pps ");
565
566                 wbkgdset(win_general, COLOR_PAIR(CYAN_ON_NOTHING));
567                 mvwaddstrf(win_general, 0, 56, "avg");
568                 mvwaddstrf(win_general, 1, 56, "avg");
569                 wbkgdset(win_general, COLOR_PAIR(NO_COLOR));
570                 wattroff(win_general, A_BOLD);
571         } else {
572                 wattron(win_general, A_BOLD);
573                 wbkgdset(win_general, COLOR_PAIR(BLUE_ON_NOTHING));
574                 mvwaddstrf(win_general, 0, 9, "rx:                   tx:                   rx-tx:                      tx/rx:            rx/tx:");
575                 mvwaddstrf(win_general, 1, 9, "rx:                   tx:                   err:                        tx/rx:            rx/tx:");
576                 wbkgdset(win_general, COLOR_PAIR(NO_COLOR));
577
578                 wbkgdset(win_general, COLOR_PAIR(CYAN_ON_NOTHING));
579                 mvwaddstrf(win_general, 0, 0, "Host tot ");
580                 mvwaddstrf(win_general, 1, 0, "NICs tot ");
581                 wattroff(win_general, A_BOLD);
582         }
583 }
584
585 static void draw_status_bar(void)
586 {
587         wbkgd(win_help, COLOR_PAIR(BLACK_ON_WHITE));
588         werase(win_help);
589         mvwaddstrf(win_help, 0, 0,
590                    "Enter 'help' or command, <ESC> or 'quit' to exit, "
591                    "1-%zu to switch screens and 0 to reset stats, '=' to toggle between per-sec and total stats",
592                    n_screens);
593         wrefresh(win_help);
594         mvwin(win_help, LINES - 1, 0);
595 }
596
597 static void draw_log_window(void)
598 {
599         idlok(win_txt, FALSE);
600         /* Get scrolling */
601         scrollok(win_txt, TRUE);
602
603         /* Leave cursor where it was */
604         leaveok(win_txt, TRUE);
605         wbkgd(win_txt, COLOR_PAIR(BLACK_ON_CYAN));
606         wrefresh(win_txt);
607 }
608
609 static void stats_display_layout(uint8_t in_place)
610 {
611         uint8_t cur_stats_height;
612
613         cur_stats_height = current_screen->get_height();
614         cur_stats_height = cur_stats_height > max_n_lines? max_n_lines: cur_stats_height;
615
616         display_lock();
617         if (!in_place) {
618                 // moving existing windows does not work
619                 delwin(win_txt);
620                 delwin(win_general);
621                 delwin(win_title);
622                 delwin(win_tabs);
623                 delwin(win_cmd);
624                 delwin(win_txt);
625                 delwin(win_help);
626
627                 clear();
628         }
629
630         if (!in_place) {
631                 win_stat = create_subwindow(cur_stats_height + 2, 0, 4, 0);
632                 win_tabs = create_subwindow(1, 0, 1, 0);
633                 win_general = create_subwindow(2, 0, 2, 0);
634                 win_title = create_subwindow(1, 0, 0, 0);
635                 win_cmd = create_subwindow(1, 0, cur_stats_height + 2 + 4,  0);
636                 win_txt_height = LINES - cur_stats_height - 2 - 3 - 3;
637                 win_txt = create_subwindow(win_txt_height, 0, cur_stats_height + 4 + 3, 0);
638                 win_help = create_subwindow(1, 0, LINES - 1, 0);
639         }
640
641         draw_title();
642         draw_general_frame();
643         /* Command line */
644         wbkgd(win_cmd, COLOR_PAIR(BLACK_ON_YELLOW));
645         idlok(win_cmd, FALSE);
646         /* Move cursor at insertion point */
647         leaveok(win_cmd, FALSE);
648
649         draw_status_bar();
650         draw_log_window();
651
652         /* Draw everything to the screen */
653         refresh();
654         current_screen->draw_frame(&screen_state);
655         display_unlock();
656
657         refresh_cmd_win();
658         display_stats();
659 }
660
661 void display_end(void)
662 {
663         pthread_mutex_destroy(&disp_mtx);
664
665         if (scr != NULL) {
666                 endwin();
667         }
668 }
669
670 static void pps_print(WINDOW *dst_scr, int y, int x, uint64_t val, int is_blue)
671 {
672         uint64_t rx_pps_disp = val;
673         uint64_t rx_pps_disp_frac = 0;
674         uint32_t ten_pow3 = 0;
675         static const char *units = " KMG";
676         char rx_unit = ' ';
677
678         while (rx_pps_disp > 1000) {
679                 rx_pps_disp /= 1000;
680                 rx_pps_disp_frac = (val - rx_pps_disp*1000) / 10;
681                 val /= 1000;
682                 ten_pow3++;
683         }
684
685         if (ten_pow3 >= strlen(units)) {
686                 wbkgdset(dst_scr, COLOR_PAIR(RED_ON_NOTHING));
687                 mvwaddstrf(dst_scr, y, x, "---");
688                 wbkgdset(dst_scr, COLOR_PAIR(NO_COLOR));
689                 return;
690         }
691
692         rx_unit = units[ten_pow3];
693
694         wattron(dst_scr, A_BOLD);
695         if (is_blue) {
696                 wbkgdset(dst_scr, COLOR_PAIR(BLUE_ON_NOTHING));
697         }
698         else
699                 wbkgdset(dst_scr, COLOR_PAIR(CYAN_ON_NOTHING));
700
701         mvwaddstrf(dst_scr, y, x, "%3lu", rx_pps_disp);
702         if (rx_unit != ' ') {
703                 mvwaddstrf(dst_scr, y, x + 3, ".%02lu", rx_pps_disp_frac);
704                 wattroff(dst_scr, A_BOLD);
705                 wbkgdset(dst_scr, COLOR_PAIR(WHITE_ON_NOTHING));
706                 wattron(dst_scr, A_BOLD);
707                 mvwaddstrf(dst_scr, y, x + 6, "%c", rx_unit);
708                 wattroff(dst_scr, A_BOLD);
709                 wbkgdset(dst_scr, COLOR_PAIR(NO_COLOR));
710         }
711         else {
712                 mvwaddstrf(dst_scr, y, x + 3, "    ");
713         }
714         wattroff(dst_scr, A_BOLD);
715         wbkgdset(dst_scr, COLOR_PAIR(NO_COLOR));
716 }
717
718 static void display_stats_general_per_sec(void)
719 {
720         struct global_stats_sample *gsl = stats_get_global_stats(1);
721         struct global_stats_sample *gsp = stats_get_global_stats(0);
722
723         uint64_t rx_pps = val_to_rate(gsl->host_rx_packets - gsp->host_rx_packets, gsl->tsc - gsp->tsc);
724         uint64_t tx_pps = val_to_rate(gsl->host_tx_packets - gsp->host_tx_packets, gsl->tsc - gsp->tsc);
725         /* Host: RX, TX, Diff */
726         pps_print(win_general, 0, 12, rx_pps, 1);
727         pps_print(win_general, 0, 25, tx_pps, 1);
728
729         uint64_t diff = 0;
730         if (rx_pps > tx_pps)
731                 diff = rx_pps - tx_pps;
732         pps_print(win_general, 0, 40, diff, 1);
733
734         uint64_t nics_rx_pps = val_to_rate(gsl->nics_rx_packets - gsp->nics_rx_packets, gsl->tsc - gsp->tsc);
735         uint64_t nics_tx_pps = val_to_rate(gsl->nics_tx_packets - gsp->nics_tx_packets, gsl->tsc - gsp->tsc);
736         uint64_t nics_ierrors = val_to_rate(gsl->nics_ierrors - gsp->nics_ierrors, gsl->tsc - gsp->tsc);
737         uint64_t nics_imissed = val_to_rate(gsl->nics_imissed - gsp->nics_imissed, gsl->tsc - gsp->tsc);
738
739         /* NIC: RX, TX, Diff */
740         pps_print(win_general, 1, 12, nics_rx_pps, 1);
741         pps_print(win_general, 1, 25, nics_tx_pps, 1);
742         pps_print(win_general, 1, 40, nics_ierrors + nics_imissed, 1);
743
744         wbkgdset(win_general, COLOR_PAIR(CYAN_ON_NOTHING));
745         wattron(win_general, A_BOLD);
746         mvwaddstrf(win_general, 0, 103, "%6.2f", tx_pps > rx_pps? 100 : tx_pps * 100.0 / rx_pps);
747         wattroff(win_general, A_BOLD);
748         wbkgdset(win_general, COLOR_PAIR(NO_COLOR));
749
750         struct global_stats_sample *gsb = stats_get_global_stats_beg();
751         if (gsb) {
752                 uint64_t rx_pps = val_to_rate(gsl->host_rx_packets - gsb->host_rx_packets, gsl->tsc - gsb->tsc);
753                 uint64_t tx_pps = val_to_rate(gsl->host_tx_packets - gsb->host_tx_packets, gsl->tsc - gsb->tsc);
754
755                 uint64_t nics_rx_pps = val_to_rate(gsl->nics_rx_packets - gsb->nics_rx_packets, gsl->tsc - gsb->tsc);
756                 uint64_t nics_tx_pps = val_to_rate(gsl->nics_tx_packets - gsb->nics_tx_packets, gsl->tsc - gsb->tsc);
757                 uint64_t nics_ierrors = val_to_rate(gsl->nics_ierrors - gsb->nics_ierrors, gsl->tsc - gsb->tsc);
758                 uint64_t nics_imissed = val_to_rate(gsl->nics_imissed - gsb->nics_imissed, gsl->tsc - gsb->tsc);
759
760                 pps_print(win_general, 0, 64, rx_pps, 0);
761                 pps_print(win_general, 0, 77, tx_pps, 0);
762
763                 pps_print(win_general, 1, 64, nics_rx_pps, 0);
764                 pps_print(win_general, 1, 77, nics_tx_pps, 0);
765                 pps_print(win_general, 1, 91, nics_ierrors + nics_imissed, 0);
766
767                 wbkgdset(win_general, COLOR_PAIR(CYAN_ON_NOTHING));
768                 wattron(win_general, A_BOLD);
769                 uint64_t nics_in = gsl->host_rx_packets - gsb->host_rx_packets + gsl->nics_ierrors - gsb->nics_ierrors + gsl->nics_imissed - gsb->nics_imissed;
770                 uint64_t nics_out = gsl->host_tx_packets - gsb->host_tx_packets;
771                 mvwaddstrf(win_general, 1, 103, "%6.2f", nics_out > nics_in?
772                            100 : nics_out * 100.0 / nics_in);
773                 wattron(win_general, A_BOLD);
774                 wbkgdset(win_general, COLOR_PAIR(NO_COLOR));
775         }
776 }
777
778 static void display_stats_general_total(void)
779 {
780         struct global_stats_sample *gsl = stats_get_global_stats(1);
781
782         int64_t diff = (int64_t)gsl->host_rx_packets - gsl->host_tx_packets;
783         uint32_t percent;
784
785         /* Host: RX, TX, Diff */
786         mvwaddstrf(win_general, 0, 13, "%16lu", gsl->host_rx_packets);
787         mvwaddstrf(win_general, 0, 35, "%16lu", gsl->host_tx_packets);
788         mvwaddstrf(win_general, 0, 60, "%16"PRId64"", diff);
789         if (gsl->host_rx_packets == 0)
790                 percent = 1000000;
791         else
792                 percent = gsl->host_tx_packets * 1000000 / gsl->host_rx_packets;
793         mvwaddstrf(win_general, 0, 88, "%3u.%04u%%", percent / 10000, percent % 10000);
794         if (gsl->host_tx_packets == 0)
795                 percent = 1000000;
796         else
797                 percent = gsl->host_rx_packets * 1000000 / gsl->host_tx_packets;
798         mvwaddstrf(win_general, 0, 106, "%3u.%04u%%", percent / 10000, percent % 10000);
799
800         mvwaddstrf(win_general, 1, 13, "%16lu", gsl->nics_rx_packets);
801         mvwaddstrf(win_general, 1, 35, "%16lu", gsl->nics_tx_packets);
802         mvwaddstrf(win_general, 1, 60, "%16lu", gsl->nics_ierrors + gsl->nics_imissed);
803         if (gsl->nics_rx_packets == 0)
804                 percent = 1000000;
805         else
806                 percent = gsl->nics_tx_packets * 1000000 / gsl->nics_rx_packets;
807         mvwaddstrf(win_general, 1, 88, "%3u.%04u%%", percent / 10000, percent % 10000);
808         if (gsl->nics_tx_packets == 0)
809                 percent = 1000000;
810         else
811                 percent = gsl->nics_rx_packets * 1000000 / gsl->nics_tx_packets;
812         mvwaddstrf(win_general, 1, 106, "%3u.%04u%%", percent / 10000, percent % 10000);
813 }
814
815 static void display_stats_general(void)
816 {
817         /* moment when stats were gathered. */
818         uint64_t cur_tsc = stats_get_last_tsc();
819         uint64_t up_time = tsc_to_sec(cur_tsc - stats_global_start_tsc());
820         uint64_t up_time2 = tsc_to_sec(cur_tsc - stats_global_beg_tsc());
821         uint64_t rem_time = -1;
822         char title_str[128] = {0};
823
824         if (stats_global_end_tsc()) {
825                 uint64_t rem_tsc = stats_global_end_tsc() > cur_tsc? stats_global_end_tsc() - cur_tsc : 0;
826
827                 rem_time = tsc_to_sec(rem_tsc);
828         }
829
830         if (up_time != up_time2 && cur_tsc >= stats_global_beg_tsc()) {
831                 if (stats_global_end_tsc())
832                         snprintf(title_str, sizeof(title_str), "%5lu (%lu) up, %lu rem", up_time, up_time2, rem_time);
833                 else
834                         snprintf(title_str, sizeof(title_str), "%5lu (%lu) up", up_time, up_time2);
835         }
836         else {
837                 if (stats_global_end_tsc())
838                         snprintf(title_str, sizeof(title_str), "%5lu up, %lu rem", up_time, rem_time);
839                 else
840                         snprintf(title_str, sizeof(title_str), "%5lu up", up_time);
841         }
842
843         /* Only print up time information if there is enough space */
844         if ((int)((COLS + title_len)/2 + strlen(title_str) + 1) < COLS) {
845                 mvwaddstrf(win_title, 0, COLS - strlen(title_str), "%s", title_str);
846                 wrefresh(win_title);
847         }
848
849         if (screen_state.toggle == 0)
850                 display_stats_general_per_sec();
851         else
852                 display_stats_general_total();
853
854         wrefresh(win_general);
855 }
856
857 char *print_time_unit_err_usec(char *dst, struct time_unit_err *t)
858 {
859         uint64_t nsec_total = time_unit_to_nsec(&t->time);
860
861         uint64_t usec = nsec_total/1000;
862         uint64_t nsec = nsec_total - usec*1000;
863
864         uint64_t nsec_total_error = time_unit_to_nsec(&t->error);
865
866         uint64_t usec_error = nsec_total_error/1000;
867         uint64_t nsec_error = nsec_total_error - usec_error*1000;
868
869         sprintf(dst, "%4"PRIu64".%03"PRIu64" +/- %2"PRIu64".%03"PRIu64"", usec, nsec, usec_error, nsec_error);
870         return dst;
871 }
872
873 char *print_time_unit_usec(char *dst, struct time_unit *t)
874 {
875         uint64_t nsec_total = time_unit_to_nsec(t);
876
877         uint64_t usec = nsec_total/1000;
878         uint64_t nsec = nsec_total - usec*1000;
879
880         sprintf(dst, "%4"PRIu64".%03"PRIu64"", usec, nsec);
881         return dst;
882 }
883
884 void toggle_display_screen(void)
885 {
886         screen_state.toggle = !screen_state.toggle;
887         stats_display_layout(0);
888 }
889
890 void display_screen(unsigned screen_id)
891 {
892         if (screen_id >= n_screens) {
893                 plog_err("Unsupported screen %d\n", screen_id + 1);
894                 return;
895         }
896
897         if (screen_state.chosen_screen == screen_id) {
898                 stats_display_layout(1);
899         }
900         else {
901                 screen_state.chosen_screen = screen_id;
902                 current_screen = display_screens[screen_id];
903                 stats_display_layout(0);
904         }
905 }
906
907 void display_page_up(void)
908 {
909 }
910
911 void display_page_down(void)
912 {
913 }
914
915 void display_refresh(void)
916 {
917         stats_display_layout(1);
918 }
919
920 void display_stats(void)
921 {
922         display_lock();
923         current_screen->draw_stats(&screen_state);
924         display_stats_general();
925         wrefresh(win_stat);
926         display_unlock();
927 }
928
929 static char pages[32768] = {0};
930 static int cur_idx = 0;
931 static size_t pages_len = 0;
932
933 void display_print_page(void)
934 {
935         int n_lines = 0;
936         int cur_idx_prev = cur_idx;
937
938         if (cur_idx >= (int)pages_len) {
939                 return;
940         }
941
942         display_lock();
943         for (size_t i = cur_idx; i < pages_len; ++i) {
944                 if (pages[i] == '\n') {
945                         n_lines++;
946                         if (n_lines == win_txt_height - 2) {
947                                 pages[i] = 0;
948                                 cur_idx = i + 1;
949                                 break;
950                         }
951                 }
952         }
953
954         waddstr(win_txt, pages + cur_idx_prev);
955         if (cur_idx != cur_idx_prev && cur_idx < (int)pages_len)
956                 waddstr(win_txt, "\nPRESS ENTER FOR MORE...\n");
957         else {
958                 pages_len = 0;
959         }
960         wrefresh(win_txt);
961         display_unlock();
962 }
963
964 void display_print(const char *str)
965 {
966         display_lock();
967
968         if (scr == NULL) {
969                 fputs(str, stdout);
970                 fflush(stdout);
971                 display_unlock();
972                 return;
973         }
974
975         /* Check if the whole string can fit on the screen. */
976         pages_len = strlen(str);
977         int n_lines = 0;
978         memset(pages, 0, sizeof(pages));
979         memcpy(pages, str, pages_len);
980         cur_idx = 0;
981         for (size_t i = 0; i < pages_len; ++i) {
982                 if (pages[i] == '\n') {
983                         n_lines++;
984                         if (n_lines == win_txt_height - 2) {
985                                 pages[i] = 0;
986                                 cur_idx = i + 1;
987                                 break;
988                         }
989                 }
990         }
991
992         waddstr(win_txt, pages);
993         if (cur_idx != 0)
994                 waddstr(win_txt, "\nPRESS ENTER FOR MORE...\n");
995         else
996                 pages_len = 0;
997
998         wrefresh(win_txt);
999         display_unlock();
1000 }