vppinfra: introduce clib_perfmom
[vpp.git] / src / vppinfra / perfmon / perfmon.c
1 /* SPDX-License-Identifier: Apache-2.0
2  * Copyright(c) 2022 Cisco Systems, Inc.
3  */
4
5 #include <vppinfra/format.h>
6 #include <vppinfra/error.h>
7 #include <vppinfra/perfmon/perfmon.h>
8 #include <vppinfra/format_table.h>
9
10 clib_perfmon_main_t clib_perfmon_main;
11
12 __clib_export clib_error_t *
13 clib_perfmon_init_by_bundle_name (clib_perfmon_ctx_t *ctx, char *fmt, ...)
14 {
15   clib_perfmon_main_t *pm = &clib_perfmon_main;
16   clib_perfmon_bundle_t *b = 0;
17   int group_fd = -1;
18   clib_error_t *err = 0;
19   va_list va;
20   char *bundle_name;
21
22   struct perf_event_attr pe = {
23     .size = sizeof (struct perf_event_attr),
24     .disabled = 1,
25     .exclude_kernel = 1,
26     .exclude_hv = 1,
27     .pinned = 1,
28     .exclusive = 1,
29     .read_format = (PERF_FORMAT_GROUP | PERF_FORMAT_TOTAL_TIME_ENABLED |
30                     PERF_FORMAT_TOTAL_TIME_RUNNING),
31   };
32
33   va_start (va, fmt);
34   bundle_name = (char *) va_format (0, fmt, &va);
35   va_end (va);
36   vec_add1 (bundle_name, 0);
37
38   for (clib_perfmon_bundle_reg_t *r = pm->bundle_regs; r; r = r->next)
39     {
40       if (strncmp (r->bundle->name, bundle_name, vec_len (bundle_name) - 1))
41         continue;
42       b = r->bundle;
43       break;
44     }
45
46   if (b == 0)
47     {
48       err = clib_error_return (0, "Unknown bundle '%s'", bundle_name);
49       goto done;
50     }
51
52   clib_memset_u8 (ctx, 0, sizeof (clib_perfmon_ctx_t));
53   vec_validate_init_empty (ctx->fds, b->n_events - 1, -1);
54   ctx->bundle = b;
55
56   for (int i = 0; i < b->n_events; i++)
57     {
58       pe.config = b->config[i];
59       pe.type = b->type;
60       int fd = syscall (__NR_perf_event_open, &pe, /* pid */ 0, /* cpu */ -1,
61                         /* group_fd */ group_fd, /* flags */ 0);
62       if (fd < 0)
63         {
64           err = clib_error_return_unix (0, "perf_event_open[%u]", i);
65           goto done;
66         }
67
68       if (ctx->debug)
69         fformat (stderr, "perf event %u open, fd %d\n", i, fd);
70
71       if (group_fd == -1)
72         {
73           group_fd = fd;
74           pe.pinned = 0;
75           pe.exclusive = 0;
76         }
77
78       ctx->fds[i] = fd;
79     }
80
81   ctx->group_fd = group_fd;
82   ctx->data = vec_new (u64, 3 + b->n_events);
83   ctx->ref_clock = os_cpu_clock_frequency ();
84   vec_validate (ctx->capture_groups, 0);
85
86 done:
87   if (err)
88     clib_perfmon_free (ctx);
89
90   vec_free (bundle_name);
91   return err;
92 }
93
94 __clib_export void
95 clib_perfmon_free (clib_perfmon_ctx_t *ctx)
96 {
97   clib_perfmon_clear (ctx);
98   vec_free (ctx->captures);
99   vec_free (ctx->capture_groups);
100
101   for (int i = 0; i < vec_len (ctx->fds); i++)
102     if (ctx->fds[i] > -1)
103       close (ctx->fds[i]);
104   vec_free (ctx->fds);
105   vec_free (ctx->data);
106 }
107
108 __clib_export void
109 clib_perfmon_clear (clib_perfmon_ctx_t *ctx)
110 {
111   for (int i = 0; i < vec_len (ctx->captures); i++)
112     vec_free (ctx->captures[i].desc);
113   vec_reset_length (ctx->captures);
114   for (int i = 0; i < vec_len (ctx->capture_groups); i++)
115     vec_free (ctx->capture_groups[i].name);
116   vec_reset_length (ctx->capture_groups);
117 }
118
119 __clib_export u64 *
120 clib_perfmon_capture (clib_perfmon_ctx_t *ctx, u32 n_ops, char *fmt, ...)
121 {
122   u32 read_size = (ctx->bundle->n_events + 3) * sizeof (u64);
123   clib_perfmon_capture_t *c;
124   u64 d[CLIB_PERFMON_MAX_EVENTS + 3];
125   va_list va;
126
127   if ((read (ctx->group_fd, d, read_size) != read_size))
128     {
129       if (ctx->debug)
130         fformat (stderr, "reading of %u bytes failed, %s (%d)\n", read_size,
131                  strerror (errno), errno);
132       return 0;
133     }
134
135   if (ctx->debug)
136     {
137       fformat (stderr, "read events: %lu enabled: %lu running: %lu ", d[0],
138                d[1], d[2]);
139       fformat (stderr, "data: [%lu", d[3]);
140       for (int i = 1; i < ctx->bundle->n_events; i++)
141         fformat (stderr, ", %lu", d[i + 3]);
142       fformat (stderr, "]\n");
143     }
144
145   vec_add2 (ctx->captures, c, 1);
146
147   va_start (va, fmt);
148   c->desc = va_format (0, fmt, &va);
149   va_end (va);
150
151   c->n_ops = n_ops;
152   c->group = vec_len (ctx->capture_groups) - 1;
153   c->time_enabled = d[1];
154   c->time_running = d[2];
155   for (int i = 0; i < CLIB_PERFMON_MAX_EVENTS; i++)
156     c->data[i] = d[i + 3];
157
158   return ctx->data + vec_len (ctx->data) - ctx->bundle->n_events;
159 }
160
161 __clib_export void
162 clib_perfmon_capture_group (clib_perfmon_ctx_t *ctx, char *fmt, ...)
163 {
164   clib_perfmon_capture_group_t *cg;
165   va_list va;
166
167   cg = vec_end (ctx->capture_groups) - 1;
168
169   if (cg->name != 0)
170     vec_add2 (ctx->capture_groups, cg, 1);
171
172   va_start (va, fmt);
173   cg->name = va_format (0, fmt, &va);
174   va_end (va);
175   ASSERT (cg->name);
176 }
177
178 __clib_export void
179 clib_perfmon_warmup (clib_perfmon_ctx_t *ctx)
180 {
181   for (u64 i = 0; i < (u64) ctx->ref_clock; i++)
182     asm inline("" : : "r"(i * i) : "memory");
183 }
184
185 __clib_export u8 *
186 format_perfmon_bundle (u8 *s, va_list *args)
187 {
188   clib_perfmon_ctx_t *ctx = va_arg (*args, clib_perfmon_ctx_t *);
189   clib_perfmon_capture_t *c;
190   clib_perfmon_capture_group_t *cg = 0;
191   char **hdr = ctx->bundle->column_headers;
192   table_t _t = {}, *t = &_t;
193   u32 n_row = 0, col = 0;
194
195   table_add_header_row (t, 0);
196
197   for (char **h = ctx->bundle->column_headers; h[0]; h++)
198     n_row++;
199
200   vec_foreach (c, ctx->captures)
201     {
202       if (cg != ctx->capture_groups + c->group)
203         {
204           cg = ctx->capture_groups + c->group;
205           table_format_cell (t, col, -1, "%v", cg->name);
206           table_set_cell_align (t, col, -1, TTAA_LEFT);
207           table_set_cell_fg_color (t, col, -1, TTAC_BRIGHT_RED);
208
209           table_format_cell (t, col, 0, "Ops");
210           table_set_cell_fg_color (t, col, 0, TTAC_BRIGHT_YELLOW);
211
212           for (int i = 0; i < n_row; i++)
213             {
214               table_format_cell (t, col, i + 1, "%s", hdr[i]);
215               table_set_cell_fg_color (t, col, i + 1, TTAC_BRIGHT_YELLOW);
216             }
217           col++;
218         }
219       table_format_cell (t, col, -1, "%v", c->desc);
220       table_format_cell (t, col, 0, "%7u", c->n_ops);
221       for (int i = 0; i < n_row; i++)
222         table_format_cell (t, col, i + 1, "%U", ctx->bundle->format_fn, ctx, c,
223                            i);
224       col++;
225     }
226
227   s = format (s, "%U", format_table, t);
228   table_free (t);
229   return s;
230 }