Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(C) 2020 Marvell International Ltd.
3 : : */
4 : :
5 : : #include <stdlib.h>
6 : : #include <fnmatch.h>
7 : : #include <pthread.h>
8 : : #include <sys/queue.h>
9 : : #include <regex.h>
10 : :
11 : : #include <rte_common.h>
12 : : #include <rte_errno.h>
13 : : #include <rte_lcore.h>
14 : : #include <rte_per_lcore.h>
15 : : #include <rte_string_fns.h>
16 : :
17 : : #include <eal_export.h>
18 : : #include "eal_trace.h"
19 : :
20 : : RTE_EXPORT_EXPERIMENTAL_SYMBOL(per_lcore_trace_point_sz, 20.05)
21 : : RTE_DEFINE_PER_LCORE(volatile int, trace_point_sz);
22 : : RTE_EXPORT_EXPERIMENTAL_SYMBOL(per_lcore_trace_mem, 20.05)
23 : : RTE_DEFINE_PER_LCORE(void *, trace_mem);
24 : : static RTE_DEFINE_PER_LCORE(char *, ctf_field);
25 : :
26 : : static struct trace_point_head tp_list = STAILQ_HEAD_INITIALIZER(tp_list);
27 : : static struct trace trace = { .args = STAILQ_HEAD_INITIALIZER(trace.args), };
28 : :
29 : : struct trace *
30 : 2950 : trace_obj_get(void)
31 : : {
32 : 2950 : return &trace;
33 : : }
34 : :
35 : : struct trace_point_head *
36 : 106467 : trace_list_head_get(void)
37 : : {
38 : 106467 : return &tp_list;
39 : : }
40 : :
41 : : int
42 : 199 : eal_trace_init(void)
43 : : {
44 : : struct trace_arg *arg;
45 : :
46 : : /* Trace memory should start with 8B aligned for natural alignment */
47 : : RTE_BUILD_BUG_ON((offsetof(struct __rte_trace_header, mem) % 8) != 0);
48 : :
49 : : /* One of the trace point registration failed */
50 [ - + ]: 199 : if (trace.register_errno) {
51 : 0 : rte_errno = trace.register_errno;
52 : 0 : goto fail;
53 : : }
54 : :
55 : : rte_spinlock_init(&trace.lock);
56 : :
57 : : /* Is duplicate trace name registered */
58 [ - + ]: 199 : if (trace_has_duplicate_entry())
59 : 0 : goto fail;
60 : :
61 : : /* Generate UUID ver 4 with total size of events and number of
62 : : * events
63 : : */
64 : 199 : trace_uuid_generate();
65 : :
66 : : /* Apply buffer size configuration for trace output */
67 : 199 : trace_bufsz_args_apply();
68 : :
69 : : /* Generate CTF TDSL metadata */
70 [ - + ]: 199 : if (trace_metadata_create() < 0)
71 : 0 : goto fail;
72 : :
73 : : /* Save current epoch timestamp for future use */
74 [ - + ]: 199 : if (trace_epoch_time_save() < 0)
75 : 0 : goto free_meta;
76 : :
77 : : /* Apply global configurations */
78 [ + + ]: 200 : STAILQ_FOREACH(arg, &trace.args, next)
79 : 1 : trace_args_apply(arg->val);
80 : :
81 : 199 : rte_trace_mode_set(trace.mode);
82 : :
83 : 199 : return 0;
84 : :
85 : : free_meta:
86 : 0 : trace_metadata_destroy();
87 : 0 : fail:
88 : 0 : trace_err("failed to initialize trace [%s]", rte_strerror(rte_errno));
89 : 0 : return -rte_errno;
90 : : }
91 : :
92 : : void
93 : 252 : eal_trace_fini(void)
94 : : {
95 : 252 : trace_mem_free();
96 : 252 : trace_metadata_destroy();
97 : 252 : eal_trace_args_free();
98 : 252 : }
99 : :
100 : : RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_trace_is_enabled, 20.05)
101 : : bool
102 : 805 : rte_trace_is_enabled(void)
103 : : {
104 : 805 : return rte_atomic_load_explicit(&trace.status, rte_memory_order_acquire) != 0;
105 : : }
106 : :
107 : : static void
108 : 243124 : trace_mode_set(rte_trace_point_t *t, enum rte_trace_mode mode)
109 : : {
110 [ + + ]: 243124 : if (mode == RTE_TRACE_MODE_OVERWRITE)
111 : 242060 : rte_atomic_fetch_and_explicit(t, ~__RTE_TRACE_FIELD_ENABLE_DISCARD,
112 : : rte_memory_order_release);
113 : : else
114 : 1064 : rte_atomic_fetch_or_explicit(t, __RTE_TRACE_FIELD_ENABLE_DISCARD,
115 : : rte_memory_order_release);
116 : 243124 : }
117 : :
118 : : RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_trace_mode_set, 20.05)
119 : : void
120 : 205 : rte_trace_mode_set(enum rte_trace_mode mode)
121 : : {
122 : : struct trace_point *tp;
123 : :
124 [ + + ]: 109265 : STAILQ_FOREACH(tp, &tp_list, next)
125 : 109060 : trace_mode_set(tp->handle, mode);
126 : :
127 : 205 : trace.mode = mode;
128 : 205 : }
129 : :
130 : : RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_trace_mode_get, 20.05)
131 : : enum
132 : 8 : rte_trace_mode rte_trace_mode_get(void)
133 : : {
134 : 8 : return trace.mode;
135 : : }
136 : :
137 : : static bool
138 : : trace_point_is_invalid(rte_trace_point_t *t)
139 : : {
140 [ - + - + : 1628 : return (t == NULL) || (trace_id_get(t) >= trace.nb_trace_points);
- + ]
141 : : }
142 : :
143 : : RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_trace_point_is_enabled, 20.05)
144 : : bool
145 [ + - ]: 1076 : rte_trace_point_is_enabled(rte_trace_point_t *t)
146 : : {
147 : : uint64_t val;
148 : :
149 [ + - ]: 1076 : if (trace_point_is_invalid(t))
150 : : return false;
151 : :
152 : 1076 : val = rte_atomic_load_explicit(t, rte_memory_order_acquire);
153 : 1076 : return (val & __RTE_TRACE_FIELD_ENABLE_MASK) != 0;
154 : : }
155 : :
156 : : RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_trace_point_enable, 20.05)
157 : : int
158 [ + - ]: 542 : rte_trace_point_enable(rte_trace_point_t *t)
159 : : {
160 : : uint64_t prev;
161 : :
162 [ + - ]: 542 : if (trace_point_is_invalid(t))
163 : : return -ERANGE;
164 : :
165 : 542 : prev = rte_atomic_fetch_or_explicit(t, __RTE_TRACE_FIELD_ENABLE_MASK,
166 : : rte_memory_order_release);
167 [ + - ]: 542 : if ((prev & __RTE_TRACE_FIELD_ENABLE_MASK) == 0)
168 : 542 : rte_atomic_fetch_add_explicit(&trace.status, 1, rte_memory_order_release);
169 : : return 0;
170 : : }
171 : :
172 : : RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_trace_point_disable, 20.05)
173 : : int
174 [ + - ]: 10 : rte_trace_point_disable(rte_trace_point_t *t)
175 : : {
176 : : uint64_t prev;
177 : :
178 [ + - ]: 10 : if (trace_point_is_invalid(t))
179 : : return -ERANGE;
180 : :
181 : 10 : prev = rte_atomic_fetch_and_explicit(t, ~__RTE_TRACE_FIELD_ENABLE_MASK,
182 : : rte_memory_order_release);
183 [ + + ]: 10 : if ((prev & __RTE_TRACE_FIELD_ENABLE_MASK) != 0)
184 : 8 : rte_atomic_fetch_sub_explicit(&trace.status, 1, rte_memory_order_release);
185 : : return 0;
186 : : }
187 : :
188 : : RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_trace_pattern, 20.05)
189 : : int
190 : 6 : rte_trace_pattern(const char *pattern, bool enable)
191 : : {
192 : : struct trace_point *tp;
193 : : int rc = 0, found = 0;
194 : :
195 [ + + ]: 3198 : STAILQ_FOREACH(tp, &tp_list, next) {
196 [ + + ]: 3192 : if (fnmatch(pattern, tp->name, 0) != 0)
197 : 3184 : continue;
198 : :
199 [ + + ]: 8 : if (enable)
200 : 4 : rc = rte_trace_point_enable(tp->handle);
201 : : else
202 : 4 : rc = rte_trace_point_disable(tp->handle);
203 [ + - ]: 8 : if (rc < 0) {
204 : : found = 0;
205 : : break;
206 : : }
207 : : found = 1;
208 : : }
209 : :
210 : 6 : return rc | found;
211 : : }
212 : :
213 : : RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_trace_regexp, 20.05)
214 : : int
215 : 7 : rte_trace_regexp(const char *regex, bool enable)
216 : : {
217 : : struct trace_point *tp;
218 : : int rc = 0, found = 0;
219 : : regex_t r;
220 : :
221 [ + - ]: 7 : if (regcomp(&r, regex, 0) != 0)
222 : : return -EINVAL;
223 : :
224 [ + + ]: 3731 : STAILQ_FOREACH(tp, &tp_list, next) {
225 [ + + ]: 3724 : if (regexec(&r, tp->name, 0, NULL, 0) != 0)
226 : 3184 : continue;
227 : :
228 [ + + ]: 540 : if (enable)
229 : 536 : rc = rte_trace_point_enable(tp->handle);
230 : : else
231 : 4 : rc = rte_trace_point_disable(tp->handle);
232 [ + - ]: 540 : if (rc < 0) {
233 : : found = 0;
234 : : break;
235 : : }
236 : : found = 1;
237 : : }
238 : 7 : regfree(&r);
239 : :
240 : 7 : return rc | found;
241 : : }
242 : :
243 : : RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_trace_point_lookup, 20.05)
244 : : rte_trace_point_t *
245 : 4 : rte_trace_point_lookup(const char *name)
246 : : {
247 : : struct trace_point *tp;
248 : :
249 [ + - ]: 4 : if (name == NULL)
250 : : return NULL;
251 : :
252 [ + + ]: 1068 : STAILQ_FOREACH(tp, &tp_list, next)
253 [ + + ]: 1066 : if (strcmp(tp->name, name) == 0)
254 : 2 : return tp->handle;
255 : :
256 : : return NULL;
257 : : }
258 : :
259 : : static void
260 : 1064 : trace_point_dump(FILE *f, struct trace_point *tp)
261 : : {
262 : 1064 : rte_trace_point_t *handle = tp->handle;
263 : :
264 [ + + ]: 1064 : fprintf(f, "\tid %d, %s, size is %d, %s\n",
265 : : trace_id_get(handle), tp->name,
266 : 1064 : (uint16_t)(*handle & __RTE_TRACE_FIELD_SIZE_MASK),
267 : 1064 : rte_trace_point_is_enabled(handle) ? "enabled" : "disabled");
268 : 1064 : }
269 : :
270 : : static void
271 : 2 : trace_lcore_mem_dump(FILE *f)
272 : : {
273 : 2 : struct trace *trace = trace_obj_get();
274 : : struct __rte_trace_header *header;
275 : : uint32_t count;
276 : :
277 : 2 : rte_spinlock_lock(&trace->lock);
278 [ - + ]: 2 : if (trace->nb_trace_mem_list == 0)
279 : 0 : goto out;
280 : : fprintf(f, "nb_trace_mem_list = %d\n", trace->nb_trace_mem_list);
281 : : fprintf(f, "\nTrace mem info\n--------------\n");
282 [ + + ]: 7 : for (count = 0; count < trace->nb_trace_mem_list; count++) {
283 : 5 : header = trace->lcore_meta[count].mem;
284 : 5 : fprintf(f, "\tid %d, mem=%p, area=%s, lcore_id=%d, name=%s\n",
285 : : count, header,
286 : : trace_area_to_string(trace->lcore_meta[count].area),
287 : : header->stream_header.lcore_id,
288 : 5 : header->stream_header.thread_name);
289 : : }
290 : 2 : out:
291 : : rte_spinlock_unlock(&trace->lock);
292 : 2 : }
293 : :
294 : : RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_trace_dump, 20.05)
295 : : void
296 : 2 : rte_trace_dump(FILE *f)
297 : : {
298 : 2 : struct trace_point_head *tp_list = trace_list_head_get();
299 : 2 : struct trace *trace = trace_obj_get();
300 : : struct trace_point *tp;
301 : :
302 : : fprintf(f, "\nGlobal info\n-----------\n");
303 [ - + ]: 2 : fprintf(f, "status = %s\n",
304 : 2 : rte_trace_is_enabled() ? "enabled" : "disabled");
305 : 2 : fprintf(f, "mode = %s\n",
306 : : trace_mode_to_string(rte_trace_mode_get()));
307 : 2 : fprintf(f, "dir = %s\n", trace->dir);
308 : 2 : fprintf(f, "buffer len = %d\n", trace->buff_len);
309 : 2 : fprintf(f, "number of trace points = %d\n", trace->nb_trace_points);
310 : :
311 : 2 : trace_lcore_mem_dump(f);
312 : : fprintf(f, "\nTrace point info\n----------------\n");
313 [ + + ]: 1066 : STAILQ_FOREACH(tp, tp_list, next)
314 : 1064 : trace_point_dump(f, tp);
315 : 2 : }
316 : :
317 : : static void
318 : : thread_get_name(rte_thread_t id, char *name, size_t len)
319 : : {
320 : : #if defined(RTE_EXEC_ENV_LINUX) && defined(__GLIBC__) && defined(__GLIBC_PREREQ)
321 : : #if __GLIBC_PREREQ(2, 12)
322 : 5 : pthread_getname_np((pthread_t)id.opaque_id, name, len);
323 : : #endif
324 : : #endif
325 : : RTE_SET_USED(id);
326 : : RTE_SET_USED(name);
327 : : RTE_SET_USED(len);
328 : : }
329 : :
330 : : RTE_EXPORT_EXPERIMENTAL_SYMBOL(__rte_trace_mem_per_thread_alloc, 20.05)
331 : : void
332 : 803 : __rte_trace_mem_per_thread_alloc(void)
333 : : {
334 : 803 : struct trace *trace = trace_obj_get();
335 : : struct __rte_trace_header *header;
336 : : uint32_t count;
337 : :
338 [ + + ]: 803 : if (!rte_trace_is_enabled())
339 : : return;
340 : :
341 [ + - ]: 5 : if (RTE_PER_LCORE(trace_mem))
342 : : return;
343 : :
344 : 5 : rte_spinlock_lock(&trace->lock);
345 : :
346 : 5 : count = trace->nb_trace_mem_list;
347 : :
348 : : /* Allocate room for storing the thread trace mem meta */
349 : 5 : trace->lcore_meta = realloc(trace->lcore_meta,
350 : 5 : sizeof(trace->lcore_meta[0]) * (count + 1));
351 : :
352 : : /* Provide dummy space for fast path to consume */
353 [ - + ]: 5 : if (trace->lcore_meta == NULL) {
354 : 0 : trace_crit("trace mem meta memory realloc failed");
355 : : header = NULL;
356 : 0 : goto fail;
357 : : }
358 : :
359 : : /* First attempt from huge page */
360 : 5 : header = eal_malloc_no_trace(NULL, trace_mem_sz(trace->buff_len), 8);
361 [ + + ]: 5 : if (header) {
362 : 3 : trace->lcore_meta[count].area = TRACE_AREA_HUGEPAGE;
363 : 3 : goto found;
364 : : }
365 : :
366 : : /* Second attempt from heap */
367 [ - + ]: 2 : header = malloc(trace_mem_sz(trace->buff_len));
368 [ - + ]: 2 : if (header == NULL) {
369 : 0 : trace_crit("trace mem malloc attempt failed");
370 : : header = NULL;
371 : 0 : goto fail;
372 : :
373 : : }
374 : :
375 : : /* Second attempt from heap is success */
376 : 2 : trace->lcore_meta[count].area = TRACE_AREA_HEAP;
377 : :
378 : : /* Initialize the trace header */
379 : 5 : found:
380 : 5 : header->offset = 0;
381 : 5 : header->len = trace->buff_len;
382 : 5 : header->stream_header.magic = TRACE_CTF_MAGIC;
383 : 5 : rte_uuid_copy(header->stream_header.uuid, trace->uuid);
384 : 5 : header->stream_header.lcore_id = rte_lcore_id();
385 : :
386 : : /* Store the thread name */
387 : 5 : char *name = header->stream_header.thread_name;
388 : : memset(name, 0, __RTE_TRACE_EMIT_STRING_LEN_MAX);
389 : 5 : thread_get_name(rte_thread_self(), name,
390 : : __RTE_TRACE_EMIT_STRING_LEN_MAX);
391 : :
392 : 5 : trace->lcore_meta[count].mem = header;
393 : 5 : trace->nb_trace_mem_list++;
394 : 5 : fail:
395 : 5 : RTE_PER_LCORE(trace_mem) = header;
396 : : rte_spinlock_unlock(&trace->lock);
397 : : }
398 : :
399 : : static void
400 : 5 : trace_mem_per_thread_free_unlocked(struct thread_mem_meta *meta)
401 : : {
402 [ + + ]: 5 : if (meta->area == TRACE_AREA_HUGEPAGE)
403 : 3 : eal_free_no_trace(meta->mem);
404 [ + - ]: 2 : else if (meta->area == TRACE_AREA_HEAP)
405 : 2 : free(meta->mem);
406 : 5 : }
407 : :
408 : : void
409 : 129 : trace_mem_per_thread_free(void)
410 : : {
411 : 129 : struct trace *trace = trace_obj_get();
412 : : struct __rte_trace_header *header;
413 : : uint32_t count;
414 : :
415 : 129 : header = RTE_PER_LCORE(trace_mem);
416 [ - + ]: 129 : if (header == NULL)
417 : : return;
418 : :
419 : 0 : rte_spinlock_lock(&trace->lock);
420 [ # # ]: 0 : for (count = 0; count < trace->nb_trace_mem_list; count++) {
421 [ # # ]: 0 : if (trace->lcore_meta[count].mem == header)
422 : : break;
423 : : }
424 [ # # ]: 0 : if (count != trace->nb_trace_mem_list) {
425 : 0 : struct thread_mem_meta *meta = &trace->lcore_meta[count];
426 : :
427 : 0 : trace_mem_per_thread_free_unlocked(meta);
428 [ # # ]: 0 : if (count != trace->nb_trace_mem_list - 1) {
429 : 0 : memmove(meta, meta + 1,
430 : : sizeof(*meta) *
431 : 0 : (trace->nb_trace_mem_list - count - 1));
432 : : }
433 : 0 : trace->nb_trace_mem_list--;
434 : : }
435 : : rte_spinlock_unlock(&trace->lock);
436 : : }
437 : :
438 : : void
439 : 252 : trace_mem_free(void)
440 : : {
441 : 252 : struct trace *trace = trace_obj_get();
442 : : uint32_t count;
443 : :
444 : 252 : rte_spinlock_lock(&trace->lock);
445 [ + + ]: 257 : for (count = 0; count < trace->nb_trace_mem_list; count++) {
446 : 5 : trace_mem_per_thread_free_unlocked(&trace->lcore_meta[count]);
447 : : }
448 : 252 : trace->nb_trace_mem_list = 0;
449 : : rte_spinlock_unlock(&trace->lock);
450 : 252 : }
451 : :
452 : : RTE_EXPORT_EXPERIMENTAL_SYMBOL(__rte_trace_point_emit_field, 20.05)
453 : : void
454 : 500220 : __rte_trace_point_emit_field(size_t sz, const char *in, const char *datatype)
455 : : {
456 : : char *field;
457 : : char *fixup;
458 : : int rc;
459 : :
460 : 500220 : fixup = trace_metadata_fixup_field(in);
461 [ + + ]: 500220 : if (fixup != NULL)
462 : : in = fixup;
463 : 500220 : rc = asprintf(&field, "%s %s %s;\n",
464 [ + + ]: 500220 : RTE_PER_LCORE(ctf_field) != NULL ?
465 : : RTE_PER_LCORE(ctf_field) : "",
466 : : datatype, in);
467 : 500220 : free(RTE_PER_LCORE(ctf_field));
468 : 500220 : free(fixup);
469 [ - + ]: 500220 : if (rc == -1) {
470 : 0 : RTE_PER_LCORE(trace_point_sz) = 0;
471 : 0 : RTE_PER_LCORE(ctf_field) = NULL;
472 : 0 : trace_crit("could not allocate CTF field");
473 : 0 : return;
474 : : }
475 : 500220 : RTE_PER_LCORE(trace_point_sz) += sz;
476 : 500220 : RTE_PER_LCORE(ctf_field) = field;
477 : : }
478 : :
479 : : RTE_EXPORT_EXPERIMENTAL_SYMBOL(__rte_trace_point_register, 20.05)
480 : : int
481 : 134064 : __rte_trace_point_register(rte_trace_point_t *handle, const char *name,
482 : : void (*register_fn)(void))
483 : : {
484 : : struct trace_point *tp;
485 : : uint16_t sz;
486 : :
487 : : /* Sanity checks of arguments */
488 [ + - - + ]: 134064 : if (name == NULL || register_fn == NULL || handle == NULL) {
489 : 0 : trace_err("invalid arguments");
490 : 0 : rte_errno = EINVAL;
491 : 0 : goto fail;
492 : : }
493 : :
494 : : /* Check the size of the trace point object */
495 : 134064 : RTE_PER_LCORE(trace_point_sz) = 0;
496 : 134064 : register_fn();
497 [ - + ]: 134064 : if (RTE_PER_LCORE(trace_point_sz) == 0) {
498 : 0 : trace_err("missing rte_trace_emit_header() in register fn");
499 : 0 : rte_errno = EBADF;
500 : 0 : goto fail;
501 : : }
502 : :
503 : : /* Is size overflowed */
504 [ - + ]: 134064 : if (RTE_PER_LCORE(trace_point_sz) > UINT16_MAX) {
505 : 0 : trace_err("trace point size overflowed");
506 : 0 : rte_errno = ENOSPC;
507 : 0 : goto fail;
508 : : }
509 : :
510 : : /* Are we running out of space to store trace points? */
511 [ - + ]: 134064 : if (trace.nb_trace_points > UINT16_MAX) {
512 : 0 : trace_err("trace point exceeds the max count");
513 : 0 : rte_errno = ENOSPC;
514 : 0 : goto fail;
515 : : }
516 : :
517 : : /* Get the size of the trace point */
518 : 134064 : sz = RTE_PER_LCORE(trace_point_sz);
519 : 134064 : tp = calloc(1, sizeof(struct trace_point));
520 [ - + ]: 134064 : if (tp == NULL) {
521 : 0 : trace_err("fail to allocate trace point memory");
522 : 0 : rte_errno = ENOMEM;
523 : 0 : goto fail;
524 : : }
525 : :
526 : : /* Initialize the trace point */
527 : 134064 : tp->name = name;
528 : :
529 : : /* Copy the accumulated fields description and clear it for the next
530 : : * trace point.
531 : : */
532 : 134064 : tp->ctf_field = RTE_PER_LCORE(ctf_field);
533 : 134064 : RTE_PER_LCORE(ctf_field) = NULL;
534 : :
535 : : /* Form the trace handle */
536 : 134064 : *handle = sz;
537 : 134064 : *handle |= trace.nb_trace_points << __RTE_TRACE_FIELD_ID_SHIFT;
538 : 134064 : trace_mode_set(handle, trace.mode);
539 : :
540 : 134064 : trace.nb_trace_points++;
541 : 134064 : tp->handle = handle;
542 : :
543 : : /* Add the trace point at tail */
544 : 134064 : STAILQ_INSERT_TAIL(&tp_list, tp, next);
545 : : rte_atomic_thread_fence(rte_memory_order_release);
546 : :
547 : : /* All Good !!! */
548 : 134064 : return 0;
549 : :
550 : 0 : fail:
551 [ # # ]: 0 : if (trace.register_errno == 0)
552 : 0 : trace.register_errno = rte_errno;
553 : :
554 : 0 : return -rte_errno;
555 : : }
|