linux/tools/perf/util/trace-event-info.c
<<
>>
Prefs
   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
  58unsigned int page_size;
  59
  60static const char *output_file = "trace.info";
  61static int output_fd;
  62
  63struct event_list {
  64        struct event_list *next;
  65        const char *event;
  66};
  67
  68struct events {
  69        struct events *sibling;
  70        struct events *children;
  71        struct events *next;
  72        char *name;
  73};
  74
  75
  76
  77static void die(const char *fmt, ...)
  78{
  79        va_list ap;
  80        int ret = errno;
  81
  82        if (errno)
  83                perror("perf");
  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
  96void *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
 106static 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 */
 120static 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
 139static 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
 154static void put_tracing_file(char *file)
 155{
 156        free(file);
 157}
 158
 159static ssize_t calc_data_size;
 160
 161static 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
 177int 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 */
 187static 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
 220static 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
 242static 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
 253static 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
 302static 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
 313static 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
 324static 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
 374static 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
 391static 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
 408out:
 409        put_tracing_file(path);
 410}
 411
 412static struct tracepoint_path *
 413get_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
 432static void
 433put_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
 445bool 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
 456static 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
 487struct 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
 543void 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
 553int 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