xref: /dpdk/lib/eal/common/eal_common_trace_utils.c (revision 7af3e7aaf9141e78858a38c0d6e32b46a196c08f)
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 - 1;
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 		errno = ENOSPC;
114 		goto fail;
115 	}
116 
117 	return rc;
118 fail:
119 	rte_errno = errno;
120 	return -rte_errno;
121 }
122 
123 static int
124 trace_dir_update(const char *str)
125 {
126 	struct trace *trace = trace_obj_get();
127 	int rc, remaining;
128 
129 	remaining = sizeof(trace->dir) - trace->dir_offset;
130 	rc = rte_strscpy(&trace->dir[0] + trace->dir_offset, str, remaining);
131 	if (rc < 0)
132 		goto fail;
133 
134 	trace->dir_offset += rc;
135 fail:
136 	return rc;
137 }
138 
139 int
140 eal_trace_args_save(const char *val)
141 {
142 	struct trace *trace = trace_obj_get();
143 	struct trace_arg *arg = malloc(sizeof(*arg));
144 
145 	if (arg == NULL) {
146 		trace_err("failed to allocate memory for %s", val);
147 		return -ENOMEM;
148 	}
149 
150 	arg->val = strdup(val);
151 	if (arg->val == NULL) {
152 		trace_err("failed to allocate memory for %s", val);
153 		free(arg);
154 		return -ENOMEM;
155 	}
156 
157 	STAILQ_INSERT_TAIL(&trace->args, arg, next);
158 	return 0;
159 }
160 
161 void
162 eal_trace_args_free(void)
163 {
164 	struct trace *trace = trace_obj_get();
165 	struct trace_arg *arg;
166 
167 	while (!STAILQ_EMPTY(&trace->args)) {
168 		arg = STAILQ_FIRST(&trace->args);
169 		STAILQ_REMOVE_HEAD(&trace->args, next);
170 		free(arg->val);
171 		free(arg);
172 	}
173 }
174 
175 int
176 trace_args_apply(const char *arg)
177 {
178 	if (rte_trace_regexp(arg, true) < 0) {
179 		trace_err("cannot enable trace for %s", arg);
180 		return -1;
181 	}
182 
183 	return 0;
184 }
185 
186 int
187 eal_trace_bufsz_args_save(char const *val)
188 {
189 	struct trace *trace = trace_obj_get();
190 	uint64_t bufsz;
191 
192 	bufsz = rte_str_to_size(val);
193 	if (bufsz == 0) {
194 		trace_err("buffer size cannot be zero");
195 		return -EINVAL;
196 	}
197 
198 	trace->buff_len = bufsz;
199 	return 0;
200 }
201 
202 void
203 trace_bufsz_args_apply(void)
204 {
205 	struct trace *trace = trace_obj_get();
206 
207 	if (trace->buff_len == 0)
208 		trace->buff_len = 1024 * 1024; /* 1MB */
209 }
210 
211 int
212 eal_trace_mode_args_save(const char *val)
213 {
214 	struct trace *trace = trace_obj_get();
215 	size_t len = strlen(val);
216 	unsigned long tmp;
217 	char *pattern;
218 
219 	if (len == 0) {
220 		trace_err("value is not provided with option");
221 		return -EINVAL;
222 	}
223 
224 	pattern = (char *)calloc(1, len + 2);
225 	if (pattern == NULL) {
226 		trace_err("fail to allocate memory");
227 		return -ENOMEM;
228 	}
229 
230 	sprintf(pattern, "%s*", val);
231 
232 	if (fnmatch(pattern, "overwrite", 0) == 0)
233 		tmp = RTE_TRACE_MODE_OVERWRITE;
234 	else if (fnmatch(pattern, "discard", 0) == 0)
235 		tmp = RTE_TRACE_MODE_DISCARD;
236 	else {
237 		free(pattern);
238 		return -EINVAL;
239 	}
240 
241 	trace->mode = tmp;
242 	free(pattern);
243 	return 0;
244 }
245 
246 int
247 eal_trace_dir_args_save(char const *val)
248 {
249 	struct trace *trace = trace_obj_get();
250 	char *dir_path;
251 	int rc;
252 
253 	if (strlen(val) >= sizeof(trace->dir) - 1) {
254 		trace_err("input string is too big");
255 		return -ENAMETOOLONG;
256 	}
257 
258 	if (asprintf(&dir_path, "%s/", val) == -1) {
259 		trace_err("failed to copy directory: %s", strerror(errno));
260 		return -ENOMEM;
261 	}
262 
263 	rc = trace_dir_update(dir_path);
264 
265 	free(dir_path);
266 	return rc;
267 }
268 
269 int
270 trace_epoch_time_save(void)
271 {
272 	struct trace *trace = trace_obj_get();
273 	struct timespec epoch = { 0, 0 };
274 	uint64_t avg, start, end;
275 
276 	start = rte_get_tsc_cycles();
277 	if (clock_gettime(CLOCK_REALTIME, &epoch) < 0) {
278 		trace_err("failed to get the epoch time");
279 		return -1;
280 	}
281 	end = rte_get_tsc_cycles();
282 	avg = (start + end) >> 1;
283 
284 	trace->epoch_sec = (uint64_t) epoch.tv_sec;
285 	trace->epoch_nsec = (uint64_t) epoch.tv_nsec;
286 	trace->uptime_ticks = avg;
287 
288 	return 0;
289 }
290 
291 static int
292 trace_dir_default_path_get(char *dir_path)
293 {
294 	struct trace *trace = trace_obj_get();
295 	uint32_t size = sizeof(trace->dir);
296 	struct passwd *pwd;
297 	char *home_dir;
298 
299 	/* First check for shell environment variable */
300 	home_dir = getenv("HOME");
301 	if (home_dir == NULL) {
302 		/* Fallback to password file entry */
303 		pwd = getpwuid(getuid());
304 		if (pwd == NULL)
305 			return -EINVAL;
306 
307 		home_dir = pwd->pw_dir;
308 	}
309 
310 	/* Append dpdk-traces to directory */
311 	if (snprintf(dir_path, size, "%s/dpdk-traces/", home_dir) < 0)
312 		return -ENAMETOOLONG;
313 
314 	return 0;
315 }
316 
317 int
318 trace_mkdir(void)
319 {
320 	struct trace *trace = trace_obj_get();
321 	char session[TRACE_DIR_STR_LEN];
322 	char *dir_path;
323 	int rc;
324 
325 	if (!trace->dir_offset) {
326 		dir_path = calloc(1, sizeof(trace->dir));
327 		if (dir_path == NULL) {
328 			trace_err("fail to allocate memory");
329 			return -ENOMEM;
330 		}
331 
332 		rc = trace_dir_default_path_get(dir_path);
333 		if (rc < 0) {
334 			trace_err("fail to get default path");
335 			free(dir_path);
336 			return rc;
337 		}
338 
339 		rc = trace_dir_update(dir_path);
340 		free(dir_path);
341 		if (rc < 0)
342 			return rc;
343 	}
344 
345 	/* Create the path if it t exist, no "mkdir -p" available here */
346 	rc = mkdir(trace->dir, 0700);
347 	if (rc < 0 && errno != EEXIST) {
348 		trace_err("mkdir %s failed [%s]", trace->dir, strerror(errno));
349 		rte_errno = errno;
350 		return -rte_errno;
351 	}
352 
353 	rc = trace_session_name_generate(session);
354 	if (rc < 0)
355 		return rc;
356 	rc = trace_dir_update(session);
357 	if (rc < 0)
358 		return rc;
359 
360 	rc = mkdir(trace->dir, 0700);
361 	if (rc < 0) {
362 		trace_err("mkdir %s failed [%s]", trace->dir, strerror(errno));
363 		rte_errno = errno;
364 		return -rte_errno;
365 	}
366 
367 	RTE_LOG(INFO, EAL, "Trace dir: %s\n", trace->dir);
368 	return 0;
369 }
370 
371 static int
372 trace_meta_save(struct trace *trace)
373 {
374 	char file_name[PATH_MAX];
375 	FILE *f;
376 	int rc;
377 
378 	rc = snprintf(file_name, PATH_MAX, "%s/metadata", trace->dir);
379 	if (rc < 0)
380 		return rc;
381 
382 	f = fopen(file_name, "w");
383 	if (f == NULL)
384 		return -errno;
385 
386 	rc = rte_trace_metadata_dump(f);
387 
388 	if (fclose(f))
389 		rc = -errno;
390 
391 	return rc;
392 }
393 
394 
395 static inline int
396 trace_file_sz(struct __rte_trace_header *hdr)
397 {
398 	return sizeof(struct __rte_trace_stream_header) + hdr->offset;
399 }
400 
401 static int
402 trace_mem_save(struct trace *trace, struct __rte_trace_header *hdr,
403 		uint32_t cnt)
404 {
405 	char file_name[PATH_MAX];
406 	FILE *f;
407 	int rc;
408 
409 	rc = snprintf(file_name, PATH_MAX, "%s/channel0_%d", trace->dir, cnt);
410 	if (rc < 0)
411 		return rc;
412 
413 	f = fopen(file_name, "w");
414 	if (f == NULL)
415 		return -errno;
416 
417 	rc = fwrite(&hdr->stream_header, trace_file_sz(hdr), 1, f);
418 	rc = (rc == 1) ?  0 : -EACCES;
419 
420 	if (fclose(f))
421 		rc = -errno;
422 
423 	return rc;
424 }
425 
426 int
427 rte_trace_save(void)
428 {
429 	struct trace *trace = trace_obj_get();
430 	struct __rte_trace_header *header;
431 	uint32_t count;
432 	int rc = 0;
433 
434 	if (trace->nb_trace_mem_list == 0)
435 		return rc;
436 
437 	rc = trace_meta_save(trace);
438 	if (rc)
439 		return rc;
440 
441 	rte_spinlock_lock(&trace->lock);
442 	for (count = 0; count < trace->nb_trace_mem_list; count++) {
443 		header = trace->lcore_meta[count].mem;
444 		rc =  trace_mem_save(trace, header, count);
445 		if (rc)
446 			break;
447 	}
448 	rte_spinlock_unlock(&trace->lock);
449 	return rc;
450 }
451