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