vppinfra: multiple perf bundle support in test_vector_funcs
[vpp.git] / src / vppinfra / vector / test / test.c
1 /* SPDX-License-Identifier: Apache-2.0
2  * Copyright(c) 2021 Cisco Systems, Inc.
3  */
4
5 #include <vppinfra/format.h>
6 #include <vppinfra/vector/test/test.h>
7 #include <vppinfra/error.h>
8
9 test_main_t test_main;
10
11 int
12 test_march_supported (clib_march_variant_type_t type)
13 {
14 #define _(s, n)                                                               \
15   if (CLIB_MARCH_VARIANT_TYPE_##s == type)                                    \
16     return clib_cpu_march_priority_##s ();
17   foreach_march_variant
18 #undef _
19     return 0;
20 }
21
22 clib_error_t *
23 test_funct (test_main_t *tm)
24 {
25   for (int i = 0; i < CLIB_MARCH_TYPE_N_VARIANTS; i++)
26     {
27       test_registration_t *r = tm->registrations[i];
28
29       if (r == 0 || test_march_supported (i) < 0)
30         continue;
31
32       fformat (stdout, "\nMultiarch Variant: %U\n", format_march_variant, i);
33       fformat (stdout,
34                "-------------------------------------------------------\n");
35       while (r)
36         {
37           clib_error_t *err;
38           if (tm->filter && strstr (r->name, (char *) tm->filter) == 0)
39             goto next;
40           err = (r->fn) (0);
41           fformat (stdout, "%-50s %s\n", r->name, err ? "FAIL" : "PASS");
42           if (err)
43             {
44               clib_error_report (err);
45               fformat (stdout, "\n");
46             }
47         next:
48           r = r->next;
49         }
50     }
51
52   fformat (stdout, "\n");
53   return 0;
54 }
55
56 #define TEST_PERF_MAX_EVENTS 7
57 typedef struct
58 {
59   char *name;
60   char *desc;
61   u64 config[TEST_PERF_MAX_EVENTS];
62   u32 type;
63   u8 n_events;
64   format_function_t *format_fn;
65 } test_perf_event_bundle_t;
66
67 static u8 *
68 format_test_perf_bundle_default (u8 *s, va_list *args)
69 {
70   test_perf_event_bundle_t __clib_unused *b =
71     va_arg (*args, test_perf_event_bundle_t *);
72   test_perf_t *tp = va_arg (*args, test_perf_t *);
73   u64 *data = va_arg (*args, u64 *);
74
75   if (data)
76     s = format (s, "%5.2f", (f64) data[1] / data[0]);
77   else
78     s = format (s, "%5s", "IPC");
79
80   if (data)
81     s = format (s, "%8.2f", (f64) data[0] / tp->n_ops);
82   else
83     s = format (s, "%8s", "Clks/Op");
84
85   if (data)
86     s = format (s, "%8.2f", (f64) data[1] / tp->n_ops);
87   else
88     s = format (s, "%8s", "Inst/Op");
89
90   if (data)
91     s = format (s, "%9.2f", (f64) data[2] / tp->n_ops);
92   else
93     s = format (s, "%9s", "Brnch/Op");
94
95   if (data)
96     s = format (s, "%10.2f", (f64) data[3] / tp->n_ops);
97   else
98     s = format (s, "%10s", "BrMiss/Op");
99   return s;
100 }
101
102 static u8 *
103 format_test_perf_bundle_core_power (u8 *s, va_list *args)
104 {
105   test_perf_event_bundle_t __clib_unused *b =
106     va_arg (*args, test_perf_event_bundle_t *);
107   test_perf_t __clib_unused *tp = va_arg (*args, test_perf_t *);
108   u64 *data = va_arg (*args, u64 *);
109
110   if (data)
111     s = format (s, "%7.1f %%", (f64) 100 * data[1] / data[0]);
112   else
113     s = format (s, "%9s", "Level 0");
114
115   if (data)
116     s = format (s, "%8.1f %%", (f64) 100 * data[2] / data[0]);
117   else
118     s = format (s, "%9s", "Level 1");
119
120   if (data)
121     s = format (s, "%7.1f %%", (f64) 100 * data[3] / data[0]);
122   else
123     s = format (s, "%9s", "Level 2");
124
125   return s;
126 }
127
128 test_perf_event_bundle_t perf_bundles[] = {
129   {
130     .name = "default",
131     .desc = "IPC, Clocks/Operatiom, Instr/Operation, Branch Total & Miss",
132     .type = PERF_TYPE_HARDWARE,
133     .config[0] = PERF_COUNT_HW_CPU_CYCLES,
134     .config[1] = PERF_COUNT_HW_INSTRUCTIONS,
135     .config[2] = PERF_COUNT_HW_BRANCH_INSTRUCTIONS,
136     .config[3] = PERF_COUNT_HW_BRANCH_MISSES,
137     .n_events = 4,
138     .format_fn = format_test_perf_bundle_default,
139   }
140 #ifdef __x86_64__
141 #define PERF_INTEL_CODE(event, umask) ((event) | (umask) << 8)
142   ,
143   {
144     .name = "core-power",
145     .desc =
146       "Core cycles where the core was running under specific turbo schedule.",
147     .type = PERF_TYPE_RAW,
148     .config[0] = PERF_INTEL_CODE (0x3c, 0x00),
149     .config[1] = PERF_INTEL_CODE (0x28, 0x07),
150     .config[2] = PERF_INTEL_CODE (0x28, 0x18),
151     .config[3] = PERF_INTEL_CODE (0x28, 0x20),
152     .config[4] = PERF_INTEL_CODE (0x28, 0x40),
153     .n_events = 5,
154     .format_fn = format_test_perf_bundle_core_power,
155   }
156 #endif
157 };
158
159 #ifdef __linux__
160 clib_error_t *
161 test_perf (test_main_t *tm)
162 {
163   clib_error_t *err = 0;
164   test_perf_event_bundle_t *b = 0;
165   int group_fd = -1, fds[TEST_PERF_MAX_EVENTS];
166   u64 count[TEST_PERF_MAX_EVENTS + 3] = {};
167   struct perf_event_attr pe = {
168     .size = sizeof (struct perf_event_attr),
169     .disabled = 1,
170     .exclude_kernel = 1,
171     .exclude_hv = 1,
172     .pinned = 1,
173     .exclusive = 1,
174     .read_format = (PERF_FORMAT_GROUP | PERF_FORMAT_TOTAL_TIME_ENABLED |
175                     PERF_FORMAT_TOTAL_TIME_RUNNING),
176   };
177
178   for (int i = 0; i < TEST_PERF_MAX_EVENTS; i++)
179     fds[i] = -1;
180
181   if (tm->bundle)
182     {
183       for (int i = 0; i < ARRAY_LEN (perf_bundles); i++)
184         if (strncmp ((char *) tm->bundle, perf_bundles[i].name,
185                      vec_len (tm->bundle)) == 0)
186           {
187             b = perf_bundles + i;
188             break;
189           }
190       if (b == 0)
191         return clib_error_return (0, "Unknown bundle '%s'", tm->bundle);
192     }
193   else
194     b = perf_bundles;
195
196   for (int i = 0; i < b->n_events; i++)
197     {
198       pe.config = b->config[i];
199       pe.type = b->type;
200       int fd = syscall (__NR_perf_event_open, &pe, /* pid */ 0, /* cpu */ -1,
201                         /* group_fd */ group_fd, /* flags */ 0);
202       if (fd < 0)
203         {
204           err = clib_error_return_unix (0, "perf_event_open");
205           goto done;
206         }
207
208       if (group_fd == -1)
209         {
210           group_fd = fd;
211           pe.pinned = 0;
212           pe.exclusive = 0;
213         }
214       fds[i] = fd;
215     }
216
217   for (int i = 0; i < CLIB_MARCH_TYPE_N_VARIANTS; i++)
218     {
219       test_registration_t *r = tm->registrations[i];
220
221       if (r == 0 || test_march_supported (i) < 0)
222         continue;
223
224       fformat (stdout, "\nMultiarch Variant: %U\n", format_march_variant, i);
225       fformat (stdout,
226                "-------------------------------------------------------\n");
227       while (r)
228         {
229           if (r->perf_tests)
230             {
231               test_perf_t *pt = r->perf_tests;
232               if (tm->filter && strstr (r->name, (char *) tm->filter) == 0)
233                 goto next;
234               fformat (stdout, "%-22s%-12s%U\n", r->name, "OpType",
235                        b->format_fn, b, pt, 0UL);
236               do
237                 {
238                   u32 read_size = (b->n_events + 3) * sizeof (u64);
239                   for (int i = 0; i < tm->repeat; i++)
240                     {
241                       test_perf_event_reset (group_fd);
242                       pt->fn (group_fd, pt);
243                       if ((read (group_fd, &count, read_size) != read_size))
244                         {
245                           err = clib_error_return_unix (0, "read");
246                           goto done;
247                         }
248                       if (count[1] != count[2])
249                         clib_warning (
250                           "perf counters were not running all the time."
251 #ifdef __x86_64__
252                           "\nConsider turning NMI watchdog off ('sysctl -w "
253                           "kernel.nmi_watchdog=0')."
254 #endif
255                         );
256                       fformat (stdout, "  %-20s%-12s%U\n", pt->name,
257                                pt->op_name ? pt->op_name : "", b->format_fn, b,
258                                pt, count + 3);
259                     }
260                 }
261               while ((++pt)->fn);
262             }
263         next:
264           r = r->next;
265         }
266     }
267
268 done:
269   for (int i = 0; i < TEST_PERF_MAX_EVENTS; i++)
270     if (fds[i] != -1)
271       close (fds[i]);
272   return err;
273 }
274 #endif
275
276 int
277 main (int argc, char *argv[])
278 {
279   test_main_t *tm = &test_main;
280   unformat_input_t _i = {}, *i = &_i;
281   clib_mem_init (0, 64ULL << 20);
282   clib_error_t *err;
283   int perf = 0;
284
285   /* defaults */
286   tm->repeat = 3;
287
288   unformat_init_command_line (i, argv);
289
290   while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
291     {
292       if (unformat (i, "perf"))
293         perf = 1;
294       else if (unformat (i, "filter %s", &tm->filter))
295         ;
296       else if (unformat (i, "bundle %s", &tm->bundle))
297         ;
298       else if (unformat (i, "repeat %d", &tm->repeat))
299         ;
300       else
301         {
302           clib_warning ("unknown input '%U'", format_unformat_error, i);
303           exit (1);
304         }
305     }
306
307   if (perf)
308     err = test_perf (tm);
309   else
310     err = test_funct (tm);
311
312   if (err)
313     {
314       clib_error_report (err);
315       fformat (stderr, "\n");
316       return 1;
317     }
318   return 0;
319 }
320
321 void *
322 test_mem_alloc (uword size)
323 {
324   void *rv;
325   size = round_pow2 (size, CLIB_CACHE_LINE_BYTES);
326   rv = clib_mem_alloc_aligned (size, CLIB_CACHE_LINE_BYTES);
327   clib_memset_u8 (rv, 0, size);
328   return rv;
329 }
330
331 void *
332 test_mem_alloc_and_fill_inc_u8 (uword size, u8 start, u8 mask)
333 {
334   u8 *rv;
335   mask = mask ? mask : 0xff;
336   size = round_pow2 (size, CLIB_CACHE_LINE_BYTES);
337   rv = clib_mem_alloc_aligned (size, CLIB_CACHE_LINE_BYTES);
338   for (uword i = 0; i < size; i++)
339     rv[i] = ((u8) i + start) & mask;
340   return rv;
341 }
342
343 void *
344 test_mem_alloc_and_splat (uword elt_size, uword n_elts, void *elt)
345 {
346   u8 *rv, *e;
347   uword data_size = elt_size * n_elts;
348   uword alloc_size = round_pow2 (data_size, CLIB_CACHE_LINE_BYTES);
349   e = rv = clib_mem_alloc_aligned (alloc_size, CLIB_CACHE_LINE_BYTES);
350   while (e - rv < data_size)
351     {
352       clib_memcpy_fast (e, elt, elt_size);
353       e += elt_size;
354     }
355
356   if (data_size < alloc_size)
357     clib_memset_u8 (e, 0, alloc_size - data_size);
358   return rv;
359 }
360
361 void
362 test_mem_free (void *p)
363 {
364   clib_mem_free (p);
365 }