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; 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 goto fail; 114 115 return rc; 116 fail: 117 rte_errno = errno; 118 return -rte_errno; 119 } 120 121 static int 122 trace_dir_update(const char *str) 123 { 124 struct trace *trace = trace_obj_get(); 125 int rc, remaining; 126 127 remaining = sizeof(trace->dir) - trace->dir_offset; 128 rc = rte_strscpy(&trace->dir[0] + trace->dir_offset, str, remaining); 129 if (rc < 0) 130 goto fail; 131 132 trace->dir_offset += rc; 133 fail: 134 return rc; 135 } 136 137 int 138 eal_trace_args_save(const char *val) 139 { 140 struct trace *trace = trace_obj_get(); 141 struct trace_arg *arg = malloc(sizeof(*arg)); 142 143 if (arg == NULL) { 144 trace_err("failed to allocate memory for %s", val); 145 return -ENOMEM; 146 } 147 148 arg->val = strdup(val); 149 if (arg->val == NULL) { 150 trace_err("failed to allocate memory for %s", val); 151 free(arg); 152 return -ENOMEM; 153 } 154 155 STAILQ_INSERT_TAIL(&trace->args, arg, next); 156 return 0; 157 } 158 159 void 160 eal_trace_args_free(void) 161 { 162 struct trace *trace = trace_obj_get(); 163 struct trace_arg *arg; 164 165 while (!STAILQ_EMPTY(&trace->args)) { 166 arg = STAILQ_FIRST(&trace->args); 167 STAILQ_REMOVE_HEAD(&trace->args, next); 168 free(arg->val); 169 free(arg); 170 } 171 } 172 173 int 174 trace_args_apply(const char *arg) 175 { 176 if (rte_trace_regexp(arg, true) < 0) { 177 trace_err("cannot enable trace for %s", arg); 178 return -1; 179 } 180 181 return 0; 182 } 183 184 int 185 eal_trace_bufsz_args_save(char const *val) 186 { 187 struct trace *trace = trace_obj_get(); 188 uint64_t bufsz; 189 190 bufsz = rte_str_to_size(val); 191 if (bufsz == 0) { 192 trace_err("buffer size cannot be zero"); 193 return -EINVAL; 194 } 195 196 trace->buff_len = bufsz; 197 return 0; 198 } 199 200 void 201 trace_bufsz_args_apply(void) 202 { 203 struct trace *trace = trace_obj_get(); 204 205 if (trace->buff_len == 0) 206 trace->buff_len = 1024 * 1024; /* 1MB */ 207 } 208 209 int 210 eal_trace_mode_args_save(const char *val) 211 { 212 struct trace *trace = trace_obj_get(); 213 size_t len = strlen(val); 214 unsigned long tmp; 215 char *pattern; 216 217 if (len == 0) { 218 trace_err("value is not provided with option"); 219 return -EINVAL; 220 } 221 222 pattern = (char *)calloc(1, len + 2); 223 if (pattern == NULL) { 224 trace_err("fail to allocate memory"); 225 return -ENOMEM; 226 } 227 228 sprintf(pattern, "%s*", val); 229 230 if (fnmatch(pattern, "overwrite", 0) == 0) 231 tmp = RTE_TRACE_MODE_OVERWRITE; 232 else if (fnmatch(pattern, "discard", 0) == 0) 233 tmp = RTE_TRACE_MODE_DISCARD; 234 else { 235 free(pattern); 236 return -EINVAL; 237 } 238 239 trace->mode = tmp; 240 free(pattern); 241 return 0; 242 } 243 244 int 245 eal_trace_dir_args_save(char const *val) 246 { 247 struct trace *trace = trace_obj_get(); 248 char *dir_path; 249 int rc; 250 251 if (strlen(val) >= sizeof(trace->dir) - 1) { 252 trace_err("input string is too big"); 253 return -ENAMETOOLONG; 254 } 255 256 if (asprintf(&dir_path, "%s/", val) == -1) { 257 trace_err("failed to copy directory: %s", strerror(errno)); 258 return -ENOMEM; 259 } 260 261 rc = trace_dir_update(dir_path); 262 263 free(dir_path); 264 return rc; 265 } 266 267 int 268 trace_epoch_time_save(void) 269 { 270 struct trace *trace = trace_obj_get(); 271 struct timespec epoch = { 0, 0 }; 272 uint64_t avg, start, end; 273 274 start = rte_get_tsc_cycles(); 275 if (clock_gettime(CLOCK_REALTIME, &epoch) < 0) { 276 trace_err("failed to get the epoch time"); 277 return -1; 278 } 279 end = rte_get_tsc_cycles(); 280 avg = (start + end) >> 1; 281 282 trace->epoch_sec = (uint64_t) epoch.tv_sec; 283 trace->epoch_nsec = (uint64_t) epoch.tv_nsec; 284 trace->uptime_ticks = avg; 285 286 return 0; 287 } 288 289 static int 290 trace_dir_default_path_get(char *dir_path) 291 { 292 struct trace *trace = trace_obj_get(); 293 uint32_t size = sizeof(trace->dir); 294 struct passwd *pwd; 295 char *home_dir; 296 297 /* First check for shell environment variable */ 298 home_dir = getenv("HOME"); 299 if (home_dir == NULL) { 300 /* Fallback to password file entry */ 301 pwd = getpwuid(getuid()); 302 if (pwd == NULL) 303 return -EINVAL; 304 305 home_dir = pwd->pw_dir; 306 } 307 308 /* Append dpdk-traces to directory */ 309 if (snprintf(dir_path, size, "%s/dpdk-traces/", home_dir) < 0) 310 return -ENAMETOOLONG; 311 312 return 0; 313 } 314 315 int 316 trace_mkdir(void) 317 { 318 struct trace *trace = trace_obj_get(); 319 char session[TRACE_DIR_STR_LEN]; 320 char *dir_path; 321 int rc; 322 323 if (!trace->dir_offset) { 324 dir_path = calloc(1, sizeof(trace->dir)); 325 if (dir_path == NULL) { 326 trace_err("fail to allocate memory"); 327 return -ENOMEM; 328 } 329 330 rc = trace_dir_default_path_get(dir_path); 331 if (rc < 0) { 332 trace_err("fail to get default path"); 333 free(dir_path); 334 return rc; 335 } 336 337 rc = trace_dir_update(dir_path); 338 free(dir_path); 339 if (rc < 0) 340 return rc; 341 } 342 343 /* Create the path if it t exist, no "mkdir -p" available here */ 344 rc = mkdir(trace->dir, 0700); 345 if (rc < 0 && errno != EEXIST) { 346 trace_err("mkdir %s failed [%s]", trace->dir, strerror(errno)); 347 rte_errno = errno; 348 return -rte_errno; 349 } 350 351 rc = trace_session_name_generate(session); 352 if (rc < 0) 353 return rc; 354 rc = trace_dir_update(session); 355 if (rc < 0) 356 return rc; 357 358 rc = mkdir(trace->dir, 0700); 359 if (rc < 0) { 360 trace_err("mkdir %s failed [%s]", trace->dir, strerror(errno)); 361 rte_errno = errno; 362 return -rte_errno; 363 } 364 365 RTE_LOG(INFO, EAL, "Trace dir: %s\n", trace->dir); 366 return 0; 367 } 368 369 static int 370 trace_meta_save(struct trace *trace) 371 { 372 char file_name[PATH_MAX]; 373 FILE *f; 374 int rc; 375 376 rc = snprintf(file_name, PATH_MAX, "%s/metadata", trace->dir); 377 if (rc < 0) 378 return rc; 379 380 f = fopen(file_name, "w"); 381 if (f == NULL) 382 return -errno; 383 384 rc = rte_trace_metadata_dump(f); 385 386 if (fclose(f)) 387 rc = -errno; 388 389 return rc; 390 } 391 392 393 static inline int 394 trace_file_sz(struct __rte_trace_header *hdr) 395 { 396 return sizeof(struct __rte_trace_stream_header) + hdr->offset; 397 } 398 399 static int 400 trace_mem_save(struct trace *trace, struct __rte_trace_header *hdr, 401 uint32_t cnt) 402 { 403 char file_name[PATH_MAX]; 404 FILE *f; 405 int rc; 406 407 rc = snprintf(file_name, PATH_MAX, "%s/channel0_%d", trace->dir, cnt); 408 if (rc < 0) 409 return rc; 410 411 f = fopen(file_name, "w"); 412 if (f == NULL) 413 return -errno; 414 415 rc = fwrite(&hdr->stream_header, trace_file_sz(hdr), 1, f); 416 rc = (rc == 1) ? 0 : -EACCES; 417 418 if (fclose(f)) 419 rc = -errno; 420 421 return rc; 422 } 423 424 int 425 rte_trace_save(void) 426 { 427 struct trace *trace = trace_obj_get(); 428 struct __rte_trace_header *header; 429 uint32_t count; 430 int rc = 0; 431 432 if (trace->nb_trace_mem_list == 0) 433 return rc; 434 435 rc = trace_meta_save(trace); 436 if (rc) 437 return rc; 438 439 rte_spinlock_lock(&trace->lock); 440 for (count = 0; count < trace->nb_trace_mem_list; count++) { 441 header = trace->lcore_meta[count].mem; 442 rc = trace_mem_save(trace, header, count); 443 if (rc) 444 break; 445 } 446 rte_spinlock_unlock(&trace->lock); 447 return rc; 448 } 449