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 static int 318 trace_mkdir(void) 319 { 320 struct trace *trace = trace_obj_get(); 321 char session[TRACE_DIR_STR_LEN]; 322 static bool already_done; 323 char *dir_path; 324 int rc; 325 326 if (already_done) 327 return 0; 328 329 if (!trace->dir_offset) { 330 dir_path = calloc(1, sizeof(trace->dir)); 331 if (dir_path == NULL) { 332 trace_err("fail to allocate memory"); 333 return -ENOMEM; 334 } 335 336 rc = trace_dir_default_path_get(dir_path); 337 if (rc < 0) { 338 trace_err("fail to get default path"); 339 free(dir_path); 340 return rc; 341 } 342 343 rc = trace_dir_update(dir_path); 344 free(dir_path); 345 if (rc < 0) 346 return rc; 347 } 348 349 /* Create the path if it t exist, no "mkdir -p" available here */ 350 rc = mkdir(trace->dir, 0700); 351 if (rc < 0 && errno != EEXIST) { 352 trace_err("mkdir %s failed [%s]", trace->dir, strerror(errno)); 353 rte_errno = errno; 354 return -rte_errno; 355 } 356 357 rc = trace_session_name_generate(session); 358 if (rc < 0) 359 return rc; 360 rc = trace_dir_update(session); 361 if (rc < 0) 362 return rc; 363 364 rc = mkdir(trace->dir, 0700); 365 if (rc < 0) { 366 trace_err("mkdir %s failed [%s]", trace->dir, strerror(errno)); 367 rte_errno = errno; 368 return -rte_errno; 369 } 370 371 RTE_LOG(INFO, EAL, "Trace dir: %s\n", trace->dir); 372 already_done = true; 373 return 0; 374 } 375 376 static int 377 trace_meta_save(struct trace *trace) 378 { 379 char file_name[PATH_MAX]; 380 FILE *f; 381 int rc; 382 383 rc = snprintf(file_name, PATH_MAX, "%s/metadata", trace->dir); 384 if (rc < 0) 385 return rc; 386 387 f = fopen(file_name, "w"); 388 if (f == NULL) 389 return -errno; 390 391 rc = rte_trace_metadata_dump(f); 392 393 if (fclose(f)) 394 rc = -errno; 395 396 return rc; 397 } 398 399 400 static inline int 401 trace_file_sz(struct __rte_trace_header *hdr) 402 { 403 return sizeof(struct __rte_trace_stream_header) + hdr->offset; 404 } 405 406 static int 407 trace_mem_save(struct trace *trace, struct __rte_trace_header *hdr, 408 uint32_t cnt) 409 { 410 char file_name[PATH_MAX]; 411 FILE *f; 412 int rc; 413 414 rc = snprintf(file_name, PATH_MAX, "%s/channel0_%d", trace->dir, cnt); 415 if (rc < 0) 416 return rc; 417 418 f = fopen(file_name, "w"); 419 if (f == NULL) 420 return -errno; 421 422 rc = fwrite(&hdr->stream_header, trace_file_sz(hdr), 1, f); 423 rc = (rc == 1) ? 0 : -EACCES; 424 425 if (fclose(f)) 426 rc = -errno; 427 428 return rc; 429 } 430 431 int 432 rte_trace_save(void) 433 { 434 struct trace *trace = trace_obj_get(); 435 struct __rte_trace_header *header; 436 uint32_t count; 437 int rc = 0; 438 439 if (trace->nb_trace_mem_list == 0) 440 return rc; 441 442 rc = trace_mkdir(); 443 if (rc < 0) 444 return rc; 445 446 rc = trace_meta_save(trace); 447 if (rc) 448 return rc; 449 450 rte_spinlock_lock(&trace->lock); 451 for (count = 0; count < trace->nb_trace_mem_list; count++) { 452 header = trace->lcore_meta[count].mem; 453 rc = trace_mem_save(trace, header, count); 454 if (rc) 455 break; 456 } 457 rte_spinlock_unlock(&trace->lock); 458 return rc; 459 } 460