0001
0002
0003
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
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
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
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
0335
0336
0337
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
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
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
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
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
0576 buf[0] = sizeof(long);
0577 if (write(output_fd, buf, 1) != 1)
0578 return -1;
0579
0580
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
0627
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
0655
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
0690
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 }