1 /* 2 * Copyright (C) 2008,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 "util.h" 22 #include <dirent.h> 23 #include <mntent.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include <stdarg.h> 28 #include <sys/types.h> 29 #include <sys/stat.h> 30 #include <sys/wait.h> 31 #include <pthread.h> 32 #include <fcntl.h> 33 #include <unistd.h> 34 #include <errno.h> 35 #include <stdbool.h> 36 #include <linux/list.h> 37 #include <linux/kernel.h> 38 39 #include "../perf.h" 40 #include "trace-event.h" 41 #include <api/fs/debugfs.h> 42 #include "evsel.h" 43 #include "debug.h" 44 45 #define VERSION "0.5" 46 47 static int output_fd; 48 49 50 int bigendian(void) 51 { 52 unsigned char str[] = { 0x1, 0x2, 0x3, 0x4, 0x0, 0x0, 0x0, 0x0}; 53 unsigned int *ptr; 54 55 ptr = (unsigned int *)(void *)str; 56 return *ptr == 0x01020304; 57 } 58 59 /* unfortunately, you can not stat debugfs or proc files for size */ 60 static int record_file(const char *file, ssize_t hdr_sz) 61 { 62 unsigned long long size = 0; 63 char buf[BUFSIZ], *sizep; 64 off_t hdr_pos = lseek(output_fd, 0, SEEK_CUR); 65 int r, fd; 66 int err = -EIO; 67 68 fd = open(file, O_RDONLY); 69 if (fd < 0) { 70 pr_debug("Can't read '%s'", file); 71 return -errno; 72 } 73 74 /* put in zeros for file size, then fill true size later */ 75 if (hdr_sz) { 76 if (write(output_fd, &size, hdr_sz) != hdr_sz) 77 goto out; 78 } 79 80 do { 81 r = read(fd, buf, BUFSIZ); 82 if (r > 0) { 83 size += r; 84 if (write(output_fd, buf, r) != r) 85 goto out; 86 } 87 } while (r > 0); 88 89 /* ugh, handle big-endian hdr_size == 4 */ 90 sizep = (char*)&size; 91 if (bigendian()) 92 sizep += sizeof(u64) - hdr_sz; 93 94 if (hdr_sz && pwrite(output_fd, sizep, hdr_sz, hdr_pos) < 0) { 95 pr_debug("writing file size failed\n"); 96 goto out; 97 } 98 99 err = 0; 100 out: 101 close(fd); 102 return err; 103 } 104 105 static int record_header_files(void) 106 { 107 char *path; 108 struct stat st; 109 int err = -EIO; 110 111 path = get_tracing_file("events/header_page"); 112 if (!path) { 113 pr_debug("can't get tracing/events/header_page"); 114 return -ENOMEM; 115 } 116 117 if (stat(path, &st) < 0) { 118 pr_debug("can't read '%s'", path); 119 goto out; 120 } 121 122 if (write(output_fd, "header_page", 12) != 12) { 123 pr_debug("can't write header_page\n"); 124 goto out; 125 } 126 127 if (record_file(path, 8) < 0) { 128 pr_debug("can't record header_page file\n"); 129 goto out; 130 } 131 132 put_tracing_file(path); 133 134 path = get_tracing_file("events/header_event"); 135 if (!path) { 136 pr_debug("can't get tracing/events/header_event"); 137 err = -ENOMEM; 138 goto out; 139 } 140 141 if (stat(path, &st) < 0) { 142 pr_debug("can't read '%s'", path); 143 goto out; 144 } 145 146 if (write(output_fd, "header_event", 13) != 13) { 147 pr_debug("can't write header_event\n"); 148 goto out; 149 } 150 151 if (record_file(path, 8) < 0) { 152 pr_debug("can't record header_event file\n"); 153 goto out; 154 } 155 156 err = 0; 157 out: 158 put_tracing_file(path); 159 return err; 160 } 161 162 static bool name_in_tp_list(char *sys, struct tracepoint_path *tps) 163 { 164 while (tps) { 165 if (!strcmp(sys, tps->name)) 166 return true; 167 tps = tps->next; 168 } 169 170 return false; 171 } 172 173 static int copy_event_system(const char *sys, struct tracepoint_path *tps) 174 { 175 struct dirent *dent; 176 struct stat st; 177 char *format; 178 DIR *dir; 179 int count = 0; 180 int ret; 181 int err; 182 183 dir = opendir(sys); 184 if (!dir) { 185 pr_debug("can't read directory '%s'", sys); 186 return -errno; 187 } 188 189 while ((dent = readdir(dir))) { 190 if (dent->d_type != DT_DIR || 191 strcmp(dent->d_name, ".") == 0 || 192 strcmp(dent->d_name, "..") == 0 || 193 !name_in_tp_list(dent->d_name, tps)) 194 continue; 195 if (asprintf(&format, "%s/%s/format", sys, dent->d_name) < 0) { 196 err = -ENOMEM; 197 goto out; 198 } 199 ret = stat(format, &st); 200 free(format); 201 if (ret < 0) 202 continue; 203 count++; 204 } 205 206 if (write(output_fd, &count, 4) != 4) { 207 err = -EIO; 208 pr_debug("can't write count\n"); 209 goto out; 210 } 211 212 rewinddir(dir); 213 while ((dent = readdir(dir))) { 214 if (dent->d_type != DT_DIR || 215 strcmp(dent->d_name, ".") == 0 || 216 strcmp(dent->d_name, "..") == 0 || 217 !name_in_tp_list(dent->d_name, tps)) 218 continue; 219 if (asprintf(&format, "%s/%s/format", sys, dent->d_name) < 0) { 220 err = -ENOMEM; 221 goto out; 222 } 223 ret = stat(format, &st); 224 225 if (ret >= 0) { 226 err = record_file(format, 8); 227 if (err) { 228 free(format); 229 goto out; 230 } 231 } 232 free(format); 233 } 234 err = 0; 235 out: 236 closedir(dir); 237 return err; 238 } 239 240 static int record_ftrace_files(struct tracepoint_path *tps) 241 { 242 char *path; 243 int ret; 244 245 path = get_tracing_file("events/ftrace"); 246 if (!path) { 247 pr_debug("can't get tracing/events/ftrace"); 248 return -ENOMEM; 249 } 250 251 ret = copy_event_system(path, tps); 252 253 put_tracing_file(path); 254 255 return ret; 256 } 257 258 static bool system_in_tp_list(char *sys, struct tracepoint_path *tps) 259 { 260 while (tps) { 261 if (!strcmp(sys, tps->system)) 262 return true; 263 tps = tps->next; 264 } 265 266 return false; 267 } 268 269 static int record_event_files(struct tracepoint_path *tps) 270 { 271 struct dirent *dent; 272 struct stat st; 273 char *path; 274 char *sys; 275 DIR *dir; 276 int count = 0; 277 int ret; 278 int err; 279 280 path = get_tracing_file("events"); 281 if (!path) { 282 pr_debug("can't get tracing/events"); 283 return -ENOMEM; 284 } 285 286 dir = opendir(path); 287 if (!dir) { 288 err = -errno; 289 pr_debug("can't read directory '%s'", path); 290 goto out; 291 } 292 293 while ((dent = readdir(dir))) { 294 if (dent->d_type != DT_DIR || 295 strcmp(dent->d_name, ".") == 0 || 296 strcmp(dent->d_name, "..") == 0 || 297 strcmp(dent->d_name, "ftrace") == 0 || 298 !system_in_tp_list(dent->d_name, tps)) 299 continue; 300 count++; 301 } 302 303 if (write(output_fd, &count, 4) != 4) { 304 err = -EIO; 305 pr_debug("can't write count\n"); 306 goto out; 307 } 308 309 rewinddir(dir); 310 while ((dent = readdir(dir))) { 311 if (dent->d_type != DT_DIR || 312 strcmp(dent->d_name, ".") == 0 || 313 strcmp(dent->d_name, "..") == 0 || 314 strcmp(dent->d_name, "ftrace") == 0 || 315 !system_in_tp_list(dent->d_name, tps)) 316 continue; 317 if (asprintf(&sys, "%s/%s", path, dent->d_name) < 0) { 318 err = -ENOMEM; 319 goto out; 320 } 321 ret = stat(sys, &st); 322 if (ret >= 0) { 323 ssize_t size = strlen(dent->d_name) + 1; 324 325 if (write(output_fd, dent->d_name, size) != size || 326 copy_event_system(sys, tps) < 0) { 327 err = -EIO; 328 free(sys); 329 goto out; 330 } 331 } 332 free(sys); 333 } 334 err = 0; 335 out: 336 closedir(dir); 337 put_tracing_file(path); 338 339 return err; 340 } 341 342 static int record_proc_kallsyms(void) 343 { 344 unsigned long long size = 0; 345 /* 346 * Just to keep older perf.data file parsers happy, record a zero 347 * sized kallsyms file, i.e. do the same thing that was done when 348 * /proc/kallsyms (or something specified via --kallsyms, in a 349 * different path) couldn't be read. 350 */ 351 return write(output_fd, &size, 4) != 4 ? -EIO : 0; 352 } 353 354 static int record_ftrace_printk(void) 355 { 356 unsigned int size; 357 char *path; 358 struct stat st; 359 int ret, err = 0; 360 361 path = get_tracing_file("printk_formats"); 362 if (!path) { 363 pr_debug("can't get tracing/printk_formats"); 364 return -ENOMEM; 365 } 366 367 ret = stat(path, &st); 368 if (ret < 0) { 369 /* not found */ 370 size = 0; 371 if (write(output_fd, &size, 4) != 4) 372 err = -EIO; 373 goto out; 374 } 375 err = record_file(path, 4); 376 377 out: 378 put_tracing_file(path); 379 return err; 380 } 381 382 static void 383 put_tracepoints_path(struct tracepoint_path *tps) 384 { 385 while (tps) { 386 struct tracepoint_path *t = tps; 387 388 tps = tps->next; 389 zfree(&t->name); 390 zfree(&t->system); 391 free(t); 392 } 393 } 394 395 static struct tracepoint_path * 396 get_tracepoints_path(struct list_head *pattrs) 397 { 398 struct tracepoint_path path, *ppath = &path; 399 struct perf_evsel *pos; 400 int nr_tracepoints = 0; 401 402 list_for_each_entry(pos, pattrs, node) { 403 if (pos->attr.type != PERF_TYPE_TRACEPOINT) 404 continue; 405 ++nr_tracepoints; 406 407 if (pos->name) { 408 ppath->next = tracepoint_name_to_path(pos->name); 409 if (ppath->next) 410 goto next; 411 412 if (strchr(pos->name, ':') == NULL) 413 goto try_id; 414 415 goto error; 416 } 417 418 try_id: 419 ppath->next = tracepoint_id_to_path(pos->attr.config); 420 if (!ppath->next) { 421 error: 422 pr_debug("No memory to alloc tracepoints list\n"); 423 put_tracepoints_path(&path); 424 return NULL; 425 } 426 next: 427 ppath = ppath->next; 428 } 429 430 return nr_tracepoints > 0 ? path.next : NULL; 431 } 432 433 bool have_tracepoints(struct list_head *pattrs) 434 { 435 struct perf_evsel *pos; 436 437 list_for_each_entry(pos, pattrs, node) 438 if (pos->attr.type == PERF_TYPE_TRACEPOINT) 439 return true; 440 441 return false; 442 } 443 444 static int tracing_data_header(void) 445 { 446 char buf[20]; 447 ssize_t size; 448 449 /* just guessing this is someone's birthday.. ;) */ 450 buf[0] = 23; 451 buf[1] = 8; 452 buf[2] = 68; 453 memcpy(buf + 3, "tracing", 7); 454 455 if (write(output_fd, buf, 10) != 10) 456 return -1; 457 458 size = strlen(VERSION) + 1; 459 if (write(output_fd, VERSION, size) != size) 460 return -1; 461 462 /* save endian */ 463 if (bigendian()) 464 buf[0] = 1; 465 else 466 buf[0] = 0; 467 468 if (write(output_fd, buf, 1) != 1) 469 return -1; 470 471 /* save size of long */ 472 buf[0] = sizeof(long); 473 if (write(output_fd, buf, 1) != 1) 474 return -1; 475 476 /* save page_size */ 477 if (write(output_fd, &page_size, 4) != 4) 478 return -1; 479 480 return 0; 481 } 482 483 struct tracing_data *tracing_data_get(struct list_head *pattrs, 484 int fd, bool temp) 485 { 486 struct tracepoint_path *tps; 487 struct tracing_data *tdata; 488 int err; 489 490 output_fd = fd; 491 492 tps = get_tracepoints_path(pattrs); 493 if (!tps) 494 return NULL; 495 496 tdata = malloc(sizeof(*tdata)); 497 if (!tdata) 498 return NULL; 499 500 tdata->temp = temp; 501 tdata->size = 0; 502 503 if (temp) { 504 int temp_fd; 505 506 snprintf(tdata->temp_file, sizeof(tdata->temp_file), 507 "/tmp/perf-XXXXXX"); 508 if (!mkstemp(tdata->temp_file)) { 509 pr_debug("Can't make temp file"); 510 return NULL; 511 } 512 513 temp_fd = open(tdata->temp_file, O_RDWR); 514 if (temp_fd < 0) { 515 pr_debug("Can't read '%s'", tdata->temp_file); 516 return NULL; 517 } 518 519 /* 520 * Set the temp file the default output, so all the 521 * tracing data are stored into it. 522 */ 523 output_fd = temp_fd; 524 } 525 526 err = tracing_data_header(); 527 if (err) 528 goto out; 529 err = record_header_files(); 530 if (err) 531 goto out; 532 err = record_ftrace_files(tps); 533 if (err) 534 goto out; 535 err = record_event_files(tps); 536 if (err) 537 goto out; 538 err = record_proc_kallsyms(); 539 if (err) 540 goto out; 541 err = record_ftrace_printk(); 542 543 out: 544 /* 545 * All tracing data are stored by now, we can restore 546 * the default output file in case we used temp file. 547 */ 548 if (temp) { 549 tdata->size = lseek(output_fd, 0, SEEK_CUR); 550 close(output_fd); 551 output_fd = fd; 552 } 553 554 if (err) 555 zfree(&tdata); 556 557 put_tracepoints_path(tps); 558 return tdata; 559 } 560 561 int tracing_data_put(struct tracing_data *tdata) 562 { 563 int err = 0; 564 565 if (tdata->temp) { 566 err = record_file(tdata->temp_file, 0); 567 unlink(tdata->temp_file); 568 } 569 570 free(tdata); 571 return err; 572 } 573 574 int read_tracing_data(int fd, struct list_head *pattrs) 575 { 576 int err; 577 struct tracing_data *tdata; 578 579 /* 580 * We work over the real file, so we can write data 581 * directly, no temp file is needed. 582 */ 583 tdata = tracing_data_get(pattrs, fd, false); 584 if (!tdata) 585 return -ENOMEM; 586 587 err = tracing_data_put(tdata); 588 return err; 589 } 590