perfmon: new perfmon plugin
[vpp.git] / src / plugins / perfmon / table.c
1 /*
2   Copyright (c) 2020 Damjan Marion
3
4   Permission is hereby granted, free of charge, to any person obtaining
5   a copy of this software and associated documentation files (the
6   "Software"), to deal in the Software without restriction, including
7   without limitation the rights to use, copy, modify, merge, publish,
8   distribute, sublicense, and/or sell copies of the Software, and to
9   permit persons to whom the Software is furnished to do so, subject to
10   the following conditions:
11
12   The above copyright notice and this permission notice shall be
13   included in all copies or substantial portions of the Software.
14
15   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19   LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20   OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21   WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24 #include <vppinfra/format.h>
25 #include "table.h"
26
27 static table_text_attr_t default_title = {
28   .flags = TTAF_FG_COLOR_SET | TTAF_BOLD,
29   .fg_color = TTAC_YELLOW,
30   .align = TTAA_CENTER,
31 };
32
33 static table_text_attr_t default_body = {
34   .align = TTAA_RIGHT,
35 };
36
37 static table_text_attr_t default_header_col = {
38   .flags = TTAF_FG_COLOR_SET,
39   .fg_color = TTAC_YELLOW,
40   .align = TTAA_CENTER,
41 };
42
43 static table_text_attr_t default_header_row = {
44   .flags = TTAF_FG_COLOR_SET | TTAF_BOLD,
45   .fg_color = TTAC_GREEN,
46   .align = TTAA_LEFT,
47 };
48
49 u8 *
50 format_text_cell (table_t *t, u8 *s, table_cell_t *c, table_text_attr_t *def,
51                   int size)
52 {
53   table_text_attr_t _a = {}, *a = &_a;
54
55   if (a == 0)
56     return format (s, t->no_ansi ? "" : "\x1b[0m");
57
58   clib_memcpy (a, def, sizeof (table_text_attr_t));
59
60   if (t->no_ansi == 0)
61     {
62       int *codes = 0;
63       if (c->attr.flags & TTAF_FG_COLOR_SET)
64         {
65           a->fg_color = c->attr.fg_color;
66           a->flags |= TTAF_FG_COLOR_SET;
67         }
68
69       if (c->attr.flags & TTAF_BG_COLOR_SET)
70         {
71           a->bg_color = c->attr.bg_color;
72           a->flags |= TTAF_BG_COLOR_SET;
73         }
74
75       if (a->flags & TTAF_RESET)
76         vec_add1 (codes, 0);
77
78       if (a->flags & TTAF_BOLD)
79         vec_add1 (codes, 1);
80
81       if (a->flags & TTAF_DIM)
82         vec_add1 (codes, 2);
83
84       if (a->flags & TTAF_UNDERLINE)
85         vec_add1 (codes, 4);
86
87       if (a->flags & TTAF_FG_COLOR_SET)
88         vec_add1 (codes,
89                   (a->flags & TTAF_FG_COLOR_BRIGHT ? 90 : 30) + a->fg_color);
90
91       if (a->flags & TTAF_BG_COLOR_SET)
92         vec_add1 (codes,
93                   (a->flags & TTAF_BG_COLOR_BRIGHT ? 100 : 40) + a->bg_color);
94
95       if (codes)
96         {
97           s = format (s, "\x1b[");
98           for (int i = 0; i < vec_len (codes); i++)
99             s = format (s, "%s%u", i ? ";" : "", codes[i]);
100           s = format (s, "m");
101           vec_free (codes);
102         }
103     }
104
105   u8 *fmt = 0;
106   table_text_attr_align_t align = c->attr.align;
107   if (align == TTAA_DEFAULT)
108     align = a->align;
109   if (align == TTAA_LEFT)
110     fmt = format (fmt, "%%-%uv%c", size, 0);
111   else if (align == TTAA_CENTER)
112     fmt = format (fmt, "%%=%uv%c", size, 0);
113   else
114     fmt = format (fmt, "%%%uv%c", size, 0);
115   s = format (s, (char *) fmt, c->text);
116   vec_free (fmt);
117   return format (s, t->no_ansi ? "" : "\x1b[0m");
118 }
119
120 u8 *
121 format_table (u8 *s, va_list *args)
122 {
123   table_t *t = va_arg (*args, table_t *);
124   table_cell_t title_cell = { .text = t->title };
125   int table_width = 0;
126   for (int i = 0; i < vec_len (t->row_sizes); i++)
127     table_width += t->row_sizes[i];
128
129   s = format_text_cell (t, s, &title_cell, &default_title, table_width);
130   s = format (s, "\n");
131
132   for (int c = 0; c < vec_len (t->cells); c++)
133     {
134       table_text_attr_t *col_default;
135
136       if (c < t->n_header_cols)
137         col_default = &default_header_col;
138       else
139         col_default = &default_body;
140
141       for (int r = 0; r < vec_len (t->cells[c]); r++)
142         {
143           table_text_attr_t *row_default = col_default;
144           if (r)
145             s = format (s, " ");
146           if (r < t->n_header_rows && c >= t->n_header_cols)
147             row_default = &default_header_row;
148           s = format_text_cell (t, s, &t->cells[c][r], row_default,
149                                 t->row_sizes[r]);
150         }
151       s = format (s, "\n");
152     }
153
154   return s;
155 }
156
157 void
158 table_format_title (table_t *t, char *fmt, ...)
159 {
160   va_list va;
161
162   va_start (va, fmt);
163   t->title = va_format (t->title, fmt, &va);
164   va_end (va);
165 }
166
167 static table_cell_t *
168 table_get_cell (table_t *t, int c, int r)
169 {
170   c += t->n_header_cols;
171   r += t->n_header_rows;
172
173   /* grow table if needed */
174   vec_validate (t->cells, c);
175   for (int i = 0; i < vec_len (t->cells); i++)
176     vec_validate (t->cells[i], r);
177   return &t->cells[c][r];
178 }
179
180 void
181 table_format_cell (table_t *t, int c, int r, char *fmt, ...)
182 {
183   table_cell_t *cell = table_get_cell (t, c, r);
184   va_list va;
185
186   c += t->n_header_cols;
187   r += t->n_header_rows;
188
189   va_start (va, fmt);
190   cell->text = va_format (t->cells[c][r].text, fmt, &va);
191   va_end (va);
192
193   vec_validate (t->row_sizes, r);
194   t->row_sizes[r] = clib_max (t->row_sizes[r], vec_len (t->cells[c][r].text));
195 }
196
197 void
198 table_set_cell_align (table_t *t, int c, int r, table_text_attr_align_t a)
199 {
200   table_cell_t *cell = table_get_cell (t, c, r);
201   cell->attr.align = a;
202 }
203
204 void
205 table_set_cell_fg_color (table_t *t, int c, int r, table_text_attr_color_t v)
206 {
207   table_cell_t *cell = table_get_cell (t, c, r);
208   cell->attr.fg_color = v;
209   cell->attr.flags |= TTAF_FG_COLOR_SET;
210 }
211
212 void
213 table_set_cell_bg_color (table_t *t, int c, int r, table_text_attr_color_t v)
214 {
215   table_cell_t *cell = table_get_cell (t, c, r);
216   cell->attr.bg_color = v;
217   cell->attr.flags |= TTAF_BG_COLOR_SET;
218 }
219
220 void
221 table_free (table_t *t)
222 {
223   for (int c = 0; c < vec_len (t->cells); c++)
224     {
225       for (int r = 0; r < vec_len (t->cells[c]); r++)
226         vec_free (t->cells[c][r].text);
227       vec_free (t->cells[c]);
228     }
229   vec_free (t->cells);
230   vec_free (t->row_sizes);
231   vec_free (t->title);
232   clib_memset (t, 0, sizeof (table_t));
233 }
234
235 void
236 table_add_header_col (table_t *t, int n_strings, ...)
237 {
238   va_list arg;
239   int r, c = t->n_header_cols++;
240   int n_rows;
241
242   vec_insert (t->cells, 1, c);
243   n_rows = clib_max (n_strings, 1);
244   n_rows = clib_max (vec_len (t->row_sizes), n_rows);
245   vec_validate (t->cells[c], n_rows - 1);
246
247   va_start (arg, n_strings);
248   for (r = 0; r < n_rows; r++)
249     {
250       if (n_strings-- > 0)
251         table_format_cell (t, -1, r - t->n_header_rows, "%s",
252                            va_arg (arg, char *));
253     }
254   va_end (arg);
255 }
256
257 void
258 table_add_header_row (table_t *t, int n_strings, ...)
259 {
260   va_list arg;
261   int c, r = t->n_header_rows++;
262
263   vec_validate (t->cells, n_strings + t->n_header_cols - 1);
264
265   va_start (arg, n_strings);
266   for (c = t->n_header_cols; c < vec_len (t->cells); c++)
267     {
268       vec_insert (t->cells[c + t->n_header_cols], 1, r);
269       if (n_strings-- > 0)
270         table_format_cell (t, c, -1, "%s", va_arg (arg, char *));
271     }
272   va_end (arg);
273 }