opcontrol.cpp revision e815c52e5e4dd0bae3113d2409097160f27348c6
1/*
2 * Copyright 2008, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18 * Binary implementation of the original opcontrol script due to missing tools
19 * like awk, test, etc.
20 */
21
22#include <unistd.h>
23#include <getopt.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <errno.h>
27#include <fcntl.h>
28#include <signal.h>
29#include <sys/stat.h>
30
31#include "op_config.h"
32
33#define verbose(fmt...) if (verbose_print) printf(fmt)
34
35/* Experiments found that using a small interval may hang the device, and the
36 * more events tracked simultaneously, the longer the interval has to be.
37 */
38
39#if defined(__i386__) || defined(__x86_64__)
40#define MAX_EVENTS 2
41int min_count[MAX_EVENTS] = {60000, 100000};
42#elif !defined(WITH_ARM_V7_A)
43#define MAX_EVENTS 3
44int min_count[MAX_EVENTS] = {150000, 200000, 250000};
45#else
46#define MAX_EVENTS 5
47int min_count[MAX_EVENTS] = {150000, 200000, 250000, 300000, 350000};
48#endif
49
50int verbose_print;
51int list_events;
52int show_usage;
53int setup;
54int quick;
55int num_events;
56int start;
57int stop;
58int reset;
59
60int selected_events[MAX_EVENTS];
61int selected_counts[MAX_EVENTS];
62
63char callgraph[8];
64char kernel_range[512];
65char vmlinux[512];
66
67struct option long_options[] = {
68    {"help", 0, &show_usage, 1},
69    {"list-events", 0, &list_events, 1},
70    {"reset", 0, &reset, 1},
71    {"setup", 0, &setup, 1},
72    {"quick", 0, &quick, 1},
73    {"callgraph", 1, 0, 'c'},
74    {"event", 1, 0, 'e'},
75    {"vmlinux", 1, 0, 'v'},
76    {"kernel-range", 1, 0, 'r'},
77    {"start", 0, &start, 1},
78    {"stop", 0, &stop, 1},
79    {"dump", 0, 0, 'd'},
80    {"shutdown", 0, 0, 'h'},
81    {"status", 0, 0, 't'},
82    {"verbose", 0, 0, 'V'},
83    {0, 0, 0, 0},
84};
85
86struct event_info {
87    int id;
88    int um;
89    const char *name;
90    const char *explanation;
91} event_info[] = {
92#if defined(__i386__) || defined(__x86_64__)
93    /* INTEL_ARCH_PERFMON events */
94
95    /* 0x3c counters:cpuid um:zero minimum:6000 filter:0 name:CPU_CLK_UNHALTED :
96     * Clock cycles when not halted
97     */
98    {0x3c, 0, "CPU_CLK_UNHALTED",
99     "Clock cycles when not halted" },
100
101    /* event:0x3c counters:cpuid um:one minimum:6000 filter:2 name:UNHALTED_REFERENCE_CYCLES :
102     * Unhalted reference cycles
103     */
104    {0x3c, 1, "UNHALTED_REFERENCE_CYCLES",
105      "Unhalted reference cycles" },
106
107    /* event:0xc0 counters:cpuid um:zero minimum:6000 filter:1 name:INST_RETIRED :
108     * number of instructions retired
109     */
110     {0xc0, 0, "INST_RETIRED",
111       "number of instructions retired"},
112
113    /* event:0x2e counters:cpuid um:x41 minimum:6000 filter:5 name:LLC_MISSES :
114     * Last level cache demand requests from this core that missed the LLC
115     */
116     {0x2e, 0x41, "LLC_MISSES",
117       "Last level cache demand requests from this core that missed the LLC"},
118
119    /* event:0x2e counters:cpuid um:x4f minimum:6000 filter:4 name:LLC_REFS :
120     * Last level cache demand requests from this core
121     */
122     {0x2e, 0x4f, "LLC_REFS",
123      "Last level cache demand requests from this core"},
124
125    /* event:0xc4 counters:cpuid um:zero minimum:500 filter:6 name:BR_INST_RETIRED :
126     * number of branch instructions retired
127     */
128     {0xc4, 0, "BR_INST_RETIRED",
129       "number of branch instructions retired"},
130
131    /* event:0xc5 counters:cpuid um:zero minimum:500 filter:7 name:BR_MISS_PRED_RETIRED :
132     * number of mispredicted branches retired (precise)
133     */
134     {0xc5, 0, "BR_MISS_PRED_RETIRED",
135       "number of mispredicted branches retired (precise)"},
136
137#elif !defined(WITH_ARM_V7_A)
138    /* ARM V6 events */
139    {0x00, 0, "IFU_IFETCH_MISS",
140     "number of instruction fetch misses"},
141    {0x01, 0, "CYCLES_IFU_MEM_STALL",
142     "cycles instruction fetch pipe is stalled"},
143    {0x02, 0, "CYCLES_DATA_STALL",
144     "cycles stall occurs for due to data dependency"},
145    {0x03, 0, "ITLB_MISS",
146     "number of Instruction MicroTLB misses"},
147    {0x04, 0, "DTLB_MISS",
148     "number of Data MicroTLB misses"},
149    {0x05, 0, "BR_INST_EXECUTED",
150     "branch instruction executed w/ or w/o program flow change"},
151    {0x06, 0, "BR_INST_MISS_PRED",
152     "branch mispredicted"},
153    {0x07, 0, "INSN_EXECUTED",
154     "instructions executed"},
155    {0x09, 0, "DCACHE_ACCESS",
156     "data cache access, cacheable locations"},
157    {0x0a, 0, "DCACHE_ACCESS_ALL",
158     "data cache access, all locations"},
159    {0x0b, 0, "DCACHE_MISS",
160     "data cache miss"},
161    {0x0c, 0, "DCACHE_WB",
162     "data cache writeback, 1 event for every half cacheline"},
163    {0x0d, 0, "PC_CHANGE",
164     "number of times the program counter was changed without a mode switch"},
165    {0x0f, 0, "TLB_MISS",
166     "Main TLB miss"},
167    {0x10, 0, "EXP_EXTERNAL",
168     "Explicit external data access"},
169    {0x11, 0, "LSU_STALL",
170     "cycles stalled because Load Store request queue is full"},
171    {0x12, 0, "WRITE_DRAIN",
172     "Times write buffer was drained"},
173    {0xff, 0, "CPU_CYCLES",
174     "clock cycles counter"},
175#else
176    /* ARM V7 events */
177    {0x00, 0, "PMNC_SW_INCR",
178     "Software increment of PMNC registers"},
179    {0x01, 0, "IFETCH_MISS",
180     "Instruction fetch misses from cache or normal cacheable memory"},
181    {0x02, 0, "ITLB_MISS",
182     "Instruction fetch misses from TLB"},
183    {0x03, 0, "DCACHE_REFILL",
184     "Data R/W operation that causes a refill from cache or normal cacheable"
185     "memory"},
186    {0x04, 0, "DCACHE_ACCESS",
187     "Data R/W from cache"},
188    {0x05, 0, "DTLB_REFILL",
189     "Data R/W that causes a TLB refill"},
190    {0x06, 0, "DREAD",
191     "Data read architecturally executed (note: architecturally executed = for"
192     "instructions that are unconditional or that pass the condition code)"},
193    {0x07, 0, "DWRITE",
194     "Data write architecturally executed"},
195    {0x08, 0, "INSTR_EXECUTED",
196     "All executed instructions"},
197    {0x09, 0, "EXC_TAKEN",
198     "Exception taken"},
199    {0x0A, 0, "EXC_EXECUTED",
200     "Exception return architecturally executed"},
201    {0x0B, 0, "CID_WRITE",
202     "Instruction that writes to the Context ID Register architecturally"
203     "executed"},
204    {0x0C, 0, "PC_WRITE",
205     "SW change of PC, architecturally executed (not by exceptions)"},
206    {0x0D, 0, "PC_IMM_BRANCH",
207     "Immediate branch instruction executed (taken or not)"},
208    {0x0E, 0, "PC_PROC_RETURN",
209     "Procedure return architecturally executed (not by exceptions)"},
210    {0x0F, 0, "UNALIGNED_ACCESS",
211     "Unaligned access architecturally executed"},
212    {0x10, 0, "PC_BRANCH_MIS_PRED",
213     "Branch mispredicted or not predicted. Counts pipeline flushes because of"
214     "misprediction"},
215    {0x12, 0, "PC_BRANCH_MIS_USED",
216    "Branch or change in program flow that could have been predicted"},
217    {0x40, 0, "WRITE_BUFFER_FULL",
218     "Any write buffer full cycle"},
219    {0x41, 0, "L2_STORE_MERGED",
220     "Any store that is merged in L2 cache"},
221    {0x42, 0, "L2_STORE_BUFF",
222     "Any bufferable store from load/store to L2 cache"},
223    {0x43, 0, "L2_ACCESS",
224     "Any access to L2 cache"},
225    {0x44, 0, "L2_CACH_MISS",
226     "Any cacheable miss in L2 cache"},
227    {0x45, 0, "AXI_READ_CYCLES",
228     "Number of cycles for an active AXI read"},
229    {0x46, 0, "AXI_WRITE_CYCLES",
230     "Number of cycles for an active AXI write"},
231    {0x47, 0, "MEMORY_REPLAY",
232     "Any replay event in the memory subsystem"},
233    {0x48, 0, "UNALIGNED_ACCESS_REPLAY",
234     "Unaligned access that causes a replay"},
235    {0x49, 0, "L1_DATA_MISS",
236     "L1 data cache miss as a result of the hashing algorithm"},
237    {0x4A, 0, "L1_INST_MISS",
238     "L1 instruction cache miss as a result of the hashing algorithm"},
239    {0x4B, 0, "L1_DATA_COLORING",
240     "L1 data access in which a page coloring alias occurs"},
241    {0x4C, 0, "L1_NEON_DATA",
242     "NEON data access that hits L1 cache"},
243    {0x4D, 0, "L1_NEON_CACH_DATA",
244     "NEON cacheable data access that hits L1 cache"},
245    {0x4E, 0, "L2_NEON",
246     "L2 access as a result of NEON memory access"},
247    {0x4F, 0, "L2_NEON_HIT",
248     "Any NEON hit in L2 cache"},
249    {0x50, 0, "L1_INST",
250     "Any L1 instruction cache access, excluding CP15 cache accesses"},
251    {0x51, 0, "PC_RETURN_MIS_PRED",
252     "Return stack misprediction at return stack pop"
253     "(incorrect target address)"},
254    {0x52, 0, "PC_BRANCH_FAILED",
255     "Branch prediction misprediction"},
256    {0x53, 0, "PC_BRANCH_TAKEN",
257     "Any predicted branch that is taken"},
258    {0x54, 0, "PC_BRANCH_EXECUTED",
259     "Any taken branch that is executed"},
260    {0x55, 0, "OP_EXECUTED",
261     "Number of operations executed"
262     "(in instruction or mutli-cycle instruction)"},
263    {0x56, 0, "CYCLES_INST_STALL",
264     "Cycles where no instruction available"},
265    {0x57, 0, "CYCLES_INST",
266     "Number of instructions issued in a cycle"},
267    {0x58, 0, "CYCLES_NEON_DATA_STALL",
268     "Number of cycles the processor waits on MRC data from NEON"},
269    {0x59, 0, "CYCLES_NEON_INST_STALL",
270     "Number of cycles the processor waits on NEON instruction queue or"
271     "NEON load queue"},
272    {0x5A, 0, "NEON_CYCLES",
273     "Number of cycles NEON and integer processors are not idle"},
274    {0x70, 0, "PMU0_EVENTS",
275     "Number of events from external input source PMUEXTIN[0]"},
276    {0x71, 0, "PMU1_EVENTS",
277     "Number of events from external input source PMUEXTIN[1]"},
278    {0x72, 0, "PMU_EVENTS",
279     "Number of events from both external input sources PMUEXTIN[0]"
280     "and PMUEXTIN[1]"},
281    {0xFF, 0, "CPU_CYCLES",
282     "Number of CPU cycles"},
283#endif
284};
285
286void usage()
287{
288    printf("\nopcontrol: usage:\n"
289           "   --list-events    list event types\n"
290           "   --help           this message\n"
291           "   --verbose        show extra status\n"
292           "   --setup          setup directories\n"
293#if defined(__i386__) || defined(__x86_64__)
294           "   --quick          setup and select CPU_CLK_UNHALTED:60000\n"
295#else
296           "   --quick          setup and select CPU_CYCLES:150000\n"
297#endif
298           "   --status         show configuration\n"
299           "   --start          start data collection\n"
300           "   --stop           stop data collection\n"
301           "   --reset          clears out data from current session\n"
302           "   --shutdown       kill the oprofile daeman\n"
303           "   --callgraph=depth callgraph depth\n"
304           "   --event=eventspec\n"
305           "      Choose an event. May be specified multiple times.\n"
306           "      eventspec is in the form of name[:count], where :\n"
307           "        name:  event name, see \"opcontrol --list-events\"\n"
308           "        count: reset counter value\n"
309           "   --vmlinux=file   vmlinux kernel image\n"
310           "   --kernel-range=start,end\n"
311           "                    kernel range vma address in hexadecimal\n"
312          );
313}
314
315void setup_session_dir()
316{
317    int fd;
318
319    fd = open(OP_DATA_DIR, O_RDONLY);
320    if (fd != -1) {
321        system("rm -r "OP_DATA_DIR);
322        close(fd);
323    }
324
325    if (mkdir(OP_DATA_DIR, 755)) {
326        fprintf(stderr, "Cannot create directory \"%s\": %s\n",
327                OP_DATA_DIR, strerror(errno));
328    }
329    if (mkdir(OP_DATA_DIR"/samples", 644)) {
330        fprintf(stderr, "Cannot create directory \"%s\": %s\n",
331                OP_DATA_DIR"/samples", strerror(errno));
332    }
333}
334
335int do_setup()
336{
337    char dir[1024];
338
339    setup_session_dir();
340
341    if (mkdir(OP_DRIVER_BASE, 644)) {
342        fprintf(stderr, "Cannot create directory "OP_DRIVER_BASE": %s\n",
343                strerror(errno));
344        return -1;
345    }
346    if (system("mount -t oprofilefs nodev "OP_DRIVER_BASE)) {
347        return -1;
348    }
349    return 0;
350}
351
352void do_list_events()
353{
354    unsigned int i;
355
356    printf("%-20s: %s\n", "name", "meaning");
357    printf("----------------------------------------"
358           "--------------------------------------\n");
359    for (i = 0; i < sizeof(event_info)/sizeof(struct event_info); i++) {
360        printf("%-20s: %s\n", event_info[i].name, event_info[i].explanation);
361    }
362}
363
364int find_event_idx_from_name(const char *name)
365{
366    unsigned int i;
367
368    for (i = 0; i < sizeof(event_info)/sizeof(struct event_info); i++) {
369        if (!strcmp(name, event_info[i].name)) {
370            return i;
371        }
372    }
373    return -1;
374}
375
376const char * find_event_name_from_id(int id)
377{
378    unsigned int i;
379
380    for (i = 0; i < sizeof(event_info)/sizeof(struct event_info); i++) {
381        if (event_info[i].id == id) {
382            return event_info[i].name;
383        }
384    }
385    return NULL;
386}
387
388int process_event(const char *event_spec)
389{
390    char event_name[512];
391    char count_name[512];
392    unsigned int i;
393    int event_idx;
394    int count_val;
395
396    strncpy(event_name, event_spec, 512);
397    count_name[0] = 0;
398
399    /* First, check if the name is followed by ":" */
400    for (i = 0; i < strlen(event_name); i++) {
401        if (event_name[i] == 0) {
402            break;
403        }
404        if (event_name[i] == ':') {
405            strncpy(count_name, event_name+i+1, 512);
406            event_name[i] = 0;
407            break;
408        }
409    }
410    event_idx = find_event_idx_from_name(event_name);
411    if (event_idx == -1) {
412        fprintf(stderr, "Unknown event name: %s\n", event_name);
413        return -1;
414    }
415
416    /* Use defualt count */
417    if (count_name[0] == 0) {
418        count_val = min_count[0];
419    } else {
420        count_val = atoi(count_name);
421    }
422
423    selected_events[num_events] = event_idx;
424    selected_counts[num_events++] = count_val;
425    verbose("event_id is %d\n", event_info[event_idx].id);
426    verbose("count_val is %d\n", count_val);
427    return 0;
428}
429
430int echo_dev(const char* str, int val, const char* file, int counter)
431{
432    char fullname[512];
433    char content[128];
434    int fd;
435
436    if (counter >= 0) {
437        snprintf(fullname, 512, OP_DRIVER_BASE"/%d/%s", counter, file);
438    }
439    else {
440        snprintf(fullname, 512, OP_DRIVER_BASE"/%s", file);
441    }
442    fd = open(fullname, O_WRONLY);
443    if (fd<0) {
444        fprintf(stderr, "Cannot open %s: %s\n", fullname, strerror(errno));
445        return fd;
446    }
447    if (str == 0) {
448        sprintf(content, "%d", val);
449    }
450    else {
451        strncpy(content, str, 128);
452    }
453    verbose("Configure %s (%s)\n", fullname, content);
454    write(fd, content, strlen(content));
455    close(fd);
456    return 0;
457}
458
459int read_num(const char* file)
460{
461    char buffer[256];
462    int fd = open(file, O_RDONLY);
463    if (fd<0) return -1;
464    int rd = read(fd, buffer, sizeof(buffer)-1);
465    buffer[rd] = 0;
466    return atoi(buffer);
467}
468
469void do_status()
470{
471    int num;
472    char fullname[512];
473    int i;
474
475    printf("Driver directory: %s\n", OP_DRIVER_BASE);
476    printf("Session directory: %s\n", OP_DATA_DIR);
477    for (i = 0; i < MAX_EVENTS; i++) {
478        sprintf(fullname, OP_DRIVER_BASE"/%d/enabled", i);
479        num = read_num(fullname);
480        if (num > 0) {
481            printf("Counter %d:\n", i);
482
483            /* event name */
484            sprintf(fullname, OP_DRIVER_BASE"/%d/event", i);
485            num = read_num(fullname);
486            printf("    name: %s\n", find_event_name_from_id(num));
487
488            /* profile interval */
489            sprintf(fullname, OP_DRIVER_BASE"/%d/count", i);
490            num = read_num(fullname);
491            printf("    count: %d\n", num);
492        }
493        else {
494            printf("Counter %d disabled\n", i);
495        }
496    }
497
498    num = read_num(OP_DATA_DIR"/lock");
499    if (num >= 0) {
500        int fd;
501        /* Still needs to check if this lock is left-over */
502        sprintf(fullname, "/proc/%d", num);
503        fd = open(fullname, O_RDONLY);
504        if (fd == -1) {
505            printf("Session directory is not clean - do \"opcontrol --setup\""
506                   " before you continue\n");
507            return;
508        }
509        else {
510            close(fd);
511            printf("oprofiled pid: %d\n", num);
512            num = read_num(OP_DRIVER_BASE"/enable");
513            printf("profiler is%s running\n", num == 0 ? " not" : "");
514            num = read_num(OP_DRIVER_BASE"/stats/cpu0/sample_received");
515            printf("  %9u samples received\n", num);
516            num = read_num(OP_DRIVER_BASE"/stats/cpu0/sample_lost_overflow");
517            printf("  %9u samples lost overflow\n", num);
518
519#if defined(__i386__) || defined(__x86_64__)
520            /* FIXME on ARM - backtrace seems broken there */
521            num = read_num(OP_DRIVER_BASE"/stats/cpu0/backtrace_aborted");
522            printf("  %9u backtrace aborted\n", num);
523            num = read_num(OP_DRIVER_BASE"/backtrace_depth");
524            printf("  %9u backtrace_depth\n", num);
525#endif
526        }
527    }
528    else {
529        printf("oprofiled is not running\n");
530    }
531}
532
533void do_reset()
534{
535    int fd;
536
537    fd = open(OP_DATA_DIR"/samples/current", O_RDONLY);
538    if (fd == -1) {
539        return;
540    }
541    close(fd);
542    system("rm -r "OP_DATA_DIR"/samples/current");
543}
544
545int main(int argc, char * const argv[])
546{
547    int option_index;
548    char command[1024];
549
550    /* Initialize default strings */
551    strcpy(vmlinux, "--no-vmlinux");
552    strcpy(kernel_range, "");
553
554    while (1) {
555        int c = getopt_long(argc, argv, "c:e:v:r:dhVt", long_options, &option_index);
556        if (c == -1) {
557            break;
558        }
559        switch (c) {
560            case 0:
561                break;
562            /* --callgraph */
563            case 'c':
564		strncpy(callgraph, optarg, sizeof(callgraph));
565                break;
566            /* --event */
567            case 'e':
568                if (num_events == MAX_EVENTS) {
569                    fprintf(stderr, "More than %d events specified\n",
570                            MAX_EVENTS);
571                    exit(1);
572                }
573                if (process_event(optarg)) {
574                    exit(1);
575                }
576                break;
577            /* --vmlinux */
578            case 'v':
579                sprintf(vmlinux, "-k %s", optarg);
580                break;
581            /* --kernel-range */
582            case 'r':
583                sprintf(kernel_range, "-r %s", optarg);
584                break;
585            case 'd':
586            /* --dump */ {
587                int pid = read_num(OP_DATA_DIR"/lock");
588                echo_dev("1", 0, "dump", -1);
589                if (pid >= 0) {
590                    sleep(1);
591                    kill(pid, SIGHUP);
592                }
593                break;
594            }
595            /* --shutdown */
596            case 'h': {
597                int pid = read_num(OP_DATA_DIR"/lock");
598                if (pid >= 0) {
599                    kill(pid, SIGHUP); /* Politely ask the daemon to close files */
600                    sleep(1);
601                    kill(pid, SIGTERM);/* Politely ask the daemon to die */
602                    sleep(1);
603                    kill(pid, SIGKILL);
604                }
605                setup_session_dir();
606                break;
607            }
608            /* --verbose */
609            case 'V':
610                verbose_print++;
611                break;
612            /* --status */
613            case 't':
614                do_status();
615                break;
616            default:
617                usage();
618                exit(1);
619        }
620    }
621    verbose("list_events = %d\n", list_events);
622    verbose("setup = %d\n", setup);
623
624    if (list_events) {
625        do_list_events();
626    }
627
628    if (quick) {
629#if defined(__i386__) || defined(__x86_64__)
630        process_event("CPU_CLK_UNHALTED");
631#else
632        process_event("CPU_CYCLES");
633#endif
634        setup = 1;
635    }
636
637    if (reset) {
638        do_reset();
639    }
640
641    if (show_usage) {
642        usage();
643    }
644
645    if (setup) {
646        if (do_setup()) {
647            fprintf(stderr, "do_setup failed");
648            exit(1);
649        }
650    }
651
652    if (strlen(callgraph)) {
653        echo_dev(callgraph, 0, "backtrace_depth", -1);
654    }
655
656    if (num_events != 0) {
657        int i;
658
659        strcpy(command, "oprofiled --session-dir="OP_DATA_DIR);
660
661#if defined(__i386__) || defined(__x86_64__)
662        /* Nothing */
663#elif !defined(WITH_ARM_V7_A)
664        /* Since counter #3 can only handle CPU_CYCLES, check and shuffle the
665         * order a bit so that the maximal number of events can be profiled
666         * simultaneously
667         */
668        if (num_events == 3) {
669            for (i = 0; i < num_events; i++) {
670                int event_idx = selected_events[i];
671
672                if (event_info[event_idx].id == 0xff) {
673                    break;
674                }
675            }
676
677            /* No CPU_CYCLES is found */
678            if (i == 3) {
679                fprintf(stderr, "You can only specify three events if one of "
680                                "them is CPU_CYCLES\n");
681                exit(1);
682            }
683            /* Swap CPU_CYCLES to counter #2 (starting from #0)*/
684            else if (i != 2) {
685                int temp;
686
687                temp = selected_events[2];
688                selected_events[2] = selected_events[i];
689                selected_events[i] = temp;
690
691                temp = selected_counts[2];
692                selected_counts[2] = selected_counts[i];
693                selected_counts[i] = temp;
694            }
695        }
696#endif
697
698
699        /* Configure the counters and enable them */
700        for (i = 0; i < num_events; i++) {
701            int event_idx = selected_events[i];
702            int setup_result = 0;
703
704            if (i == 0) {
705                snprintf(command+strlen(command), 1024 - strlen(command),
706                         " --events=");
707            }
708            else {
709                snprintf(command+strlen(command), 1024 - strlen(command),
710                         ",");
711            }
712            /* Compose name:id:count:unit_mask:kernel:user, something like
713             * --events=CYCLES_DATA_STALL:2:0:200000:0:1:1,....
714             */
715            snprintf(command+strlen(command), 1024 - strlen(command),
716                     "%s:%d:%d:%d:%d:1:1",
717                     event_info[event_idx].name,
718                     event_info[event_idx].id,
719                     i,
720                     selected_counts[i],
721                     event_info[event_idx].um);
722
723            setup_result |= echo_dev("1", 0, "user", i);
724            setup_result |= echo_dev("1", 0, "kernel", i);
725            setup_result |= echo_dev(NULL, event_info[event_idx].um, "unit_mask", i);
726            setup_result |= echo_dev("1", 0, "enabled", i);
727            setup_result |= echo_dev(NULL, selected_counts[i], "count", i);
728            setup_result |= echo_dev(NULL, event_info[event_idx].id,
729                                     "event", i);
730            if (setup_result) {
731                fprintf(stderr, "Counter configuration failed for %s\n",
732                        event_info[event_idx].name);
733                fprintf(stderr, "Did you do \"opcontrol --setup\" first?\n");
734                exit(1);
735            }
736        }
737
738        /* Disable the unused counters */
739        for (i = num_events; i < MAX_EVENTS; i++) {
740            echo_dev("0", 0, "enabled", i);
741        }
742
743        snprintf(command+strlen(command), 1024 - strlen(command), " %s",
744                 vmlinux);
745        if (kernel_range[0]) {
746            snprintf(command+strlen(command), 1024 - strlen(command), " %s",
747                     kernel_range);
748        }
749        verbose("command: %s\n", command);
750        system(command);
751    }
752
753    if (start) {
754        echo_dev("1", 0, "enable", -1);
755    }
756
757    if (stop) {
758        echo_dev("0", 0, "enable", -1);
759    }
760}
761