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