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