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