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 * 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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