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