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