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 40 static int input_fd; 41 42 static ssize_t trace_data_size; 43 static bool repipe; 44 45 static 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 71 static 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 */ 88 static 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 100 static 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 109 static 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 118 static 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); 158 out: 159 return str; 160 } 161 162 static 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 185 static 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 + 1); 196 if (buf == NULL) 197 return -1; 198 199 if (do_read(buf, size) < 0) { 200 free(buf); 201 return -1; 202 } 203 204 buf[size] = '\0'; 205 206 parse_ftrace_printk(pevent, buf, size); 207 208 free(buf); 209 return 0; 210 } 211 212 static int read_header_files(struct pevent *pevent) 213 { 214 unsigned long long size; 215 char *header_page; 216 char buf[BUFSIZ]; 217 int ret = 0; 218 219 if (do_read(buf, 12) < 0) 220 return -1; 221 222 if (memcmp(buf, "header_page", 12) != 0) { 223 pr_debug("did not read header page"); 224 return -1; 225 } 226 227 size = read8(pevent); 228 229 header_page = malloc(size); 230 if (header_page == NULL) 231 return -1; 232 233 if (do_read(header_page, size) < 0) { 234 pr_debug("did not read header page"); 235 free(header_page); 236 return -1; 237 } 238 239 if (!pevent_parse_header_page(pevent, header_page, size, 240 pevent_get_long_size(pevent))) { 241 /* 242 * The commit field in the page is of type long, 243 * use that instead, since it represents the kernel. 244 */ 245 pevent_set_long_size(pevent, pevent->header_page_size_size); 246 } 247 free(header_page); 248 249 if (do_read(buf, 13) < 0) 250 return -1; 251 252 if (memcmp(buf, "header_event", 13) != 0) { 253 pr_debug("did not read header event"); 254 return -1; 255 } 256 257 size = read8(pevent); 258 skip(size); 259 260 return ret; 261 } 262 263 static int read_ftrace_file(struct pevent *pevent, unsigned long long size) 264 { 265 int ret; 266 char *buf; 267 268 buf = malloc(size); 269 if (buf == NULL) { 270 pr_debug("memory allocation failure\n"); 271 return -1; 272 } 273 274 ret = do_read(buf, size); 275 if (ret < 0) { 276 pr_debug("error reading ftrace file.\n"); 277 goto out; 278 } 279 280 ret = parse_ftrace_file(pevent, buf, size); 281 if (ret < 0) 282 pr_debug("error parsing ftrace file.\n"); 283 out: 284 free(buf); 285 return ret; 286 } 287 288 static int read_event_file(struct pevent *pevent, char *sys, 289 unsigned long long size) 290 { 291 int ret; 292 char *buf; 293 294 buf = malloc(size); 295 if (buf == NULL) { 296 pr_debug("memory allocation failure\n"); 297 return -1; 298 } 299 300 ret = do_read(buf, size); 301 if (ret < 0) { 302 free(buf); 303 goto out; 304 } 305 306 ret = parse_event_file(pevent, buf, size, sys); 307 if (ret < 0) 308 pr_debug("error parsing event file.\n"); 309 out: 310 free(buf); 311 return ret; 312 } 313 314 static int read_ftrace_files(struct pevent *pevent) 315 { 316 unsigned long long size; 317 int count; 318 int i; 319 int ret; 320 321 count = read4(pevent); 322 323 for (i = 0; i < count; i++) { 324 size = read8(pevent); 325 ret = read_ftrace_file(pevent, size); 326 if (ret) 327 return ret; 328 } 329 return 0; 330 } 331 332 static int read_event_files(struct pevent *pevent) 333 { 334 unsigned long long size; 335 char *sys; 336 int systems; 337 int count; 338 int i,x; 339 int ret; 340 341 systems = read4(pevent); 342 343 for (i = 0; i < systems; i++) { 344 sys = read_string(); 345 if (sys == NULL) 346 return -1; 347 348 count = read4(pevent); 349 350 for (x=0; x < count; x++) { 351 size = read8(pevent); 352 ret = read_event_file(pevent, sys, size); 353 if (ret) 354 return ret; 355 } 356 } 357 return 0; 358 } 359 360 static int read_saved_cmdline(struct pevent *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 pevent *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 pevent_set_flag(pevent, PEVENT_NSEC_OUTPUT); 444 pevent_set_file_bigendian(pevent, file_bigendian); 445 pevent_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 pevent_set_long_size(pevent, file_long_size); 456 pevent_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 pevent_print_funcs(pevent); 484 } else if (show_printk) { 485 pevent_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