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