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