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#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
  53unsigned int page_size;
  54
  55static const char *output_file = "trace.info";
  56static int output_fd;
  57
  58struct event_list {
  59        struct event_list *next;
  60        const char *event;
  61};
  62
  63struct events {
  64        struct events *sibling;
  65        struct events *children;
  66        struct events *next;
  67        char *name;
  68};
  69
  70
  71void *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
  81static 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 */
  95static 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
 114static 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
 129static void put_tracing_file(char *file)
 130{
 131        free(file);
 132}
 133
 134static ssize_t calc_data_size;
 135
 136static 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
 152int 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 */
 162static 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
 195static 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
 217static 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
 228static 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
 277static 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
 288static 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
 299static 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
 349static 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
 366static 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
 383out:
 384        put_tracing_file(path);
 385}
 386
 387static struct tracepoint_path *
 388get_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
 407static void
 408put_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
 420bool 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
 431static 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
 462struct 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
 518void 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
 528int 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
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.