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