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