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 write_or_die(&size, hdr_sz); 200 201 do { 202 r = read(fd, buf, BUFSIZ); 203 if (r > 0) { 204 size += r; 205 write_or_die(buf, r); 206 } 207 } while (r > 0); 208 close(fd); 209 210 /* ugh, handle big-endian hdr_size == 4 */ 211 sizep = (char*)&size; 212 if (bigendian()) 213 sizep += sizeof(u64) - hdr_sz; 214 215 if (pwrite(output_fd, sizep, hdr_sz, hdr_pos) < 0) 216 die("writing to %s", output_file); 217 } 218 219 static void read_header_files(void) 220 { 221 char *path; 222 struct stat st; 223 224 path = get_tracing_file("events/header_page"); 225 if (stat(path, &st) < 0) 226 die("can't read '%s'", path); 227 228 write_or_die("header_page", 12); 229 record_file(path, 8); 230 put_tracing_file(path); 231 232 path = get_tracing_file("events/header_event"); 233 if (stat(path, &st) < 0) 234 die("can't read '%s'", path); 235 236 write_or_die("header_event", 13); 237 record_file(path, 8); 238 put_tracing_file(path); 239 } 240 241 static bool name_in_tp_list(char *sys, struct tracepoint_path *tps) 242 { 243 while (tps) { 244 if (!strcmp(sys, tps->name)) 245 return true; 246 tps = tps->next; 247 } 248 249 return false; 250 } 251 252 static void copy_event_system(const char *sys, struct tracepoint_path *tps) 253 { 254 struct dirent *dent; 255 struct stat st; 256 char *format; 257 DIR *dir; 258 int count = 0; 259 int ret; 260 261 dir = opendir(sys); 262 if (!dir) 263 die("can't read directory '%s'", sys); 264 265 while ((dent = readdir(dir))) { 266 if (dent->d_type != DT_DIR || 267 strcmp(dent->d_name, ".") == 0 || 268 strcmp(dent->d_name, "..") == 0 || 269 !name_in_tp_list(dent->d_name, tps)) 270 continue; 271 format = malloc_or_die(strlen(sys) + strlen(dent->d_name) + 10); 272 sprintf(format, "%s/%s/format", sys, dent->d_name); 273 ret = stat(format, &st); 274 free(format); 275 if (ret < 0) 276 continue; 277 count++; 278 } 279 280 write_or_die(&count, 4); 281 282 rewinddir(dir); 283 while ((dent = readdir(dir))) { 284 if (dent->d_type != DT_DIR || 285 strcmp(dent->d_name, ".") == 0 || 286 strcmp(dent->d_name, "..") == 0 || 287 !name_in_tp_list(dent->d_name, tps)) 288 continue; 289 format = malloc_or_die(strlen(sys) + strlen(dent->d_name) + 10); 290 sprintf(format, "%s/%s/format", sys, dent->d_name); 291 ret = stat(format, &st); 292 293 if (ret >= 0) 294 record_file(format, 8); 295 296 free(format); 297 } 298 closedir(dir); 299 } 300 301 static void read_ftrace_files(struct tracepoint_path *tps) 302 { 303 char *path; 304 305 path = get_tracing_file("events/ftrace"); 306 307 copy_event_system(path, tps); 308 309 put_tracing_file(path); 310 } 311 312 static bool system_in_tp_list(char *sys, struct tracepoint_path *tps) 313 { 314 while (tps) { 315 if (!strcmp(sys, tps->system)) 316 return true; 317 tps = tps->next; 318 } 319 320 return false; 321 } 322 323 static void read_event_files(struct tracepoint_path *tps) 324 { 325 struct dirent *dent; 326 struct stat st; 327 char *path; 328 char *sys; 329 DIR *dir; 330 int count = 0; 331 int ret; 332 333 path = get_tracing_file("events"); 334 335 dir = opendir(path); 336 if (!dir) 337 die("can't read directory '%s'", path); 338 339 while ((dent = readdir(dir))) { 340 if (dent->d_type != DT_DIR || 341 strcmp(dent->d_name, ".") == 0 || 342 strcmp(dent->d_name, "..") == 0 || 343 strcmp(dent->d_name, "ftrace") == 0 || 344 !system_in_tp_list(dent->d_name, tps)) 345 continue; 346 count++; 347 } 348 349 write_or_die(&count, 4); 350 351 rewinddir(dir); 352 while ((dent = readdir(dir))) { 353 if (dent->d_type != DT_DIR || 354 strcmp(dent->d_name, ".") == 0 || 355 strcmp(dent->d_name, "..") == 0 || 356 strcmp(dent->d_name, "ftrace") == 0 || 357 !system_in_tp_list(dent->d_name, tps)) 358 continue; 359 sys = malloc_or_die(strlen(path) + strlen(dent->d_name) + 2); 360 sprintf(sys, "%s/%s", path, dent->d_name); 361 ret = stat(sys, &st); 362 if (ret >= 0) { 363 write_or_die(dent->d_name, strlen(dent->d_name) + 1); 364 copy_event_system(sys, tps); 365 } 366 free(sys); 367 } 368 369 closedir(dir); 370 put_tracing_file(path); 371 } 372 373 static void read_proc_kallsyms(void) 374 { 375 unsigned int size; 376 const char *path = "/proc/kallsyms"; 377 struct stat st; 378 int ret; 379 380 ret = stat(path, &st); 381 if (ret < 0) { 382 /* not found */ 383 size = 0; 384 write_or_die(&size, 4); 385 return; 386 } 387 record_file(path, 4); 388 } 389 390 static void read_ftrace_printk(void) 391 { 392 unsigned int size; 393 char *path; 394 struct stat st; 395 int ret; 396 397 path = get_tracing_file("printk_formats"); 398 ret = stat(path, &st); 399 if (ret < 0) { 400 /* not found */ 401 size = 0; 402 write_or_die(&size, 4); 403 goto out; 404 } 405 record_file(path, 4); 406 407 out: 408 put_tracing_file(path); 409 } 410 411 static struct tracepoint_path * 412 get_tracepoints_path(struct list_head *pattrs) 413 { 414 struct tracepoint_path path, *ppath = &path; 415 struct perf_evsel *pos; 416 int nr_tracepoints = 0; 417 418 list_for_each_entry(pos, pattrs, node) { 419 if (pos->attr.type != PERF_TYPE_TRACEPOINT) 420 continue; 421 ++nr_tracepoints; 422 ppath->next = tracepoint_id_to_path(pos->attr.config); 423 if (!ppath->next) 424 die("%s\n", "No memory to alloc tracepoints list"); 425 ppath = ppath->next; 426 } 427 428 return nr_tracepoints > 0 ? path.next : NULL; 429 } 430 431 bool have_tracepoints(struct list_head *pattrs) 432 { 433 struct perf_evsel *pos; 434 435 list_for_each_entry(pos, pattrs, node) 436 if (pos->attr.type == PERF_TYPE_TRACEPOINT) 437 return true; 438 439 return false; 440 } 441 442 int read_tracing_data(int fd, struct list_head *pattrs) 443 { 444 char buf[BUFSIZ]; 445 struct tracepoint_path *tps = get_tracepoints_path(pattrs); 446 447 /* 448 * What? No tracepoints? No sense writing anything here, bail out. 449 */ 450 if (tps == NULL) 451 return -1; 452 453 output_fd = fd; 454 455 buf[0] = 23; 456 buf[1] = 8; 457 buf[2] = 68; 458 memcpy(buf + 3, "tracing", 7); 459 460 write_or_die(buf, 10); 461 462 write_or_die(VERSION, strlen(VERSION) + 1); 463 464 /* save endian */ 465 if (bigendian()) 466 buf[0] = 1; 467 else 468 buf[0] = 0; 469 470 write_or_die(buf, 1); 471 472 /* save size of long */ 473 buf[0] = sizeof(long); 474 write_or_die(buf, 1); 475 476 /* save page_size */ 477 page_size = sysconf(_SC_PAGESIZE); 478 write_or_die(&page_size, 4); 479 480 read_header_files(); 481 read_ftrace_files(tps); 482 read_event_files(tps); 483 read_proc_kallsyms(); 484 read_ftrace_printk(); 485 486 return 0; 487 } 488 489 ssize_t read_tracing_data_size(int fd, struct list_head *pattrs) 490 { 491 ssize_t size; 492 int err = 0; 493 494 calc_data_size = 1; 495 err = read_tracing_data(fd, pattrs); 496 size = calc_data_size - 1; 497 calc_data_size = 0; 498 499 if (err < 0) 500 return err; 501 502 return size; 503 } 504