Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (C) 2008,2009, Steven Rostedt <srostedt@redhat.com>
0004  */
0005 #include <dirent.h>
0006 #include <mntent.h>
0007 #include <stdio.h>
0008 #include <stdlib.h>
0009 #include <string.h>
0010 #include <stdarg.h>
0011 #include <sys/types.h>
0012 #include <sys/stat.h>
0013 #include <sys/wait.h>
0014 #include <fcntl.h>
0015 #include <unistd.h>
0016 #include <errno.h>
0017 #include <stdbool.h>
0018 #include <linux/list.h>
0019 #include <linux/kernel.h>
0020 #include <linux/zalloc.h>
0021 #include <internal/lib.h> // page_size
0022 #include <sys/param.h>
0023 
0024 #include "trace-event.h"
0025 #include "tracepoint.h"
0026 #include <api/fs/tracing_path.h>
0027 #include "evsel.h"
0028 #include "debug.h"
0029 
0030 #define VERSION "0.6"
0031 #define MAX_EVENT_LENGTH 512
0032 
0033 static int output_fd;
0034 
0035 struct tracepoint_path {
0036     char *system;
0037     char *name;
0038     struct tracepoint_path *next;
0039 };
0040 
0041 int bigendian(void)
0042 {
0043     unsigned char str[] = { 0x1, 0x2, 0x3, 0x4, 0x0, 0x0, 0x0, 0x0};
0044     unsigned int *ptr;
0045 
0046     ptr = (unsigned int *)(void *)str;
0047     return *ptr == 0x01020304;
0048 }
0049 
0050 /* unfortunately, you can not stat debugfs or proc files for size */
0051 static int record_file(const char *file, ssize_t hdr_sz)
0052 {
0053     unsigned long long size = 0;
0054     char buf[BUFSIZ], *sizep;
0055     off_t hdr_pos = lseek(output_fd, 0, SEEK_CUR);
0056     int r, fd;
0057     int err = -EIO;
0058 
0059     fd = open(file, O_RDONLY);
0060     if (fd < 0) {
0061         pr_debug("Can't read '%s'", file);
0062         return -errno;
0063     }
0064 
0065     /* put in zeros for file size, then fill true size later */
0066     if (hdr_sz) {
0067         if (write(output_fd, &size, hdr_sz) != hdr_sz)
0068             goto out;
0069     }
0070 
0071     do {
0072         r = read(fd, buf, BUFSIZ);
0073         if (r > 0) {
0074             size += r;
0075             if (write(output_fd, buf, r) != r)
0076                 goto out;
0077         }
0078     } while (r > 0);
0079 
0080     /* ugh, handle big-endian hdr_size == 4 */
0081     sizep = (char*)&size;
0082     if (bigendian())
0083         sizep += sizeof(u64) - hdr_sz;
0084 
0085     if (hdr_sz && pwrite(output_fd, sizep, hdr_sz, hdr_pos) < 0) {
0086         pr_debug("writing file size failed\n");
0087         goto out;
0088     }
0089 
0090     err = 0;
0091 out:
0092     close(fd);
0093     return err;
0094 }
0095 
0096 static int record_header_files(void)
0097 {
0098     char *path = get_events_file("header_page");
0099     struct stat st;
0100     int err = -EIO;
0101 
0102     if (!path) {
0103         pr_debug("can't get tracing/events/header_page");
0104         return -ENOMEM;
0105     }
0106 
0107     if (stat(path, &st) < 0) {
0108         pr_debug("can't read '%s'", path);
0109         goto out;
0110     }
0111 
0112     if (write(output_fd, "header_page", 12) != 12) {
0113         pr_debug("can't write header_page\n");
0114         goto out;
0115     }
0116 
0117     if (record_file(path, 8) < 0) {
0118         pr_debug("can't record header_page file\n");
0119         goto out;
0120     }
0121 
0122     put_events_file(path);
0123 
0124     path = get_events_file("header_event");
0125     if (!path) {
0126         pr_debug("can't get tracing/events/header_event");
0127         err = -ENOMEM;
0128         goto out;
0129     }
0130 
0131     if (stat(path, &st) < 0) {
0132         pr_debug("can't read '%s'", path);
0133         goto out;
0134     }
0135 
0136     if (write(output_fd, "header_event", 13) != 13) {
0137         pr_debug("can't write header_event\n");
0138         goto out;
0139     }
0140 
0141     if (record_file(path, 8) < 0) {
0142         pr_debug("can't record header_event file\n");
0143         goto out;
0144     }
0145 
0146     err = 0;
0147 out:
0148     put_events_file(path);
0149     return err;
0150 }
0151 
0152 static bool name_in_tp_list(char *sys, struct tracepoint_path *tps)
0153 {
0154     while (tps) {
0155         if (!strcmp(sys, tps->name))
0156             return true;
0157         tps = tps->next;
0158     }
0159 
0160     return false;
0161 }
0162 
0163 #define for_each_event_tps(dir, dent, tps)          \
0164     while ((dent = readdir(dir)))               \
0165         if (dent->d_type == DT_DIR &&           \
0166             (strcmp(dent->d_name, ".")) &&      \
0167             (strcmp(dent->d_name, "..")))       \
0168 
0169 static int copy_event_system(const char *sys, struct tracepoint_path *tps)
0170 {
0171     struct dirent *dent;
0172     struct stat st;
0173     char *format;
0174     DIR *dir;
0175     int count = 0;
0176     int ret;
0177     int err;
0178 
0179     dir = opendir(sys);
0180     if (!dir) {
0181         pr_debug("can't read directory '%s'", sys);
0182         return -errno;
0183     }
0184 
0185     for_each_event_tps(dir, dent, tps) {
0186         if (!name_in_tp_list(dent->d_name, tps))
0187             continue;
0188 
0189         if (asprintf(&format, "%s/%s/format", sys, dent->d_name) < 0) {
0190             err = -ENOMEM;
0191             goto out;
0192         }
0193         ret = stat(format, &st);
0194         free(format);
0195         if (ret < 0)
0196             continue;
0197         count++;
0198     }
0199 
0200     if (write(output_fd, &count, 4) != 4) {
0201         err = -EIO;
0202         pr_debug("can't write count\n");
0203         goto out;
0204     }
0205 
0206     rewinddir(dir);
0207     for_each_event_tps(dir, dent, tps) {
0208         if (!name_in_tp_list(dent->d_name, tps))
0209             continue;
0210 
0211         if (asprintf(&format, "%s/%s/format", sys, dent->d_name) < 0) {
0212             err = -ENOMEM;
0213             goto out;
0214         }
0215         ret = stat(format, &st);
0216 
0217         if (ret >= 0) {
0218             err = record_file(format, 8);
0219             if (err) {
0220                 free(format);
0221                 goto out;
0222             }
0223         }
0224         free(format);
0225     }
0226     err = 0;
0227 out:
0228     closedir(dir);
0229     return err;
0230 }
0231 
0232 static int record_ftrace_files(struct tracepoint_path *tps)
0233 {
0234     char *path;
0235     int ret;
0236 
0237     path = get_events_file("ftrace");
0238     if (!path) {
0239         pr_debug("can't get tracing/events/ftrace");
0240         return -ENOMEM;
0241     }
0242 
0243     ret = copy_event_system(path, tps);
0244 
0245     put_tracing_file(path);
0246 
0247     return ret;
0248 }
0249 
0250 static bool system_in_tp_list(char *sys, struct tracepoint_path *tps)
0251 {
0252     while (tps) {
0253         if (!strcmp(sys, tps->system))
0254             return true;
0255         tps = tps->next;
0256     }
0257 
0258     return false;
0259 }
0260 
0261 static int record_event_files(struct tracepoint_path *tps)
0262 {
0263     struct dirent *dent;
0264     struct stat st;
0265     char *path;
0266     char *sys;
0267     DIR *dir;
0268     int count = 0;
0269     int ret;
0270     int err;
0271 
0272     path = get_tracing_file("events");
0273     if (!path) {
0274         pr_debug("can't get tracing/events");
0275         return -ENOMEM;
0276     }
0277 
0278     dir = opendir(path);
0279     if (!dir) {
0280         err = -errno;
0281         pr_debug("can't read directory '%s'", path);
0282         goto out;
0283     }
0284 
0285     for_each_event_tps(dir, dent, tps) {
0286         if (strcmp(dent->d_name, "ftrace") == 0 ||
0287             !system_in_tp_list(dent->d_name, tps))
0288             continue;
0289 
0290         count++;
0291     }
0292 
0293     if (write(output_fd, &count, 4) != 4) {
0294         err = -EIO;
0295         pr_debug("can't write count\n");
0296         goto out;
0297     }
0298 
0299     rewinddir(dir);
0300     for_each_event_tps(dir, dent, tps) {
0301         if (strcmp(dent->d_name, "ftrace") == 0 ||
0302             !system_in_tp_list(dent->d_name, tps))
0303             continue;
0304 
0305         if (asprintf(&sys, "%s/%s", path, dent->d_name) < 0) {
0306             err = -ENOMEM;
0307             goto out;
0308         }
0309         ret = stat(sys, &st);
0310         if (ret >= 0) {
0311             ssize_t size = strlen(dent->d_name) + 1;
0312 
0313             if (write(output_fd, dent->d_name, size) != size ||
0314                 copy_event_system(sys, tps) < 0) {
0315                 err = -EIO;
0316                 free(sys);
0317                 goto out;
0318             }
0319         }
0320         free(sys);
0321     }
0322     err = 0;
0323 out:
0324     closedir(dir);
0325     put_tracing_file(path);
0326 
0327     return err;
0328 }
0329 
0330 static int record_proc_kallsyms(void)
0331 {
0332     unsigned long long size = 0;
0333     /*
0334      * Just to keep older perf.data file parsers happy, record a zero
0335      * sized kallsyms file, i.e. do the same thing that was done when
0336      * /proc/kallsyms (or something specified via --kallsyms, in a
0337      * different path) couldn't be read.
0338      */
0339     return write(output_fd, &size, 4) != 4 ? -EIO : 0;
0340 }
0341 
0342 static int record_ftrace_printk(void)
0343 {
0344     unsigned int size;
0345     char *path;
0346     struct stat st;
0347     int ret, err = 0;
0348 
0349     path = get_tracing_file("printk_formats");
0350     if (!path) {
0351         pr_debug("can't get tracing/printk_formats");
0352         return -ENOMEM;
0353     }
0354 
0355     ret = stat(path, &st);
0356     if (ret < 0) {
0357         /* not found */
0358         size = 0;
0359         if (write(output_fd, &size, 4) != 4)
0360             err = -EIO;
0361         goto out;
0362     }
0363     err = record_file(path, 4);
0364 
0365 out:
0366     put_tracing_file(path);
0367     return err;
0368 }
0369 
0370 static int record_saved_cmdline(void)
0371 {
0372     unsigned long long size;
0373     char *path;
0374     struct stat st;
0375     int ret, err = 0;
0376 
0377     path = get_tracing_file("saved_cmdlines");
0378     if (!path) {
0379         pr_debug("can't get tracing/saved_cmdline");
0380         return -ENOMEM;
0381     }
0382 
0383     ret = stat(path, &st);
0384     if (ret < 0) {
0385         /* not found */
0386         size = 0;
0387         if (write(output_fd, &size, 8) != 8)
0388             err = -EIO;
0389         goto out;
0390     }
0391     err = record_file(path, 8);
0392 
0393 out:
0394     put_tracing_file(path);
0395     return err;
0396 }
0397 
0398 static void
0399 put_tracepoints_path(struct tracepoint_path *tps)
0400 {
0401     while (tps) {
0402         struct tracepoint_path *t = tps;
0403 
0404         tps = tps->next;
0405         zfree(&t->name);
0406         zfree(&t->system);
0407         free(t);
0408     }
0409 }
0410 
0411 static struct tracepoint_path *tracepoint_id_to_path(u64 config)
0412 {
0413     struct tracepoint_path *path = NULL;
0414     DIR *sys_dir, *evt_dir;
0415     struct dirent *sys_dirent, *evt_dirent;
0416     char id_buf[24];
0417     int fd;
0418     u64 id;
0419     char evt_path[MAXPATHLEN];
0420     char *dir_path;
0421 
0422     sys_dir = tracing_events__opendir();
0423     if (!sys_dir)
0424         return NULL;
0425 
0426     for_each_subsystem(sys_dir, sys_dirent) {
0427         dir_path = get_events_file(sys_dirent->d_name);
0428         if (!dir_path)
0429             continue;
0430         evt_dir = opendir(dir_path);
0431         if (!evt_dir)
0432             goto next;
0433 
0434         for_each_event(dir_path, evt_dir, evt_dirent) {
0435 
0436             scnprintf(evt_path, MAXPATHLEN, "%s/%s/id", dir_path,
0437                   evt_dirent->d_name);
0438             fd = open(evt_path, O_RDONLY);
0439             if (fd < 0)
0440                 continue;
0441             if (read(fd, id_buf, sizeof(id_buf)) < 0) {
0442                 close(fd);
0443                 continue;
0444             }
0445             close(fd);
0446             id = atoll(id_buf);
0447             if (id == config) {
0448                 put_events_file(dir_path);
0449                 closedir(evt_dir);
0450                 closedir(sys_dir);
0451                 path = zalloc(sizeof(*path));
0452                 if (!path)
0453                     return NULL;
0454                 if (asprintf(&path->system, "%.*s",
0455                          MAX_EVENT_LENGTH, sys_dirent->d_name) < 0) {
0456                     free(path);
0457                     return NULL;
0458                 }
0459                 if (asprintf(&path->name, "%.*s",
0460                          MAX_EVENT_LENGTH, evt_dirent->d_name) < 0) {
0461                     zfree(&path->system);
0462                     free(path);
0463                     return NULL;
0464                 }
0465                 return path;
0466             }
0467         }
0468         closedir(evt_dir);
0469 next:
0470         put_events_file(dir_path);
0471     }
0472 
0473     closedir(sys_dir);
0474     return NULL;
0475 }
0476 
0477 static struct tracepoint_path *tracepoint_name_to_path(const char *name)
0478 {
0479     struct tracepoint_path *path = zalloc(sizeof(*path));
0480     char *str = strchr(name, ':');
0481 
0482     if (path == NULL || str == NULL) {
0483         free(path);
0484         return NULL;
0485     }
0486 
0487     path->system = strndup(name, str - name);
0488     path->name = strdup(str+1);
0489 
0490     if (path->system == NULL || path->name == NULL) {
0491         zfree(&path->system);
0492         zfree(&path->name);
0493         zfree(&path);
0494     }
0495 
0496     return path;
0497 }
0498 
0499 static struct tracepoint_path *
0500 get_tracepoints_path(struct list_head *pattrs)
0501 {
0502     struct tracepoint_path path, *ppath = &path;
0503     struct evsel *pos;
0504     int nr_tracepoints = 0;
0505 
0506     list_for_each_entry(pos, pattrs, core.node) {
0507         if (pos->core.attr.type != PERF_TYPE_TRACEPOINT)
0508             continue;
0509         ++nr_tracepoints;
0510 
0511         if (pos->name) {
0512             ppath->next = tracepoint_name_to_path(pos->name);
0513             if (ppath->next)
0514                 goto next;
0515 
0516             if (strchr(pos->name, ':') == NULL)
0517                 goto try_id;
0518 
0519             goto error;
0520         }
0521 
0522 try_id:
0523         ppath->next = tracepoint_id_to_path(pos->core.attr.config);
0524         if (!ppath->next) {
0525 error:
0526             pr_debug("No memory to alloc tracepoints list\n");
0527             put_tracepoints_path(path.next);
0528             return NULL;
0529         }
0530 next:
0531         ppath = ppath->next;
0532     }
0533 
0534     return nr_tracepoints > 0 ? path.next : NULL;
0535 }
0536 
0537 bool have_tracepoints(struct list_head *pattrs)
0538 {
0539     struct evsel *pos;
0540 
0541     list_for_each_entry(pos, pattrs, core.node)
0542         if (pos->core.attr.type == PERF_TYPE_TRACEPOINT)
0543             return true;
0544 
0545     return false;
0546 }
0547 
0548 static int tracing_data_header(void)
0549 {
0550     char buf[20];
0551     ssize_t size;
0552 
0553     /* just guessing this is someone's birthday.. ;) */
0554     buf[0] = 23;
0555     buf[1] = 8;
0556     buf[2] = 68;
0557     memcpy(buf + 3, "tracing", 7);
0558 
0559     if (write(output_fd, buf, 10) != 10)
0560         return -1;
0561 
0562     size = strlen(VERSION) + 1;
0563     if (write(output_fd, VERSION, size) != size)
0564         return -1;
0565 
0566     /* save endian */
0567     if (bigendian())
0568         buf[0] = 1;
0569     else
0570         buf[0] = 0;
0571 
0572     if (write(output_fd, buf, 1) != 1)
0573         return -1;
0574 
0575     /* save size of long */
0576     buf[0] = sizeof(long);
0577     if (write(output_fd, buf, 1) != 1)
0578         return -1;
0579 
0580     /* save page_size */
0581     if (write(output_fd, &page_size, 4) != 4)
0582         return -1;
0583 
0584     return 0;
0585 }
0586 
0587 struct tracing_data *tracing_data_get(struct list_head *pattrs,
0588                       int fd, bool temp)
0589 {
0590     struct tracepoint_path *tps;
0591     struct tracing_data *tdata;
0592     int err;
0593 
0594     output_fd = fd;
0595 
0596     tps = get_tracepoints_path(pattrs);
0597     if (!tps)
0598         return NULL;
0599 
0600     tdata = malloc(sizeof(*tdata));
0601     if (!tdata)
0602         return NULL;
0603 
0604     tdata->temp = temp;
0605     tdata->size = 0;
0606 
0607     if (temp) {
0608         int temp_fd;
0609 
0610         snprintf(tdata->temp_file, sizeof(tdata->temp_file),
0611              "/tmp/perf-XXXXXX");
0612         if (!mkstemp(tdata->temp_file)) {
0613             pr_debug("Can't make temp file");
0614             free(tdata);
0615             return NULL;
0616         }
0617 
0618         temp_fd = open(tdata->temp_file, O_RDWR);
0619         if (temp_fd < 0) {
0620             pr_debug("Can't read '%s'", tdata->temp_file);
0621             free(tdata);
0622             return NULL;
0623         }
0624 
0625         /*
0626          * Set the temp file the default output, so all the
0627          * tracing data are stored into it.
0628          */
0629         output_fd = temp_fd;
0630     }
0631 
0632     err = tracing_data_header();
0633     if (err)
0634         goto out;
0635     err = record_header_files();
0636     if (err)
0637         goto out;
0638     err = record_ftrace_files(tps);
0639     if (err)
0640         goto out;
0641     err = record_event_files(tps);
0642     if (err)
0643         goto out;
0644     err = record_proc_kallsyms();
0645     if (err)
0646         goto out;
0647     err = record_ftrace_printk();
0648     if (err)
0649         goto out;
0650     err = record_saved_cmdline();
0651 
0652 out:
0653     /*
0654      * All tracing data are stored by now, we can restore
0655      * the default output file in case we used temp file.
0656      */
0657     if (temp) {
0658         tdata->size = lseek(output_fd, 0, SEEK_CUR);
0659         close(output_fd);
0660         output_fd = fd;
0661     }
0662 
0663     if (err)
0664         zfree(&tdata);
0665 
0666     put_tracepoints_path(tps);
0667     return tdata;
0668 }
0669 
0670 int tracing_data_put(struct tracing_data *tdata)
0671 {
0672     int err = 0;
0673 
0674     if (tdata->temp) {
0675         err = record_file(tdata->temp_file, 0);
0676         unlink(tdata->temp_file);
0677     }
0678 
0679     free(tdata);
0680     return err;
0681 }
0682 
0683 int read_tracing_data(int fd, struct list_head *pattrs)
0684 {
0685     int err;
0686     struct tracing_data *tdata;
0687 
0688     /*
0689      * We work over the real file, so we can write data
0690      * directly, no temp file is needed.
0691      */
0692     tdata = tracing_data_get(pattrs, fd, false);
0693     if (!tdata)
0694         return -ENOMEM;
0695 
0696     err = tracing_data_put(tdata);
0697     return err;
0698 }