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