1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2009, Steven Rostedt <srostedt@redhat.com> 4 */ 5 #include <dirent.h> 6 #include <stdio.h> 7 #include <stdlib.h> 8 #include <string.h> 9 #include <stdarg.h> 10 #include <sys/types.h> 11 #include <sys/stat.h> 12 #include <sys/wait.h> 13 #include <sys/mman.h> 14 #include <event-parse.h> 15 #include <fcntl.h> 16 #include <unistd.h> 17 #include <errno.h> 18 19 #include "trace-event.h" 20 #include "debug.h" 21 #include "util.h" 22 23 static int input_fd; 24 25 static ssize_t trace_data_size; 26 static bool repipe; 27 28 static int __do_read(int fd, void *buf, int size) 29 { 30 int rsize = size; 31 32 while (size) { 33 int ret = read(fd, buf, size); 34 35 if (ret <= 0) 36 return -1; 37 38 if (repipe) { 39 int retw = write(STDOUT_FILENO, buf, ret); 40 41 if (retw <= 0 || retw != ret) { 42 pr_debug("repiping input file"); 43 return -1; 44 } 45 } 46 47 size -= ret; 48 buf += ret; 49 } 50 51 return rsize; 52 } 53 54 static int do_read(void *data, int size) 55 { 56 int r; 57 58 r = __do_read(input_fd, data, size); 59 if (r <= 0) { 60 pr_debug("reading input file (size expected=%d received=%d)", 61 size, r); 62 return -1; 63 } 64 65 trace_data_size += r; 66 67 return r; 68 } 69 70 /* If it fails, the next read will report it */ 71 static void skip(int size) 72 { 73 char buf[BUFSIZ]; 74 int r; 75 76 while (size) { 77 r = size > BUFSIZ ? BUFSIZ : size; 78 do_read(buf, r); 79 size -= r; 80 } 81 } 82 83 static unsigned int read4(struct tep_handle *pevent) 84 { 85 unsigned int data; 86 87 if (do_read(&data, 4) < 0) 88 return 0; 89 return tep_read_number(pevent, &data, 4); 90 } 91 92 static unsigned long long read8(struct tep_handle *pevent) 93 { 94 unsigned long long data; 95 96 if (do_read(&data, 8) < 0) 97 return 0; 98 return tep_read_number(pevent, &data, 8); 99 } 100 101 static char *read_string(void) 102 { 103 char buf[BUFSIZ]; 104 char *str = NULL; 105 int size = 0; 106 off_t r; 107 char c; 108 109 for (;;) { 110 r = read(input_fd, &c, 1); 111 if (r < 0) { 112 pr_debug("reading input file"); 113 goto out; 114 } 115 116 if (!r) { 117 pr_debug("no data"); 118 goto out; 119 } 120 121 if (repipe) { 122 int retw = write(STDOUT_FILENO, &c, 1); 123 124 if (retw <= 0 || retw != r) { 125 pr_debug("repiping input file string"); 126 goto out; 127 } 128 } 129 130 buf[size++] = c; 131 132 if (!c) 133 break; 134 } 135 136 trace_data_size += size; 137 138 str = malloc(size); 139 if (str) 140 memcpy(str, buf, size); 141 out: 142 return str; 143 } 144 145 static int read_proc_kallsyms(struct tep_handle *pevent) 146 { 147 unsigned int size; 148 149 size = read4(pevent); 150 if (!size) 151 return 0; 152 /* 153 * Just skip it, now that we configure libtraceevent to use the 154 * tools/perf/ symbol resolver. 155 * 156 * We need to skip it so that we can continue parsing old perf.data 157 * files, that contains this /proc/kallsyms payload. 158 * 159 * Newer perf.data files will have just the 4-bytes zeros "kallsyms 160 * payload", so that older tools can continue reading it and interpret 161 * it as "no kallsyms payload is present". 162 */ 163 lseek(input_fd, size, SEEK_CUR); 164 trace_data_size += size; 165 return 0; 166 } 167 168 static int read_ftrace_printk(struct tep_handle *pevent) 169 { 170 unsigned int size; 171 char *buf; 172 173 /* it can have 0 size */ 174 size = read4(pevent); 175 if (!size) 176 return 0; 177 178 buf = malloc(size + 1); 179 if (buf == NULL) 180 return -1; 181 182 if (do_read(buf, size) < 0) { 183 free(buf); 184 return -1; 185 } 186 187 buf[size] = '\0'; 188 189 parse_ftrace_printk(pevent, buf, size); 190 191 free(buf); 192 return 0; 193 } 194 195 static int read_header_files(struct tep_handle *pevent) 196 { 197 unsigned long long size; 198 char *header_page; 199 char buf[BUFSIZ]; 200 int ret = 0; 201 202 if (do_read(buf, 12) < 0) 203 return -1; 204 205 if (memcmp(buf, "header_page", 12) != 0) { 206 pr_debug("did not read header page"); 207 return -1; 208 } 209 210 size = read8(pevent); 211 212 header_page = malloc(size); 213 if (header_page == NULL) 214 return -1; 215 216 if (do_read(header_page, size) < 0) { 217 pr_debug("did not read header page"); 218 free(header_page); 219 return -1; 220 } 221 222 if (!tep_parse_header_page(pevent, header_page, size, 223 tep_get_long_size(pevent))) { 224 /* 225 * The commit field in the page is of type long, 226 * use that instead, since it represents the kernel. 227 */ 228 tep_set_long_size(pevent, tep_get_header_page_size(pevent)); 229 } 230 free(header_page); 231 232 if (do_read(buf, 13) < 0) 233 return -1; 234 235 if (memcmp(buf, "header_event", 13) != 0) { 236 pr_debug("did not read header event"); 237 return -1; 238 } 239 240 size = read8(pevent); 241 skip(size); 242 243 return ret; 244 } 245 246 static int read_ftrace_file(struct tep_handle *pevent, unsigned long long size) 247 { 248 int ret; 249 char *buf; 250 251 buf = malloc(size); 252 if (buf == NULL) { 253 pr_debug("memory allocation failure\n"); 254 return -1; 255 } 256 257 ret = do_read(buf, size); 258 if (ret < 0) { 259 pr_debug("error reading ftrace file.\n"); 260 goto out; 261 } 262 263 ret = parse_ftrace_file(pevent, buf, size); 264 if (ret < 0) 265 pr_debug("error parsing ftrace file.\n"); 266 out: 267 free(buf); 268 return ret; 269 } 270 271 static int read_event_file(struct tep_handle *pevent, char *sys, 272 unsigned long long size) 273 { 274 int ret; 275 char *buf; 276 277 buf = malloc(size); 278 if (buf == NULL) { 279 pr_debug("memory allocation failure\n"); 280 return -1; 281 } 282 283 ret = do_read(buf, size); 284 if (ret < 0) 285 goto out; 286 287 ret = parse_event_file(pevent, buf, size, sys); 288 if (ret < 0) 289 pr_debug("error parsing event file.\n"); 290 out: 291 free(buf); 292 return ret; 293 } 294 295 static int read_ftrace_files(struct tep_handle *pevent) 296 { 297 unsigned long long size; 298 int count; 299 int i; 300 int ret; 301 302 count = read4(pevent); 303 304 for (i = 0; i < count; i++) { 305 size = read8(pevent); 306 ret = read_ftrace_file(pevent, size); 307 if (ret) 308 return ret; 309 } 310 return 0; 311 } 312 313 static int read_event_files(struct tep_handle *pevent) 314 { 315 unsigned long long size; 316 char *sys; 317 int systems; 318 int count; 319 int i,x; 320 int ret; 321 322 systems = read4(pevent); 323 324 for (i = 0; i < systems; i++) { 325 sys = read_string(); 326 if (sys == NULL) 327 return -1; 328 329 count = read4(pevent); 330 331 for (x=0; x < count; x++) { 332 size = read8(pevent); 333 ret = read_event_file(pevent, sys, size); 334 if (ret) { 335 free(sys); 336 return ret; 337 } 338 } 339 free(sys); 340 } 341 return 0; 342 } 343 344 static int read_saved_cmdline(struct tep_handle *pevent) 345 { 346 unsigned long long size; 347 char *buf; 348 int ret; 349 350 /* it can have 0 size */ 351 size = read8(pevent); 352 if (!size) 353 return 0; 354 355 buf = malloc(size + 1); 356 if (buf == NULL) { 357 pr_debug("memory allocation failure\n"); 358 return -1; 359 } 360 361 ret = do_read(buf, size); 362 if (ret < 0) { 363 pr_debug("error reading saved cmdlines\n"); 364 goto out; 365 } 366 buf[ret] = '\0'; 367 368 parse_saved_cmdline(pevent, buf, size); 369 ret = 0; 370 out: 371 free(buf); 372 return ret; 373 } 374 375 ssize_t trace_report(int fd, struct trace_event *tevent, bool __repipe) 376 { 377 char buf[BUFSIZ]; 378 char test[] = { 23, 8, 68 }; 379 char *version; 380 int show_version = 0; 381 int show_funcs = 0; 382 int show_printk = 0; 383 ssize_t size = -1; 384 int file_bigendian; 385 int host_bigendian; 386 int file_long_size; 387 int file_page_size; 388 struct tep_handle *pevent = NULL; 389 int err; 390 391 repipe = __repipe; 392 input_fd = fd; 393 394 if (do_read(buf, 3) < 0) 395 return -1; 396 if (memcmp(buf, test, 3) != 0) { 397 pr_debug("no trace data in the file"); 398 return -1; 399 } 400 401 if (do_read(buf, 7) < 0) 402 return -1; 403 if (memcmp(buf, "tracing", 7) != 0) { 404 pr_debug("not a trace file (missing 'tracing' tag)"); 405 return -1; 406 } 407 408 version = read_string(); 409 if (version == NULL) 410 return -1; 411 if (show_version) 412 printf("version = %s\n", version); 413 414 if (do_read(buf, 1) < 0) { 415 free(version); 416 return -1; 417 } 418 file_bigendian = buf[0]; 419 host_bigendian = host_is_bigendian() ? 1 : 0; 420 421 if (trace_event__init(tevent)) { 422 pr_debug("trace_event__init failed"); 423 goto out; 424 } 425 426 pevent = tevent->pevent; 427 428 tep_set_flag(pevent, TEP_NSEC_OUTPUT); 429 tep_set_file_bigendian(pevent, file_bigendian); 430 tep_set_local_bigendian(pevent, host_bigendian); 431 432 if (do_read(buf, 1) < 0) 433 goto out; 434 file_long_size = buf[0]; 435 436 file_page_size = read4(pevent); 437 if (!file_page_size) 438 goto out; 439 440 tep_set_long_size(pevent, file_long_size); 441 tep_set_page_size(pevent, file_page_size); 442 443 err = read_header_files(pevent); 444 if (err) 445 goto out; 446 err = read_ftrace_files(pevent); 447 if (err) 448 goto out; 449 err = read_event_files(pevent); 450 if (err) 451 goto out; 452 err = read_proc_kallsyms(pevent); 453 if (err) 454 goto out; 455 err = read_ftrace_printk(pevent); 456 if (err) 457 goto out; 458 if (atof(version) >= 0.6) { 459 err = read_saved_cmdline(pevent); 460 if (err) 461 goto out; 462 } 463 464 size = trace_data_size; 465 repipe = false; 466 467 if (show_funcs) { 468 tep_print_funcs(pevent); 469 } else if (show_printk) { 470 tep_print_printk(pevent); 471 } 472 473 pevent = NULL; 474 475 out: 476 if (pevent) 477 trace_event__cleanup(tevent); 478 free(version); 479 return size; 480 } 481