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