481 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*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 <traceevent/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 23static int input_fd;24 25static ssize_t trace_data_size;26static bool repipe;27 28static 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 54static 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 */71static 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 83static 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 92static 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 101static 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);141out:142 return str;143}144 145static 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 the154 * tools/perf/ symbol resolver.155 *156 * We need to skip it so that we can continue parsing old perf.data157 * files, that contains this /proc/kallsyms payload.158 *159 * Newer perf.data files will have just the 4-bytes zeros "kallsyms160 * payload", so that older tools can continue reading it and interpret161 * 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 168static 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 195static 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 246static 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");266out:267 free(buf);268 return ret;269}270 271static 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");290out:291 free(buf);292 return ret;293}294 295static 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 313static 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 344static 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;370out:371 free(buf);372 return ret;373}374 375ssize_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 475out:476 if (pevent)477 trace_event__cleanup(tevent);478 free(version);479 return size;480}481