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