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