xref: /dpdk/lib/eal/common/eal_common_trace_utils.c (revision daa02b5cddbb8e11b31d41e2bf7bb1ae64dcae2f)
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 *
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 *
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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