144604e5fcded0c8be95c7276a5424d16bac003b
[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   u64 config[TEST_PERF_MAX_EVENTS];
60   u8 n_events;
61   format_function_t *format_fn;
62 } test_perf_event_bundle_t;
63
64 static u8 *
65 format_test_perf_bundle_default (u8 *s, va_list *args)
66 {
67   test_perf_event_bundle_t __clib_unused *b =
68     va_arg (*args, test_perf_event_bundle_t *);
69   test_perf_t *tp = va_arg (*args, test_perf_t *);
70   u64 *data = va_arg (*args, u64 *);
71
72   if (data)
73     s = format (s, "%5.2f", (f64) data[1] / data[0]);
74   else
75     s = format (s, "%5s", "IPC");
76
77   if (data)
78     s = format (s, "%8.2f", (f64) data[0] / tp->n_ops);
79   else
80     s = format (s, "%8s", "Clks/Op");
81
82   if (data)
83     s = format (s, "%8.2f", (f64) data[1] / tp->n_ops);
84   else
85     s = format (s, "%8s", "Inst/Op");
86
87   if (data)
88     s = format (s, "%9.2f", (f64) data[2] / tp->n_ops);
89   else
90     s = format (s, "%9s", "Brnch/Op");
91
92   if (data)
93     s = format (s, "%10.2f", (f64) data[3] / tp->n_ops);
94   else
95     s = format (s, "%10s", "BrMiss/Op");
96   return s;
97 }
98
99 test_perf_event_bundle_t perf_bundles[] = { {
100   .config[0] = PERF_COUNT_HW_CPU_CYCLES,
101   .config[1] = PERF_COUNT_HW_INSTRUCTIONS,
102   .config[2] = PERF_COUNT_HW_BRANCH_INSTRUCTIONS,
103   .config[3] = PERF_COUNT_HW_BRANCH_MISSES,
104   .n_events = 4,
105   .format_fn = format_test_perf_bundle_default,
106 } };
107
108 #ifdef __linux__
109 clib_error_t *
110 test_perf (test_main_t *tm)
111 {
112   clib_error_t *err = 0;
113   test_perf_event_bundle_t *b = perf_bundles;
114   int group_fd = -1, fds[TEST_PERF_MAX_EVENTS];
115   u64 count[TEST_PERF_MAX_EVENTS + 3] = {};
116   struct perf_event_attr pe = {
117     .size = sizeof (struct perf_event_attr),
118     .type = PERF_TYPE_HARDWARE,
119     .disabled = 1,
120     .exclude_kernel = 1,
121     .exclude_hv = 1,
122     .pinned = 1,
123     .exclusive = 1,
124     .read_format = (PERF_FORMAT_GROUP | PERF_FORMAT_TOTAL_TIME_ENABLED |
125                     PERF_FORMAT_TOTAL_TIME_RUNNING),
126   };
127
128   for (int i = 0; i < TEST_PERF_MAX_EVENTS; i++)
129     fds[i] = -1;
130
131   for (int i = 0; i < b->n_events; i++)
132     {
133       pe.config = b->config[i];
134       int fd = syscall (__NR_perf_event_open, &pe, /* pid */ 0, /* cpu */ -1,
135                         /* group_fd */ group_fd, /* flags */ 0);
136       if (fd < 0)
137         {
138           err = clib_error_return_unix (0, "perf_event_open");
139           goto done;
140         }
141
142       if (group_fd == -1)
143         {
144           group_fd = fd;
145           pe.pinned = 0;
146           pe.exclusive = 0;
147         }
148       fds[i] = fd;
149     }
150
151   for (int i = 0; i < CLIB_MARCH_TYPE_N_VARIANTS; i++)
152     {
153       test_registration_t *r = tm->registrations[i];
154
155       if (r == 0 || test_march_supported (i) < 0)
156         continue;
157
158       fformat (stdout, "\nMultiarch Variant: %U\n", format_march_variant, i);
159       fformat (stdout,
160                "-------------------------------------------------------\n");
161       while (r)
162         {
163           if (r->perf_tests)
164             {
165               test_perf_t *pt = r->perf_tests;
166               if (tm->filter && strstr (r->name, (char *) tm->filter) == 0)
167                 goto next;
168               fformat (stdout, "%-22s%-12s%U\n", r->name, "OpType",
169                        b->format_fn, b, pt, 0UL);
170               do
171                 {
172                   u32 read_size = (b->n_events + 3) * sizeof (u64);
173                   for (int i = 0; i < tm->repeat; i++)
174                     {
175                       test_perf_event_reset (group_fd);
176                       pt->fn (group_fd, pt);
177                       if ((read (group_fd, &count, read_size) != read_size))
178                         {
179                           err = clib_error_return_unix (0, "read");
180                           goto done;
181                         }
182                       if (count[1] != count[2])
183                         clib_warning (
184                           "perf counters were not running all the time."
185 #ifdef __x86_64__
186                           "\nConsider turning NMI watchdog off ('sysctl -w "
187                           "kernel.nmi_watchdog=0')."
188 #endif
189                         );
190                       fformat (stdout, "  %-20s%-12s%U\n", pt->name,
191                                pt->op_name ? pt->op_name : "", b->format_fn, b,
192                                pt, count + 3);
193                     }
194                 }
195               while ((++pt)->fn);
196             }
197         next:
198           r = r->next;
199         }
200     }
201
202 done:
203   for (int i = 0; i < TEST_PERF_MAX_EVENTS; i++)
204     if (fds[i] != -1)
205       close (fds[i]);
206   return err;
207 }
208 #endif
209
210 int
211 main (int argc, char *argv[])
212 {
213   test_main_t *tm = &test_main;
214   unformat_input_t _i = {}, *i = &_i;
215   clib_mem_init (0, 64ULL << 20);
216   clib_error_t *err;
217   int perf = 0;
218
219   /* defaults */
220   tm->repeat = 3;
221
222   unformat_init_command_line (i, argv);
223
224   while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
225     {
226       if (unformat (i, "perf"))
227         perf = 1;
228       else if (unformat (i, "filter %s", &tm->filter))
229         ;
230       else if (unformat (i, "repeat %d", &tm->repeat))
231         ;
232       else
233         {
234           clib_warning ("unknown input '%U'", format_unformat_error, i);
235           exit (1);
236         }
237     }
238
239   if (perf)
240     err = test_perf (tm);
241   else
242     err = test_funct (tm);
243
244   if (err)
245     {
246       clib_error_report (err);
247       fformat (stderr, "\n");
248       return 1;
249     }
250   return 0;
251 }
252
253 void *
254 test_mem_alloc (uword size)
255 {
256   void *rv;
257   size = round_pow2 (size, CLIB_CACHE_LINE_BYTES);
258   rv = clib_mem_alloc_aligned (size, CLIB_CACHE_LINE_BYTES);
259   clib_memset_u8 (rv, 0, size);
260   return rv;
261 }
262
263 void *
264 test_mem_alloc_and_fill_inc_u8 (uword size, u8 start, u8 mask)
265 {
266   u8 *rv;
267   mask = mask ? mask : 0xff;
268   size = round_pow2 (size, CLIB_CACHE_LINE_BYTES);
269   rv = clib_mem_alloc_aligned (size, CLIB_CACHE_LINE_BYTES);
270   for (uword i = 0; i < size; i++)
271     rv[i] = ((u8) i + start) & mask;
272   return rv;
273 }
274
275 void *
276 test_mem_alloc_and_splat (uword elt_size, uword n_elts, void *elt)
277 {
278   u8 *rv, *e;
279   uword data_size = elt_size * n_elts;
280   uword alloc_size = round_pow2 (data_size, CLIB_CACHE_LINE_BYTES);
281   e = rv = clib_mem_alloc_aligned (alloc_size, CLIB_CACHE_LINE_BYTES);
282   while (e - rv < data_size)
283     {
284       clib_memcpy_fast (e, elt, elt_size);
285       e += elt_size;
286     }
287
288   if (data_size < alloc_size)
289     clib_memset_u8 (e, 0, alloc_size - data_size);
290   return rv;
291 }
292
293 void
294 test_mem_free (void *p)
295 {
296   clib_mem_free (p);
297 }