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