1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(C) 2020 Marvell International Ltd.
3 */
4
5 #include <fnmatch.h>
6 #include <pwd.h>
7 #include <sys/stat.h>
8 #include <time.h>
9
10 #include <rte_common.h>
11 #include <rte_errno.h>
12 #include <rte_string_fns.h>
13
14 #include "eal_filesystem.h"
15 #include "eal_trace.h"
16
17 const char *
trace_mode_to_string(enum rte_trace_mode mode)18 trace_mode_to_string(enum rte_trace_mode mode)
19 {
20 switch (mode) {
21 case RTE_TRACE_MODE_OVERWRITE: return "overwrite";
22 case RTE_TRACE_MODE_DISCARD: return "discard";
23 default: return "unknown";
24 }
25 }
26
27 const char *
trace_area_to_string(enum trace_area_e area)28 trace_area_to_string(enum trace_area_e area)
29 {
30 switch (area) {
31 case TRACE_AREA_HEAP: return "heap";
32 case TRACE_AREA_HUGEPAGE: return "hugepage";
33 default: return "unknown";
34 }
35 }
36
37 static bool
trace_entry_compare(const char * name)38 trace_entry_compare(const char *name)
39 {
40 struct trace_point_head *tp_list = trace_list_head_get();
41 struct trace_point *tp;
42 int count = 0;
43
44 STAILQ_FOREACH(tp, tp_list, next) {
45 if (strncmp(tp->name, name, TRACE_POINT_NAME_SIZE) == 0)
46 count++;
47 if (count > 1) {
48 trace_err("found duplicate entry %s", name);
49 rte_errno = EEXIST;
50 return true;
51 }
52 }
53 return false;
54 }
55
56 bool
trace_has_duplicate_entry(void)57 trace_has_duplicate_entry(void)
58 {
59 struct trace_point_head *tp_list = trace_list_head_get();
60 struct trace_point *tp;
61
62 /* Is duplicate trace name registered */
63 STAILQ_FOREACH(tp, tp_list, next)
64 if (trace_entry_compare(tp->name))
65 return true;
66
67 return false;
68 }
69
70 void
trace_uuid_generate(void)71 trace_uuid_generate(void)
72 {
73 struct trace_point_head *tp_list = trace_list_head_get();
74 struct trace *trace = trace_obj_get();
75 struct trace_point *tp;
76 uint64_t sz_total = 0;
77
78 /* Go over the registered trace points to get total size of events */
79 STAILQ_FOREACH(tp, tp_list, next) {
80 const uint16_t sz = *tp->handle & __RTE_TRACE_FIELD_SIZE_MASK;
81 sz_total += sz;
82 }
83
84 rte_uuid_t uuid = RTE_UUID_INIT(sz_total, trace->nb_trace_points,
85 0x4370, 0x8f50, 0x222ddd514176ULL);
86 rte_uuid_copy(trace->uuid, uuid);
87 }
88
89 static int
trace_session_name_generate(char * trace_dir)90 trace_session_name_generate(char *trace_dir)
91 {
92 struct tm *tm_result;
93 time_t tm;
94 int rc;
95
96 tm = time(NULL);
97 if ((int)tm == -1)
98 goto fail;
99
100 tm_result = localtime(&tm);
101 if (tm_result == NULL)
102 goto fail;
103
104 rc = rte_strscpy(trace_dir, eal_get_hugefile_prefix(),
105 TRACE_PREFIX_LEN);
106 if (rc == -E2BIG)
107 rc = TRACE_PREFIX_LEN;
108 trace_dir[rc++] = '-';
109
110 rc = strftime(trace_dir + rc, TRACE_DIR_STR_LEN - rc,
111 "%Y-%m-%d-%p-%I-%M-%S", tm_result);
112 if (rc == 0)
113 goto fail;
114
115 return rc;
116 fail:
117 rte_errno = errno;
118 return -rte_errno;
119 }
120
121 static int
trace_dir_update(const char * str)122 trace_dir_update(const char *str)
123 {
124 struct trace *trace = trace_obj_get();
125 int rc, remaining;
126
127 remaining = sizeof(trace->dir) - trace->dir_offset;
128 rc = rte_strscpy(&trace->dir[0] + trace->dir_offset, str, remaining);
129 if (rc < 0)
130 goto fail;
131
132 trace->dir_offset += rc;
133 fail:
134 return rc;
135 }
136
137 int
eal_trace_args_save(const char * val)138 eal_trace_args_save(const char *val)
139 {
140 struct trace *trace = trace_obj_get();
141 struct trace_arg *arg = malloc(sizeof(*arg));
142
143 if (arg == NULL) {
144 trace_err("failed to allocate memory for %s", val);
145 return -ENOMEM;
146 }
147
148 arg->val = strdup(val);
149 if (arg->val == NULL) {
150 trace_err("failed to allocate memory for %s", val);
151 free(arg);
152 return -ENOMEM;
153 }
154
155 STAILQ_INSERT_TAIL(&trace->args, arg, next);
156 return 0;
157 }
158
159 void
eal_trace_args_free(void)160 eal_trace_args_free(void)
161 {
162 struct trace *trace = trace_obj_get();
163 struct trace_arg *arg;
164
165 while (!STAILQ_EMPTY(&trace->args)) {
166 arg = STAILQ_FIRST(&trace->args);
167 STAILQ_REMOVE_HEAD(&trace->args, next);
168 free(arg->val);
169 free(arg);
170 }
171 }
172
173 int
trace_args_apply(const char * arg)174 trace_args_apply(const char *arg)
175 {
176 if (rte_trace_regexp(arg, true) < 0) {
177 trace_err("cannot enable trace for %s", arg);
178 return -1;
179 }
180
181 return 0;
182 }
183
184 int
eal_trace_bufsz_args_save(char const * val)185 eal_trace_bufsz_args_save(char const *val)
186 {
187 struct trace *trace = trace_obj_get();
188 uint64_t bufsz;
189
190 bufsz = rte_str_to_size(val);
191 if (bufsz == 0) {
192 trace_err("buffer size cannot be zero");
193 return -EINVAL;
194 }
195
196 trace->buff_len = bufsz;
197 return 0;
198 }
199
200 void
trace_bufsz_args_apply(void)201 trace_bufsz_args_apply(void)
202 {
203 struct trace *trace = trace_obj_get();
204
205 if (trace->buff_len == 0)
206 trace->buff_len = 1024 * 1024; /* 1MB */
207 }
208
209 int
eal_trace_mode_args_save(const char * val)210 eal_trace_mode_args_save(const char *val)
211 {
212 struct trace *trace = trace_obj_get();
213 size_t len = strlen(val);
214 unsigned long tmp;
215 char *pattern;
216
217 if (len == 0) {
218 trace_err("value is not provided with option");
219 return -EINVAL;
220 }
221
222 pattern = (char *)calloc(1, len + 2);
223 if (pattern == NULL) {
224 trace_err("fail to allocate memory");
225 return -ENOMEM;
226 }
227
228 sprintf(pattern, "%s*", val);
229
230 if (fnmatch(pattern, "overwrite", 0) == 0)
231 tmp = RTE_TRACE_MODE_OVERWRITE;
232 else if (fnmatch(pattern, "discard", 0) == 0)
233 tmp = RTE_TRACE_MODE_DISCARD;
234 else {
235 free(pattern);
236 return -EINVAL;
237 }
238
239 trace->mode = tmp;
240 free(pattern);
241 return 0;
242 }
243
244 int
eal_trace_dir_args_save(char const * val)245 eal_trace_dir_args_save(char const *val)
246 {
247 struct trace *trace = trace_obj_get();
248 char *dir_path;
249 int rc;
250
251 if (strlen(val) >= sizeof(trace->dir) - 1) {
252 trace_err("input string is too big");
253 return -ENAMETOOLONG;
254 }
255
256 if (asprintf(&dir_path, "%s/", val) == -1) {
257 trace_err("failed to copy directory: %s", strerror(errno));
258 return -ENOMEM;
259 }
260
261 rc = trace_dir_update(dir_path);
262
263 free(dir_path);
264 return rc;
265 }
266
267 int
trace_epoch_time_save(void)268 trace_epoch_time_save(void)
269 {
270 struct trace *trace = trace_obj_get();
271 struct timespec epoch = { 0, 0 };
272 uint64_t avg, start, end;
273
274 start = rte_get_tsc_cycles();
275 if (clock_gettime(CLOCK_REALTIME, &epoch) < 0) {
276 trace_err("failed to get the epoch time");
277 return -1;
278 }
279 end = rte_get_tsc_cycles();
280 avg = (start + end) >> 1;
281
282 trace->epoch_sec = (uint64_t) epoch.tv_sec;
283 trace->epoch_nsec = (uint64_t) epoch.tv_nsec;
284 trace->uptime_ticks = avg;
285
286 return 0;
287 }
288
289 static int
trace_dir_default_path_get(char * dir_path)290 trace_dir_default_path_get(char *dir_path)
291 {
292 struct trace *trace = trace_obj_get();
293 uint32_t size = sizeof(trace->dir);
294 struct passwd *pwd;
295 char *home_dir;
296
297 /* First check for shell environment variable */
298 home_dir = getenv("HOME");
299 if (home_dir == NULL) {
300 /* Fallback to password file entry */
301 pwd = getpwuid(getuid());
302 if (pwd == NULL)
303 return -EINVAL;
304
305 home_dir = pwd->pw_dir;
306 }
307
308 /* Append dpdk-traces to directory */
309 if (snprintf(dir_path, size, "%s/dpdk-traces/", home_dir) < 0)
310 return -ENAMETOOLONG;
311
312 return 0;
313 }
314
315 int
trace_mkdir(void)316 trace_mkdir(void)
317 {
318 struct trace *trace = trace_obj_get();
319 char session[TRACE_DIR_STR_LEN];
320 char *dir_path;
321 int rc;
322
323 if (!trace->dir_offset) {
324 dir_path = calloc(1, sizeof(trace->dir));
325 if (dir_path == NULL) {
326 trace_err("fail to allocate memory");
327 return -ENOMEM;
328 }
329
330 rc = trace_dir_default_path_get(dir_path);
331 if (rc < 0) {
332 trace_err("fail to get default path");
333 free(dir_path);
334 return rc;
335 }
336
337 rc = trace_dir_update(dir_path);
338 free(dir_path);
339 if (rc < 0)
340 return rc;
341 }
342
343 /* Create the path if it t exist, no "mkdir -p" available here */
344 rc = mkdir(trace->dir, 0700);
345 if (rc < 0 && errno != EEXIST) {
346 trace_err("mkdir %s failed [%s]", trace->dir, strerror(errno));
347 rte_errno = errno;
348 return -rte_errno;
349 }
350
351 rc = trace_session_name_generate(session);
352 if (rc < 0)
353 return rc;
354 rc = trace_dir_update(session);
355 if (rc < 0)
356 return rc;
357
358 rc = mkdir(trace->dir, 0700);
359 if (rc < 0) {
360 trace_err("mkdir %s failed [%s]", trace->dir, strerror(errno));
361 rte_errno = errno;
362 return -rte_errno;
363 }
364
365 RTE_LOG(INFO, EAL, "Trace dir: %s\n", trace->dir);
366 return 0;
367 }
368
369 static int
trace_meta_save(struct trace * trace)370 trace_meta_save(struct trace *trace)
371 {
372 char file_name[PATH_MAX];
373 FILE *f;
374 int rc;
375
376 rc = snprintf(file_name, PATH_MAX, "%s/metadata", trace->dir);
377 if (rc < 0)
378 return rc;
379
380 f = fopen(file_name, "w");
381 if (f == NULL)
382 return -errno;
383
384 rc = rte_trace_metadata_dump(f);
385
386 if (fclose(f))
387 rc = -errno;
388
389 return rc;
390 }
391
392
393 static inline int
trace_file_sz(struct __rte_trace_header * hdr)394 trace_file_sz(struct __rte_trace_header *hdr)
395 {
396 return sizeof(struct __rte_trace_stream_header) + hdr->offset;
397 }
398
399 static int
trace_mem_save(struct trace * trace,struct __rte_trace_header * hdr,uint32_t cnt)400 trace_mem_save(struct trace *trace, struct __rte_trace_header *hdr,
401 uint32_t cnt)
402 {
403 char file_name[PATH_MAX];
404 FILE *f;
405 int rc;
406
407 rc = snprintf(file_name, PATH_MAX, "%s/channel0_%d", trace->dir, cnt);
408 if (rc < 0)
409 return rc;
410
411 f = fopen(file_name, "w");
412 if (f == NULL)
413 return -errno;
414
415 rc = fwrite(&hdr->stream_header, trace_file_sz(hdr), 1, f);
416 rc = (rc == 1) ? 0 : -EACCES;
417
418 if (fclose(f))
419 rc = -errno;
420
421 return rc;
422 }
423
424 int
rte_trace_save(void)425 rte_trace_save(void)
426 {
427 struct trace *trace = trace_obj_get();
428 struct __rte_trace_header *header;
429 uint32_t count;
430 int rc = 0;
431
432 if (trace->nb_trace_mem_list == 0)
433 return rc;
434
435 rc = trace_meta_save(trace);
436 if (rc)
437 return rc;
438
439 rte_spinlock_lock(&trace->lock);
440 for (count = 0; count < trace->nb_trace_mem_list; count++) {
441 header = trace->lcore_meta[count].mem;
442 rc = trace_mem_save(trace, header, count);
443 if (rc)
444 break;
445 }
446 rte_spinlock_unlock(&trace->lock);
447 return rc;
448 }
449