• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
read_file(const char * pathname,char * buffer,size_t buffsize)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  */
extract_cpuinfo_field(char * buffer,int buflen,const char * field)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  */
count_cpuinfo_field(char * buffer,int buflen,const char * field)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  */
has_list_item(const char * list,const char * item)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 
cpuInit(void)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 
WebRtc_GetCPUFeaturesARM(void)330 uint64_t WebRtc_GetCPUFeaturesARM(void) {
331   pthread_once(&g_once, cpuInit);
332   return g_cpuFeatures;
333 }
334