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 pevent *pevent) 100 { 101 unsigned int data; 102 103 if (do_read(&data, 4) < 0) 104 return 0; 105 return __data2host4(pevent, data); 106 } 107 108 static unsigned long long read8(struct pevent *pevent) 109 { 110 unsigned long long data; 111 112 if (do_read(&data, 8) < 0) 113 return 0; 114 return __data2host8(pevent, data); 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 pevent *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 pevent *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 pevent *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 (!pevent_parse_header_page(pevent, header_page, size, 239 pevent_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 pevent_set_long_size(pevent, pevent->header_page_size_size); 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 pevent *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 pevent *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 free(buf); 302 goto out; 303 } 304 305 ret = parse_event_file(pevent, buf, size, sys); 306 if (ret < 0) 307 pr_debug("error parsing event file.\n"); 308 out: 309 free(buf); 310 return ret; 311 } 312 313 static int read_ftrace_files(struct pevent *pevent) 314 { 315 unsigned long long size; 316 int count; 317 int i; 318 int ret; 319 320 count = read4(pevent); 321 322 for (i = 0; i < count; i++) { 323 size = read8(pevent); 324 ret = read_ftrace_file(pevent, size); 325 if (ret) 326 return ret; 327 } 328 return 0; 329 } 330 331 static int read_event_files(struct pevent *pevent) 332 { 333 unsigned long long size; 334 char *sys; 335 int systems; 336 int count; 337 int i,x; 338 int ret; 339 340 systems = read4(pevent); 341 342 for (i = 0; i < systems; i++) { 343 sys = read_string(); 344 if (sys == NULL) 345 return -1; 346 347 count = read4(pevent); 348 349 for (x=0; x < count; x++) { 350 size = read8(pevent); 351 ret = read_event_file(pevent, sys, size); 352 if (ret) 353 return ret; 354 } 355 } 356 return 0; 357 } 358 359 static int read_saved_cmdline(struct pevent *pevent) 360 { 361 unsigned long long size; 362 char *buf; 363 int ret; 364 365 /* it can have 0 size */ 366 size = read8(pevent); 367 if (!size) 368 return 0; 369 370 buf = malloc(size + 1); 371 if (buf == NULL) { 372 pr_debug("memory allocation failure\n"); 373 return -1; 374 } 375 376 ret = do_read(buf, size); 377 if (ret < 0) { 378 pr_debug("error reading saved cmdlines\n"); 379 goto out; 380 } 381 382 parse_saved_cmdline(pevent, buf, size); 383 ret = 0; 384 out: 385 free(buf); 386 return ret; 387 } 388 389 ssize_t trace_report(int fd, struct trace_event *tevent, bool __repipe) 390 { 391 char buf[BUFSIZ]; 392 char test[] = { 23, 8, 68 }; 393 char *version; 394 int show_version = 0; 395 int show_funcs = 0; 396 int show_printk = 0; 397 ssize_t size = -1; 398 int file_bigendian; 399 int host_bigendian; 400 int file_long_size; 401 int file_page_size; 402 struct pevent *pevent = NULL; 403 int err; 404 405 repipe = __repipe; 406 input_fd = fd; 407 408 if (do_read(buf, 3) < 0) 409 return -1; 410 if (memcmp(buf, test, 3) != 0) { 411 pr_debug("no trace data in the file"); 412 return -1; 413 } 414 415 if (do_read(buf, 7) < 0) 416 return -1; 417 if (memcmp(buf, "tracing", 7) != 0) { 418 pr_debug("not a trace file (missing 'tracing' tag)"); 419 return -1; 420 } 421 422 version = read_string(); 423 if (version == NULL) 424 return -1; 425 if (show_version) 426 printf("version = %s\n", version); 427 428 if (do_read(buf, 1) < 0) { 429 free(version); 430 return -1; 431 } 432 file_bigendian = buf[0]; 433 host_bigendian = bigendian(); 434 435 if (trace_event__init(tevent)) { 436 pr_debug("trace_event__init failed"); 437 goto out; 438 } 439 440 pevent = tevent->pevent; 441 442 pevent_set_flag(pevent, PEVENT_NSEC_OUTPUT); 443 pevent_set_file_bigendian(pevent, file_bigendian); 444 pevent_set_host_bigendian(pevent, host_bigendian); 445 446 if (do_read(buf, 1) < 0) 447 goto out; 448 file_long_size = buf[0]; 449 450 file_page_size = read4(pevent); 451 if (!file_page_size) 452 goto out; 453 454 pevent_set_long_size(pevent, file_long_size); 455 pevent_set_page_size(pevent, file_page_size); 456 457 err = read_header_files(pevent); 458 if (err) 459 goto out; 460 err = read_ftrace_files(pevent); 461 if (err) 462 goto out; 463 err = read_event_files(pevent); 464 if (err) 465 goto out; 466 err = read_proc_kallsyms(pevent); 467 if (err) 468 goto out; 469 err = read_ftrace_printk(pevent); 470 if (err) 471 goto out; 472 if (atof(version) >= 0.6) { 473 err = read_saved_cmdline(pevent); 474 if (err) 475 goto out; 476 } 477 478 size = trace_data_size; 479 repipe = false; 480 481 if (show_funcs) { 482 pevent_print_funcs(pevent); 483 } else if (show_printk) { 484 pevent_print_printk(pevent); 485 } 486 487 pevent = NULL; 488 489 out: 490 if (pevent) 491 trace_event__cleanup(tevent); 492 free(version); 493 return size; 494 } 495