linux/tools/perf/builtin-record.c
<<
>>
Prefs
   1/*
   2 * builtin-record.c
   3 *
   4 * Builtin record command: Record the profile of a workload
   5 * (or a CPU, or a PID) into the perf.data output file - for
   6 * later analysis via perf report.
   7 */
   8#include "builtin.h"
   9
  10#include "perf.h"
  11
  12#include "util/util.h"
  13#include "util/parse-options.h"
  14#include "util/parse-events.h"
  15#include "util/string.h"
  16
  17#include "util/header.h"
  18#include "util/event.h"
  19#include "util/debug.h"
  20#include "util/trace-event.h"
  21
  22#include <unistd.h>
  23#include <sched.h>
  24
  25#define ALIGN(x, a)             __ALIGN_MASK(x, (typeof(x))(a)-1)
  26#define __ALIGN_MASK(x, mask)   (((x)+(mask))&~(mask))
  27
  28static int                      fd[MAX_NR_CPUS][MAX_COUNTERS];
  29
  30static long                     default_interval                = 100000;
  31
  32static int                      nr_cpus                         = 0;
  33static unsigned int             page_size;
  34static unsigned int             mmap_pages                      = 128;
  35static int                      freq                            = 0;
  36static int                      output;
  37static const char               *output_name                    = "perf.data";
  38static int                      group                           = 0;
  39static unsigned int             realtime_prio                   = 0;
  40static int                      raw_samples                     = 0;
  41static int                      system_wide                     = 0;
  42static int                      profile_cpu                     = -1;
  43static pid_t                    target_pid                      = -1;
  44static pid_t                    child_pid                       = -1;
  45static int                      inherit                         = 1;
  46static int                      force                           = 0;
  47static int                      append_file                     = 0;
  48static int                      call_graph                      = 0;
  49static int                      inherit_stat                    = 0;
  50static int                      no_samples                      = 0;
  51static int                      sample_address                  = 0;
  52static int                      multiplex                       = 0;
  53static int                      multiplex_fd                    = -1;
  54
  55static long                     samples;
  56static struct timeval           last_read;
  57static struct timeval           this_read;
  58
  59static u64                      bytes_written;
  60
  61static struct pollfd            event_array[MAX_NR_CPUS * MAX_COUNTERS];
  62
  63static int                      nr_poll;
  64static int                      nr_cpu;
  65
  66static int                      file_new = 1;
  67
  68struct perf_header              *header;
  69
  70struct mmap_data {
  71        int                     counter;
  72        void                    *base;
  73        unsigned int            mask;
  74        unsigned int            prev;
  75};
  76
  77static struct mmap_data         mmap_array[MAX_NR_CPUS][MAX_COUNTERS];
  78
  79static unsigned long mmap_read_head(struct mmap_data *md)
  80{
  81        struct perf_event_mmap_page *pc = md->base;
  82        long head;
  83
  84        head = pc->data_head;
  85        rmb();
  86
  87        return head;
  88}
  89
  90static void mmap_write_tail(struct mmap_data *md, unsigned long tail)
  91{
  92        struct perf_event_mmap_page *pc = md->base;
  93
  94        /*
  95         * ensure all reads are done before we write the tail out.
  96         */
  97        /* mb(); */
  98        pc->data_tail = tail;
  99}
 100
 101static void write_output(void *buf, size_t size)
 102{
 103        while (size) {
 104                int ret = write(output, buf, size);
 105
 106                if (ret < 0)
 107                        die("failed to write");
 108
 109                size -= ret;
 110                buf += ret;
 111
 112                bytes_written += ret;
 113        }
 114}
 115
 116static void mmap_read(struct mmap_data *md)
 117{
 118        unsigned int head = mmap_read_head(md);
 119        unsigned int old = md->prev;
 120        unsigned char *data = md->base + page_size;
 121        unsigned long size;
 122        void *buf;
 123        int diff;
 124
 125        gettimeofday(&this_read, NULL);
 126
 127        /*
 128         * If we're further behind than half the buffer, there's a chance
 129         * the writer will bite our tail and mess up the samples under us.
 130         *
 131         * If we somehow ended up ahead of the head, we got messed up.
 132         *
 133         * In either case, truncate and restart at head.
 134         */
 135        diff = head - old;
 136        if (diff < 0) {
 137                struct timeval iv;
 138                unsigned long msecs;
 139
 140                timersub(&this_read, &last_read, &iv);
 141                msecs = iv.tv_sec*1000 + iv.tv_usec/1000;
 142
 143                fprintf(stderr, "WARNING: failed to keep up with mmap data."
 144                                "  Last read %lu msecs ago.\n", msecs);
 145
 146                /*
 147                 * head points to a known good entry, start there.
 148                 */
 149                old = head;
 150        }
 151
 152        last_read = this_read;
 153
 154        if (old != head)
 155                samples++;
 156
 157        size = head - old;
 158
 159        if ((old & md->mask) + size != (head & md->mask)) {
 160                buf = &data[old & md->mask];
 161                size = md->mask + 1 - (old & md->mask);
 162                old += size;
 163
 164                write_output(buf, size);
 165        }
 166
 167        buf = &data[old & md->mask];
 168        size = head - old;
 169        old += size;
 170
 171        write_output(buf, size);
 172
 173        md->prev = old;
 174        mmap_write_tail(md, old);
 175}
 176
 177static volatile int done = 0;
 178static volatile int signr = -1;
 179
 180static void sig_handler(int sig)
 181{
 182        done = 1;
 183        signr = sig;
 184}
 185
 186static void sig_atexit(void)
 187{
 188        if (child_pid != -1)
 189                kill(child_pid, SIGTERM);
 190
 191        if (signr == -1)
 192                return;
 193
 194        signal(signr, SIG_DFL);
 195        kill(getpid(), signr);
 196}
 197
 198static pid_t pid_synthesize_comm_event(pid_t pid, int full)
 199{
 200        struct comm_event comm_ev;
 201        char filename[PATH_MAX];
 202        char bf[BUFSIZ];
 203        FILE *fp;
 204        size_t size = 0;
 205        DIR *tasks;
 206        struct dirent dirent, *next;
 207        pid_t tgid = 0;
 208
 209        snprintf(filename, sizeof(filename), "/proc/%d/status", pid);
 210
 211        fp = fopen(filename, "r");
 212        if (fp == NULL) {
 213                /*
 214                 * We raced with a task exiting - just return:
 215                 */
 216                if (verbose)
 217                        fprintf(stderr, "couldn't open %s\n", filename);
 218                return 0;
 219        }
 220
 221        memset(&comm_ev, 0, sizeof(comm_ev));
 222        while (!comm_ev.comm[0] || !comm_ev.pid) {
 223                if (fgets(bf, sizeof(bf), fp) == NULL)
 224                        goto out_failure;
 225
 226                if (memcmp(bf, "Name:", 5) == 0) {
 227                        char *name = bf + 5;
 228                        while (*name && isspace(*name))
 229                                ++name;
 230                        size = strlen(name) - 1;
 231                        memcpy(comm_ev.comm, name, size++);
 232                } else if (memcmp(bf, "Tgid:", 5) == 0) {
 233                        char *tgids = bf + 5;
 234                        while (*tgids && isspace(*tgids))
 235                                ++tgids;
 236                        tgid = comm_ev.pid = atoi(tgids);
 237                }
 238        }
 239
 240        comm_ev.header.type = PERF_RECORD_COMM;
 241        size = ALIGN(size, sizeof(u64));
 242        comm_ev.header.size = sizeof(comm_ev) - (sizeof(comm_ev.comm) - size);
 243
 244        if (!full) {
 245                comm_ev.tid = pid;
 246
 247                write_output(&comm_ev, comm_ev.header.size);
 248                goto out_fclose;
 249        }
 250
 251        snprintf(filename, sizeof(filename), "/proc/%d/task", pid);
 252
 253        tasks = opendir(filename);
 254        while (!readdir_r(tasks, &dirent, &next) && next) {
 255                char *end;
 256                pid = strtol(dirent.d_name, &end, 10);
 257                if (*end)
 258                        continue;
 259
 260                comm_ev.tid = pid;
 261
 262                write_output(&comm_ev, comm_ev.header.size);
 263        }
 264        closedir(tasks);
 265
 266out_fclose:
 267        fclose(fp);
 268        return tgid;
 269
 270out_failure:
 271        fprintf(stderr, "couldn't get COMM and pgid, malformed %s\n",
 272                filename);
 273        exit(EXIT_FAILURE);
 274}
 275
 276static void pid_synthesize_mmap_samples(pid_t pid, pid_t tgid)
 277{
 278        char filename[PATH_MAX];
 279        FILE *fp;
 280
 281        snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
 282
 283        fp = fopen(filename, "r");
 284        if (fp == NULL) {
 285                /*
 286                 * We raced with a task exiting - just return:
 287                 */
 288                if (verbose)
 289                        fprintf(stderr, "couldn't open %s\n", filename);
 290                return;
 291        }
 292        while (1) {
 293                char bf[BUFSIZ], *pbf = bf;
 294                struct mmap_event mmap_ev = {
 295                        .header = { .type = PERF_RECORD_MMAP },
 296                };
 297                int n;
 298                size_t size;
 299                if (fgets(bf, sizeof(bf), fp) == NULL)
 300                        break;
 301
 302                /* 00400000-0040c000 r-xp 00000000 fd:01 41038  /bin/cat */
 303                n = hex2u64(pbf, &mmap_ev.start);
 304                if (n < 0)
 305                        continue;
 306                pbf += n + 1;
 307                n = hex2u64(pbf, &mmap_ev.len);
 308                if (n < 0)
 309                        continue;
 310                pbf += n + 3;
 311                if (*pbf == 'x') { /* vm_exec */
 312                        char *execname = strchr(bf, '/');
 313
 314                        /* Catch VDSO */
 315                        if (execname == NULL)
 316                                execname = strstr(bf, "[vdso]");
 317
 318                        if (execname == NULL)
 319                                continue;
 320
 321                        size = strlen(execname);
 322                        execname[size - 1] = '\0'; /* Remove \n */
 323                        memcpy(mmap_ev.filename, execname, size);
 324                        size = ALIGN(size, sizeof(u64));
 325                        mmap_ev.len -= mmap_ev.start;
 326                        mmap_ev.header.size = (sizeof(mmap_ev) -
 327                                               (sizeof(mmap_ev.filename) - size));
 328                        mmap_ev.pid = tgid;
 329                        mmap_ev.tid = pid;
 330
 331                        write_output(&mmap_ev, mmap_ev.header.size);
 332                }
 333        }
 334
 335        fclose(fp);
 336}
 337
 338static void synthesize_all(void)
 339{
 340        DIR *proc;
 341        struct dirent dirent, *next;
 342
 343        proc = opendir("/proc");
 344
 345        while (!readdir_r(proc, &dirent, &next) && next) {
 346                char *end;
 347                pid_t pid, tgid;
 348
 349                pid = strtol(dirent.d_name, &end, 10);
 350                if (*end) /* only interested in proper numerical dirents */
 351                        continue;
 352
 353                tgid = pid_synthesize_comm_event(pid, 1);
 354                pid_synthesize_mmap_samples(pid, tgid);
 355        }
 356
 357        closedir(proc);
 358}
 359
 360static int group_fd;
 361
 362static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int nr)
 363{
 364        struct perf_header_attr *h_attr;
 365
 366        if (nr < header->attrs) {
 367                h_attr = header->attr[nr];
 368        } else {
 369                h_attr = perf_header_attr__new(a);
 370                perf_header__add_attr(header, h_attr);
 371        }
 372
 373        return h_attr;
 374}
 375
 376static void create_counter(int counter, int cpu, pid_t pid)
 377{
 378        struct perf_event_attr *attr = attrs + counter;
 379        struct perf_header_attr *h_attr;
 380        int track = !counter; /* only the first counter needs these */
 381        struct {
 382                u64 count;
 383                u64 time_enabled;
 384                u64 time_running;
 385                u64 id;
 386        } read_data;
 387
 388        attr->read_format       = PERF_FORMAT_TOTAL_TIME_ENABLED |
 389                                  PERF_FORMAT_TOTAL_TIME_RUNNING |
 390                                  PERF_FORMAT_ID;
 391
 392        attr->sample_type       |= PERF_SAMPLE_IP | PERF_SAMPLE_TID;
 393
 394        if (freq) {
 395                attr->sample_type       |= PERF_SAMPLE_PERIOD;
 396                attr->freq              = 1;
 397                attr->sample_freq       = freq;
 398        }
 399
 400        if (no_samples)
 401                attr->sample_freq = 0;
 402
 403        if (inherit_stat)
 404                attr->inherit_stat = 1;
 405
 406        if (sample_address)
 407                attr->sample_type       |= PERF_SAMPLE_ADDR;
 408
 409        if (call_graph)
 410                attr->sample_type       |= PERF_SAMPLE_CALLCHAIN;
 411
 412        if (raw_samples) {
 413                attr->sample_type       |= PERF_SAMPLE_TIME;
 414                attr->sample_type       |= PERF_SAMPLE_RAW;
 415                attr->sample_type       |= PERF_SAMPLE_CPU;
 416        }
 417
 418        attr->mmap              = track;
 419        attr->comm              = track;
 420        attr->inherit           = (cpu < 0) && inherit;
 421        attr->disabled          = 1;
 422
 423try_again:
 424        fd[nr_cpu][counter] = sys_perf_event_open(attr, pid, cpu, group_fd, 0);
 425
 426        if (fd[nr_cpu][counter] < 0) {
 427                int err = errno;
 428
 429                if (err == EPERM || err == EACCES)
 430                        die("Permission error - are you root?\n");
 431                else if (err ==  ENODEV && profile_cpu != -1)
 432                        die("No such device - did you specify an out-of-range profile CPU?\n");
 433
 434                /*
 435                 * If it's cycles then fall back to hrtimer
 436                 * based cpu-clock-tick sw counter, which
 437                 * is always available even if no PMU support:
 438                 */
 439                if (attr->type == PERF_TYPE_HARDWARE
 440                        && attr->config == PERF_COUNT_HW_CPU_CYCLES) {
 441
 442                        if (verbose)
 443                                warning(" ... trying to fall back to cpu-clock-ticks\n");
 444                        attr->type = PERF_TYPE_SOFTWARE;
 445                        attr->config = PERF_COUNT_SW_CPU_CLOCK;
 446                        goto try_again;
 447                }
 448                printf("\n");
 449                error("perfcounter syscall returned with %d (%s)\n",
 450                        fd[nr_cpu][counter], strerror(err));
 451                die("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
 452                exit(-1);
 453        }
 454
 455        h_attr = get_header_attr(attr, counter);
 456
 457        if (!file_new) {
 458                if (memcmp(&h_attr->attr, attr, sizeof(*attr))) {
 459                        fprintf(stderr, "incompatible append\n");
 460                        exit(-1);
 461                }
 462        }
 463
 464        if (read(fd[nr_cpu][counter], &read_data, sizeof(read_data)) == -1) {
 465                perror("Unable to read perf file descriptor\n");
 466                exit(-1);
 467        }
 468
 469        perf_header_attr__add_id(h_attr, read_data.id);
 470
 471        assert(fd[nr_cpu][counter] >= 0);
 472        fcntl(fd[nr_cpu][counter], F_SETFL, O_NONBLOCK);
 473
 474        /*
 475         * First counter acts as the group leader:
 476         */
 477        if (group && group_fd == -1)
 478                group_fd = fd[nr_cpu][counter];
 479        if (multiplex && multiplex_fd == -1)
 480                multiplex_fd = fd[nr_cpu][counter];
 481
 482        if (multiplex && fd[nr_cpu][counter] != multiplex_fd) {
 483                int ret;
 484
 485                ret = ioctl(fd[nr_cpu][counter], PERF_EVENT_IOC_SET_OUTPUT, multiplex_fd);
 486                assert(ret != -1);
 487        } else {
 488                event_array[nr_poll].fd = fd[nr_cpu][counter];
 489                event_array[nr_poll].events = POLLIN;
 490                nr_poll++;
 491
 492                mmap_array[nr_cpu][counter].counter = counter;
 493                mmap_array[nr_cpu][counter].prev = 0;
 494                mmap_array[nr_cpu][counter].mask = mmap_pages*page_size - 1;
 495                mmap_array[nr_cpu][counter].base = mmap(NULL, (mmap_pages+1)*page_size,
 496                                PROT_READ|PROT_WRITE, MAP_SHARED, fd[nr_cpu][counter], 0);
 497                if (mmap_array[nr_cpu][counter].base == MAP_FAILED) {
 498                        error("failed to mmap with %d (%s)\n", errno, strerror(errno));
 499                        exit(-1);
 500                }
 501        }
 502
 503        ioctl(fd[nr_cpu][counter], PERF_EVENT_IOC_ENABLE);
 504}
 505
 506static void open_counters(int cpu, pid_t pid)
 507{
 508        int counter;
 509
 510        group_fd = -1;
 511        for (counter = 0; counter < nr_counters; counter++)
 512                create_counter(counter, cpu, pid);
 513
 514        nr_cpu++;
 515}
 516
 517static void atexit_header(void)
 518{
 519        header->data_size += bytes_written;
 520
 521        perf_header__write(header, output);
 522}
 523
 524static int __cmd_record(int argc, const char **argv)
 525{
 526        int i, counter;
 527        struct stat st;
 528        pid_t pid = 0;
 529        int flags;
 530        int ret;
 531        unsigned long waking = 0;
 532
 533        page_size = sysconf(_SC_PAGE_SIZE);
 534        nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
 535        assert(nr_cpus <= MAX_NR_CPUS);
 536        assert(nr_cpus >= 0);
 537
 538        atexit(sig_atexit);
 539        signal(SIGCHLD, sig_handler);
 540        signal(SIGINT, sig_handler);
 541
 542        if (!stat(output_name, &st) && st.st_size) {
 543                if (!force && !append_file) {
 544                        fprintf(stderr, "Error, output file %s exists, use -A to append or -f to overwrite.\n",
 545                                        output_name);
 546                        exit(-1);
 547                }
 548        } else {
 549                append_file = 0;
 550        }
 551
 552        flags = O_CREAT|O_RDWR;
 553        if (append_file)
 554                file_new = 0;
 555        else
 556                flags |= O_TRUNC;
 557
 558        output = open(output_name, flags, S_IRUSR|S_IWUSR);
 559        if (output < 0) {
 560                perror("failed to create output file");
 561                exit(-1);
 562        }
 563
 564        if (!file_new)
 565                header = perf_header__read(output);
 566        else
 567                header = perf_header__new();
 568
 569
 570        if (raw_samples) {
 571                read_tracing_data(attrs, nr_counters);
 572        } else {
 573                for (i = 0; i < nr_counters; i++) {
 574                        if (attrs[i].sample_type & PERF_SAMPLE_RAW) {
 575                                read_tracing_data(attrs, nr_counters);
 576                                break;
 577                        }
 578                }
 579        }
 580        atexit(atexit_header);
 581
 582        if (!system_wide) {
 583                pid = target_pid;
 584                if (pid == -1)
 585                        pid = getpid();
 586
 587                open_counters(profile_cpu, pid);
 588        } else {
 589                if (profile_cpu != -1) {
 590                        open_counters(profile_cpu, target_pid);
 591                } else {
 592                        for (i = 0; i < nr_cpus; i++)
 593                                open_counters(i, target_pid);
 594                }
 595        }
 596
 597        if (file_new)
 598                perf_header__write(header, output);
 599
 600        if (!system_wide) {
 601                pid_t tgid = pid_synthesize_comm_event(pid, 0);
 602                pid_synthesize_mmap_samples(pid, tgid);
 603        } else
 604                synthesize_all();
 605
 606        if (target_pid == -1 && argc) {
 607                pid = fork();
 608                if (pid < 0)
 609                        perror("failed to fork");
 610
 611                if (!pid) {
 612                        if (execvp(argv[0], (char **)argv)) {
 613                                perror(argv[0]);
 614                                exit(-1);
 615                        }
 616                }
 617
 618                child_pid = pid;
 619        }
 620
 621        if (realtime_prio) {
 622                struct sched_param param;
 623
 624                param.sched_priority = realtime_prio;
 625                if (sched_setscheduler(0, SCHED_FIFO, &param)) {
 626                        printf("Could not set realtime priority.\n");
 627                        exit(-1);
 628                }
 629        }
 630
 631        for (;;) {
 632                int hits = samples;
 633
 634                for (i = 0; i < nr_cpu; i++) {
 635                        for (counter = 0; counter < nr_counters; counter++) {
 636                                if (mmap_array[i][counter].base)
 637                                        mmap_read(&mmap_array[i][counter]);
 638                        }
 639                }
 640
 641                if (hits == samples) {
 642                        if (done)
 643                                break;
 644                        ret = poll(event_array, nr_poll, -1);
 645                        waking++;
 646                }
 647
 648                if (done) {
 649                        for (i = 0; i < nr_cpu; i++) {
 650                                for (counter = 0; counter < nr_counters; counter++)
 651                                        ioctl(fd[i][counter], PERF_EVENT_IOC_DISABLE);
 652                        }
 653                }
 654        }
 655
 656        fprintf(stderr, "[ perf record: Woken up %ld times to write data ]\n", waking);
 657
 658        /*
 659         * Approximate RIP event size: 24 bytes.
 660         */
 661        fprintf(stderr,
 662                "[ perf record: Captured and wrote %.3f MB %s (~%lld samples) ]\n",
 663                (double)bytes_written / 1024.0 / 1024.0,
 664                output_name,
 665                bytes_written / 24);
 666
 667        return 0;
 668}
 669
 670static const char * const record_usage[] = {
 671        "perf record [<options>] [<command>]",
 672        "perf record [<options>] -- <command> [<options>]",
 673        NULL
 674};
 675
 676static const struct option options[] = {
 677        OPT_CALLBACK('e', "event", NULL, "event",
 678                     "event selector. use 'perf list' to list available events",
 679                     parse_events),
 680        OPT_INTEGER('p', "pid", &target_pid,
 681                    "record events on existing pid"),
 682        OPT_INTEGER('r', "realtime", &realtime_prio,
 683                    "collect data with this RT SCHED_FIFO priority"),
 684        OPT_BOOLEAN('R', "raw-samples", &raw_samples,
 685                    "collect raw sample records from all opened counters"),
 686        OPT_BOOLEAN('a', "all-cpus", &system_wide,
 687                            "system-wide collection from all CPUs"),
 688        OPT_BOOLEAN('A', "append", &append_file,
 689                            "append to the output file to do incremental profiling"),
 690        OPT_INTEGER('C', "profile_cpu", &profile_cpu,
 691                            "CPU to profile on"),
 692        OPT_BOOLEAN('f', "force", &force,
 693                        "overwrite existing data file"),
 694        OPT_LONG('c', "count", &default_interval,
 695                    "event period to sample"),
 696        OPT_STRING('o', "output", &output_name, "file",
 697                    "output file name"),
 698        OPT_BOOLEAN('i', "inherit", &inherit,
 699                    "child tasks inherit counters"),
 700        OPT_INTEGER('F', "freq", &freq,
 701                    "profile at this frequency"),
 702        OPT_INTEGER('m', "mmap-pages", &mmap_pages,
 703                    "number of mmap data pages"),
 704        OPT_BOOLEAN('g', "call-graph", &call_graph,
 705                    "do call-graph (stack chain/backtrace) recording"),
 706        OPT_BOOLEAN('v', "verbose", &verbose,
 707                    "be more verbose (show counter open errors, etc)"),
 708        OPT_BOOLEAN('s', "stat", &inherit_stat,
 709                    "per thread counts"),
 710        OPT_BOOLEAN('d', "data", &sample_address,
 711                    "Sample addresses"),
 712        OPT_BOOLEAN('n', "no-samples", &no_samples,
 713                    "don't sample"),
 714        OPT_BOOLEAN('M', "multiplex", &multiplex,
 715                    "multiplex counter output in a single channel"),
 716        OPT_END()
 717};
 718
 719int cmd_record(int argc, const char **argv, const char *prefix __used)
 720{
 721        int counter;
 722
 723        argc = parse_options(argc, argv, options, record_usage,
 724                PARSE_OPT_STOP_AT_NON_OPTION);
 725        if (!argc && target_pid == -1 && !system_wide)
 726                usage_with_options(record_usage, options);
 727
 728        if (!nr_counters) {
 729                nr_counters     = 1;
 730                attrs[0].type   = PERF_TYPE_HARDWARE;
 731                attrs[0].config = PERF_COUNT_HW_CPU_CYCLES;
 732        }
 733
 734        for (counter = 0; counter < nr_counters; counter++) {
 735                if (attrs[counter].sample_period)
 736                        continue;
 737
 738                attrs[counter].sample_period = default_interval;
 739        }
 740
 741        return __cmd_record(argc, argv);
 742}
 743