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