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