Home | History | Annotate | Download | only in source
      1 /*
      2  *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 // This file is derived from Android's NDK package r7, located at
     12 // <ndk>/sources/android/cpufeatures/ (downloadable from
     13 // http://developer.android.com/sdk/ndk/index.html).
     14 
     15 #include "cpu_features_wrapper.h"
     16 
     17 #include <fcntl.h>
     18 #include <errno.h>
     19 #include <pthread.h>
     20 #include <stdio.h>
     21 #include <stdlib.h>
     22 
     23 // Define CPU family.
     24 typedef enum {
     25   CPU_FAMILY_UNKNOWN = 0,
     26   CPU_FAMILY_ARM,
     27   CPU_FAMILY_X86,
     28   CPU_FAMILY_MAX  // Do not remove.
     29 } CpuFamily;
     30 
     31 static pthread_once_t g_once;
     32 static CpuFamily g_cpuFamily;
     33 static uint64_t g_cpuFeatures;
     34 static int g_cpuCount;
     35 
     36 static const int cpufeatures_debug = 0;
     37 
     38 #ifdef __arm__
     39 #  define DEFAULT_CPU_FAMILY  CPU_FAMILY_ARM
     40 #elif defined __i386__
     41 #  define DEFAULT_CPU_FAMILY  CPU_FAMILY_X86
     42 #else
     43 #  define DEFAULT_CPU_FAMILY  CPU_FAMILY_UNKNOWN
     44 #endif
     45 
     46 #define  D(...) \
     47   do { \
     48     if (cpufeatures_debug) { \
     49       printf(__VA_ARGS__); fflush(stdout); \
     50     } \
     51   } while (0)
     52 
     53 /* Read the content of /proc/cpuinfo into a user-provided buffer.
     54  * Return the length of the data, or -1 on error. Does *not*
     55  * zero-terminate the content. Will not read more
     56  * than 'buffsize' bytes.
     57  */
     58 static int read_file(const char*  pathname, char*  buffer, size_t  buffsize) {
     59   int  fd, len;
     60 
     61   fd = open(pathname, O_RDONLY);
     62   if (fd < 0)
     63     return -1;
     64 
     65   do {
     66     len = read(fd, buffer, buffsize);
     67   } while (len < 0 && errno == EINTR);
     68 
     69   close(fd);
     70 
     71   return len;
     72 }
     73 
     74 /* Extract the content of a the first occurence of a given field in
     75  * the content of /proc/cpuinfo and return it as a heap-allocated
     76  * string that must be freed by the caller.
     77  *
     78  * Return NULL if not found
     79  */
     80 static char* extract_cpuinfo_field(char* buffer, int buflen, const char* field) {
     81   int  fieldlen = strlen(field);
     82   char* bufend = buffer + buflen;
     83   char* result = NULL;
     84   int len, ignore;
     85   const char* p, *q;
     86 
     87   /* Look for first field occurence, and ensures it starts the line.
     88    */
     89   p = buffer;
     90   bufend = buffer + buflen;
     91   for (;;) {
     92     p = memmem(p, bufend - p, field, fieldlen);
     93     if (p == NULL)
     94       goto EXIT;
     95 
     96     if (p == buffer || p[-1] == '\n')
     97       break;
     98 
     99     p += fieldlen;
    100   }
    101 
    102   /* Skip to the first column followed by a space */
    103   p += fieldlen;
    104   p  = memchr(p, ':', bufend - p);
    105   if (p == NULL || p[1] != ' ')
    106     goto EXIT;
    107 
    108   /* Find the end of the line */
    109   p += 2;
    110   q = memchr(p, '\n', bufend - p);
    111   if (q == NULL)
    112     q = bufend;
    113 
    114   /* Copy the line into a heap-allocated buffer */
    115   len = q - p;
    116   result = malloc(len + 1);
    117   if (result == NULL)
    118     goto EXIT;
    119 
    120   memcpy(result, p, len);
    121   result[len] = '\0';
    122 
    123 EXIT:
    124   return result;
    125 }
    126 
    127 /* Count the number of occurences of a given field prefix in /proc/cpuinfo.
    128  */
    129 static int count_cpuinfo_field(char* buffer, int buflen, const char* field) {
    130   int fieldlen = strlen(field);
    131   const char* p = buffer;
    132   const char* bufend = buffer + buflen;
    133   const char* q;
    134   int count = 0;
    135 
    136   for (;;) {
    137     const char* q;
    138 
    139     p = memmem(p, bufend - p, field, fieldlen);
    140     if (p == NULL)
    141       break;
    142 
    143     /* Ensure that the field is at the start of a line */
    144     if (p > buffer && p[-1] != '\n') {
    145       p += fieldlen;
    146       continue;
    147     }
    148 
    149 
    150     /* skip any whitespace */
    151     q = p + fieldlen;
    152     while (q < bufend && (*q == ' ' || *q == '\t'))
    153       q++;
    154 
    155     /* we must have a colon now */
    156     if (q < bufend && *q == ':') {
    157       count += 1;
    158       q ++;
    159     }
    160     p = q;
    161   }
    162 
    163   return count;
    164 }
    165 
    166 /* Like strlen(), but for constant string literals */
    167 #define STRLEN_CONST(x)  ((sizeof(x)-1)
    168 
    169 
    170 /* Checks that a space-separated list of items contains one given 'item'.
    171  * Returns 1 if found, 0 otherwise.
    172  */
    173 static int has_list_item(const char* list, const char* item) {
    174   const char*  p = list;
    175   int itemlen = strlen(item);
    176 
    177   if (list == NULL)
    178     return 0;
    179 
    180   while (*p) {
    181     const char*  q;
    182 
    183     /* skip spaces */
    184     while (*p == ' ' || *p == '\t')
    185       p++;
    186 
    187     /* find end of current list item */
    188     q = p;
    189     while (*q && *q != ' ' && *q != '\t')
    190       q++;
    191 
    192     if (itemlen == q - p && !memcmp(p, item, itemlen))
    193       return 1;
    194 
    195     /* skip to next item */
    196     p = q;
    197   }
    198   return 0;
    199 }
    200 
    201 
    202 static void cpuInit(void) {
    203   char cpuinfo[4096];
    204   int  cpuinfo_len;
    205 
    206   g_cpuFamily   = DEFAULT_CPU_FAMILY;
    207   g_cpuFeatures = 0;
    208   g_cpuCount    = 1;
    209 
    210   cpuinfo_len = read_file("/proc/cpuinfo", cpuinfo, sizeof cpuinfo);
    211   D("cpuinfo_len is (%d):\n%.*s\n", cpuinfo_len,
    212     cpuinfo_len >= 0 ? cpuinfo_len : 0, cpuinfo);
    213 
    214   if (cpuinfo_len < 0) { /* should not happen */
    215     return;
    216   }
    217 
    218   /* Count the CPU cores, the value may be 0 for single-core CPUs */
    219   g_cpuCount = count_cpuinfo_field(cpuinfo, cpuinfo_len, "processor");
    220   if (g_cpuCount == 0) {
    221     g_cpuCount = count_cpuinfo_field(cpuinfo, cpuinfo_len, "Processor");
    222     if (g_cpuCount == 0) {
    223       g_cpuCount = 1;
    224     }
    225   }
    226 
    227   D("found cpuCount = %d\n", g_cpuCount);
    228 
    229 #ifdef __arm__
    230   {
    231     char*  features = NULL;
    232     char*  architecture = NULL;
    233 
    234     /* Extract architecture from the "CPU Architecture" field.
    235      * The list is well-known, unlike the the output of
    236      * the 'Processor' field which can vary greatly.
    237      *
    238      * See the definition of the 'proc_arch' array in
    239      * $KERNEL/arch/arm/kernel/setup.c and the 'c_show' function in
    240      * same file.
    241      */
    242     char* cpuArch = extract_cpuinfo_field(cpuinfo, cpuinfo_len,
    243                                           "CPU architecture");
    244 
    245     if (cpuArch != NULL) {
    246       char*  end;
    247       long   archNumber;
    248       int    hasARMv7 = 0;
    249 
    250       D("found cpuArch = '%s'\n", cpuArch);
    251 
    252       /* read the initial decimal number, ignore the rest */
    253       archNumber = strtol(cpuArch, &end, 10);
    254 
    255       /* Here we assume that ARMv8 will be upwards compatible with v7
    256           * in the future. Unfortunately, there is no 'Features' field to
    257           * indicate that Thumb-2 is supported.
    258           */
    259       if (end > cpuArch && archNumber >= 7) {
    260         hasARMv7 = 1;
    261       }
    262 
    263       /* Unfortunately, it seems that certain ARMv6-based CPUs
    264        * report an incorrect architecture number of 7!
    265        *
    266        * We try to correct this by looking at the 'elf_format'
    267        * field reported by the 'Processor' field, which is of the
    268        * form of "(v7l)" for an ARMv7-based CPU, and "(v6l)" for
    269        * an ARMv6-one.
    270        */
    271       if (hasARMv7) {
    272         char* cpuProc = extract_cpuinfo_field(cpuinfo, cpuinfo_len,
    273                                               "Processor");
    274         if (cpuProc != NULL) {
    275           D("found cpuProc = '%s'\n", cpuProc);
    276           if (has_list_item(cpuProc, "(v6l)")) {
    277             D("CPU processor and architecture mismatch!!\n");
    278             hasARMv7 = 0;
    279           }
    280           free(cpuProc);
    281         }
    282       }
    283 
    284       if (hasARMv7) {
    285         g_cpuFeatures |= kCPUFeatureARMv7;
    286       }
    287 
    288       /* The LDREX / STREX instructions are available from ARMv6 */
    289       if (archNumber >= 6) {
    290         g_cpuFeatures |= kCPUFeatureLDREXSTREX;
    291       }
    292 
    293       free(cpuArch);
    294     }
    295 
    296     /* Extract the list of CPU features from 'Features' field */
    297     char* cpuFeatures = extract_cpuinfo_field(cpuinfo, cpuinfo_len,
    298                                               "Features");
    299 
    300     if (cpuFeatures != NULL) {
    301 
    302       D("found cpuFeatures = '%s'\n", cpuFeatures);
    303 
    304       if (has_list_item(cpuFeatures, "vfpv3"))
    305         g_cpuFeatures |= kCPUFeatureVFPv3;
    306 
    307       else if (has_list_item(cpuFeatures, "vfpv3d16"))
    308         g_cpuFeatures |= kCPUFeatureVFPv3;
    309 
    310       if (has_list_item(cpuFeatures, "neon")) {
    311         /* Note: Certain kernels only report neon but not vfpv3
    312             *       in their features list. However, ARM mandates
    313             *       that if Neon is implemented, so must be VFPv3
    314             *       so always set the flag.
    315             */
    316         g_cpuFeatures |= kCPUFeatureNEON |
    317                          kCPUFeatureVFPv3;
    318       }
    319       free(cpuFeatures);
    320     }
    321   }
    322 #endif  // __arm__
    323 
    324 #ifdef __i386__
    325   g_cpuFamily = CPU_FAMILY_X86;
    326 #endif
    327 }
    328 
    329 
    330 uint64_t WebRtc_GetCPUFeaturesARM(void) {
    331   pthread_once(&g_once, cpuInit);
    332   return g_cpuFeatures;
    333 }
    334