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