Note: We no longer publish the latest version of our code here. We primarily use a kumc-bmi github organization. The heron ETL repository, in particular, is not public. Peers in the informatics community should see MultiSiteDev for details on requesting access.

source: webrtc/webrtc/system_wrappers/source/android/cpu-features.c @ 0:4bda6873e34c

pub_scrub_3792 tip
Last change on this file since 0:4bda6873e34c was 0:4bda6873e34c, checked in by Michael Prittie <mprittie@…>, 6 years ago

Scrubbed password for publication.

File size: 10.4 KB
Line 
1/*
2 *  Copyright (c) 2012 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#include <sys/system_properties.h>
12#ifdef __arm__
13#include <machine/cpu-features.h>
14#endif
15#include <errno.h>
16#include <fcntl.h>
17#include <pthread.h>
18#include <stdio.h>
19#include <stdlib.h>
20
21#include "webrtc/system_wrappers/source/android/cpu-features.h"
22
23static  pthread_once_t     g_once;
24static  AndroidCpuFamily   g_cpuFamily;
25static  uint64_t           g_cpuFeatures;
26static  int                g_cpuCount;
27
28static const int  android_cpufeatures_debug = 0;
29
30#ifdef __arm__
31#  define DEFAULT_CPU_FAMILY  ANDROID_CPU_FAMILY_ARM
32#elif defined __i386__
33#  define DEFAULT_CPU_FAMILY  ANDROID_CPU_FAMILY_X86
34#else
35#  define DEFAULT_CPU_FAMILY  ANDROID_CPU_FAMILY_UNKNOWN
36#endif
37
38#define  D(...) \
39    do { \
40        if (android_cpufeatures_debug) { \
41            printf(__VA_ARGS__); fflush(stdout); \
42        } \
43    } while (0)
44
45#ifdef __i386__
46static __inline__ void x86_cpuid(int func, int values[4])
47{
48    int a, b, c, d;
49    /* We need to preserve ebx since we're compiling PIC code */
50    /* this means we can't use "=b" for the second output register */
51    __asm__ __volatile__ ( \
52      "push %%ebx\n"
53      "cpuid\n" \
54      "mov %1, %%ebx\n"
55      "pop %%ebx\n"
56      : "=a" (a), "=r" (b), "=c" (c), "=d" (d) \
57      : "a" (func) \
58    );
59    values[0] = a;
60    values[1] = b;
61    values[2] = c;
62    values[3] = d;
63}
64#endif
65
66/* Read the content of /proc/cpuinfo into a user-provided buffer.
67 * Return the length of the data, or -1 on error. Does *not*
68 * zero-terminate the content. Will not read more
69 * than 'buffsize' bytes.
70 */
71static int
72read_file(const char*  pathname, char*  buffer, size_t  buffsize)
73{
74    int  fd, len;
75
76    fd = open(pathname, O_RDONLY);
77    if (fd < 0)
78        return -1;
79
80    do {
81        len = read(fd, buffer, buffsize);
82    } while (len < 0 && errno == EINTR);
83
84    close(fd);
85
86    return len;
87}
88
89/* Extract the content of a the first occurence of a given field in
90 * the content of /proc/cpuinfo and return it as a heap-allocated
91 * string that must be freed by the caller.
92 *
93 * Return NULL if not found
94 */
95static char*
96extract_cpuinfo_field(char* buffer, int buflen, const char* field)
97{
98    int  fieldlen = strlen(field);
99    char* bufend = buffer + buflen;
100    char* result = NULL;
101    int len, ignore;
102    const char *p, *q;
103
104    /* Look for first field occurence, and ensures it starts the line.
105     */
106    p = buffer;
107    bufend = buffer + buflen;
108    for (;;) {
109        p = memmem(p, bufend-p, field, fieldlen);
110        if (p == NULL)
111            goto EXIT;
112
113        if (p == buffer || p[-1] == '\n')
114            break;
115
116        p += fieldlen;
117    }
118
119    /* Skip to the first column followed by a space */
120    p += fieldlen;
121    p  = memchr(p, ':', bufend-p);
122    if (p == NULL || p[1] != ' ')
123        goto EXIT;
124
125    /* Find the end of the line */
126    p += 2;
127    q = memchr(p, '\n', bufend-p);
128    if (q == NULL)
129        q = bufend;
130
131    /* Copy the line into a heap-allocated buffer */
132    len = q-p;
133    result = malloc(len+1);
134    if (result == NULL)
135        goto EXIT;
136
137    memcpy(result, p, len);
138    result[len] = '\0';
139
140EXIT:
141    return result;
142}
143
144/* Count the number of occurences of a given field prefix in /proc/cpuinfo.
145 */
146static int
147count_cpuinfo_field(char* buffer, int buflen, const char* field)
148{
149    int fieldlen = strlen(field);
150    const char* p = buffer;
151    const char* bufend = buffer + buflen;
152    const char* q;
153    int count = 0;
154
155    for (;;) {
156        const char* q;
157
158        p = memmem(p, bufend-p, field, fieldlen);
159        if (p == NULL)
160            break;
161
162        /* Ensure that the field is at the start of a line */
163        if (p > buffer && p[-1] != '\n') {
164            p += fieldlen;
165            continue;
166        }
167
168
169        /* skip any whitespace */
170        q = p + fieldlen;
171        while (q < bufend && (*q == ' ' || *q == '\t'))
172            q++;
173
174        /* we must have a colon now */
175        if (q < bufend && *q == ':') {
176            count += 1;
177            q ++;
178        }
179        p = q;
180    }
181
182    return count;
183}
184
185/* Like strlen(), but for constant string literals */
186#define STRLEN_CONST(x)  ((sizeof(x)-1)
187
188
189/* Checks that a space-separated list of items contains one given 'item'.
190 * Returns 1 if found, 0 otherwise.
191 */
192static int
193has_list_item(const char* list, const char* item)
194{
195    const char*  p = list;
196    int itemlen = strlen(item);
197
198    if (list == NULL)
199        return 0;
200
201    while (*p) {
202        const char*  q;
203
204        /* skip spaces */
205        while (*p == ' ' || *p == '\t')
206            p++;
207
208        /* find end of current list item */
209        q = p;
210        while (*q && *q != ' ' && *q != '\t')
211            q++;
212
213        if (itemlen == q-p && !memcmp(p, item, itemlen))
214            return 1;
215
216        /* skip to next item */
217        p = q;
218    }
219    return 0;
220}
221
222
223static void
224android_cpuInit(void)
225{
226    char cpuinfo[4096];
227    int  cpuinfo_len;
228
229    g_cpuFamily   = DEFAULT_CPU_FAMILY;
230    g_cpuFeatures = 0;
231    g_cpuCount    = 1;
232
233    cpuinfo_len = read_file("/proc/cpuinfo", cpuinfo, sizeof cpuinfo);
234    D("cpuinfo_len is (%d):\n%.*s\n", cpuinfo_len,
235      cpuinfo_len >= 0 ? cpuinfo_len : 0, cpuinfo);
236
237    if (cpuinfo_len < 0)  /* should not happen */ {
238        return;
239    }
240
241    /* Count the CPU cores, the value may be 0 for single-core CPUs */
242    g_cpuCount = count_cpuinfo_field(cpuinfo, cpuinfo_len, "processor");
243    if (g_cpuCount == 0) {
244        g_cpuCount = count_cpuinfo_field(cpuinfo, cpuinfo_len, "Processor");
245        if (g_cpuCount == 0) {
246            g_cpuCount = 1;
247        }
248    }
249
250    D("found cpuCount = %d\n", g_cpuCount);
251
252#ifdef __ARM_ARCH__
253    {
254        char*  features = NULL;
255        char*  architecture = NULL;
256
257        /* Extract architecture from the "CPU Architecture" field.
258         * The list is well-known, unlike the the output of
259         * the 'Processor' field which can vary greatly.
260         *
261         * See the definition of the 'proc_arch' array in
262         * $KERNEL/arch/arm/kernel/setup.c and the 'c_show' function in
263         * same file.
264         */
265        char* cpuArch = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "CPU architecture");
266
267        if (cpuArch != NULL) {
268            char*  end;
269            long   archNumber;
270            int    hasARMv7 = 0;
271
272            D("found cpuArch = '%s'\n", cpuArch);
273
274            /* read the initial decimal number, ignore the rest */
275            archNumber = strtol(cpuArch, &end, 10);
276
277            /* Here we assume that ARMv8 will be upwards compatible with v7
278                * in the future. Unfortunately, there is no 'Features' field to
279                * indicate that Thumb-2 is supported.
280                */
281            if (end > cpuArch && archNumber >= 7) {
282                hasARMv7 = 1;
283            }
284
285            /* Unfortunately, it seems that certain ARMv6-based CPUs
286             * report an incorrect architecture number of 7!
287             *
288             * See http://code.google.com/p/android/issues/detail?id=10812
289             *
290             * We try to correct this by looking at the 'elf_format'
291             * field reported by the 'Processor' field, which is of the
292             * form of "(v7l)" for an ARMv7-based CPU, and "(v6l)" for
293             * an ARMv6-one.
294             */
295            if (hasARMv7) {
296                char* cpuProc = extract_cpuinfo_field(cpuinfo, cpuinfo_len,
297                                                      "Processor");
298                if (cpuProc != NULL) {
299                    D("found cpuProc = '%s'\n", cpuProc);
300                    if (has_list_item(cpuProc, "(v6l)")) {
301                        D("CPU processor and architecture mismatch!!\n");
302                        hasARMv7 = 0;
303                    }
304                    free(cpuProc);
305                }
306            }
307
308            if (hasARMv7) {
309                g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_ARMv7;
310            }
311
312            /* The LDREX / STREX instructions are available from ARMv6 */
313            if (archNumber >= 6) {
314                g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_LDREX_STREX;
315            }
316
317            free(cpuArch);
318        }
319
320        /* Extract the list of CPU features from 'Features' field */
321        char* cpuFeatures = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "Features");
322
323        if (cpuFeatures != NULL) {
324
325            D("found cpuFeatures = '%s'\n", cpuFeatures);
326
327            if (has_list_item(cpuFeatures, "vfpv3"))
328                g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3;
329
330            else if (has_list_item(cpuFeatures, "vfpv3d16"))
331                g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3;
332
333            if (has_list_item(cpuFeatures, "neon")) {
334                /* Note: Certain kernels only report neon but not vfpv3
335                    *       in their features list. However, ARM mandates
336                    *       that if Neon is implemented, so must be VFPv3
337                    *       so always set the flag.
338                    */
339                g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_NEON |
340                                 ANDROID_CPU_ARM_FEATURE_VFPv3;
341            }
342            free(cpuFeatures);
343        }
344    }
345#endif /* __ARM_ARCH__ */
346
347#ifdef __i386__
348    g_cpuFamily = ANDROID_CPU_FAMILY_X86;
349
350    int regs[4];
351
352/* According to http://en.wikipedia.org/wiki/CPUID */
353#define VENDOR_INTEL_b  0x756e6547
354#define VENDOR_INTEL_c  0x6c65746e
355#define VENDOR_INTEL_d  0x49656e69
356
357    x86_cpuid(0, regs);
358    int vendorIsIntel = (regs[1] == VENDOR_INTEL_b &&
359                         regs[2] == VENDOR_INTEL_c &&
360                         regs[3] == VENDOR_INTEL_d);
361
362    x86_cpuid(1, regs);
363    if ((regs[2] & (1 << 9)) != 0) {
364        g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_SSSE3;
365    }
366    if ((regs[2] & (1 << 23)) != 0) {
367        g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_POPCNT;
368    }
369    if (vendorIsIntel && (regs[2] & (1 << 22)) != 0) {
370        g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_MOVBE;
371    }
372#endif
373}
374
375
376AndroidCpuFamily
377android_getCpuFamily(void)
378{
379    pthread_once(&g_once, android_cpuInit);
380    return g_cpuFamily;
381}
382
383
384uint64_t
385android_getCpuFeatures(void)
386{
387    pthread_once(&g_once, android_cpuInit);
388    return g_cpuFeatures;
389}
390
391
392int
393android_getCpuCount(void)
394{
395    pthread_once(&g_once, android_cpuInit);
396    return g_cpuCount;
397}
Note: See TracBrowser for help on using the repository browser.