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