Index: base/cpu.cc |
diff --git a/base/cpu.cc b/base/cpu.cc |
index dcba3b6e73e5aa7884d02b59794adfafa505ca9d..a6a6128bb962724a7405fc888e9f5424459dc8ea 100644 |
--- a/base/cpu.cc |
+++ b/base/cpu.cc |
@@ -4,11 +4,13 @@ |
#include "base/cpu.h" |
+#include <stdlib.h> |
#include <string.h> |
#include <algorithm> |
#include "base/basictypes.h" |
+#include "base/strings/string_piece.h" |
#include "build/build_config.h" |
#if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX)) |
@@ -44,6 +46,7 @@ CPU::CPU() |
has_avx_hardware_(false), |
has_aesni_(false), |
has_non_stop_time_stamp_counter_(false), |
+ has_broken_neon_(false), |
cpu_vendor_("unknown") { |
Initialize(); |
} |
@@ -90,52 +93,99 @@ uint64 _xgetbv(uint32 xcr) { |
#endif // ARCH_CPU_X86_FAMILY |
#if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX)) |
+class LazyCpuInfoValue { |
+ public: |
+ LazyCpuInfoValue() : has_broken_neon_(false) { |
+ // This function finds the value from /proc/cpuinfo under the key "model |
+ // name" or "Processor". "model name" is used in Linux 3.8 and later (3.7 |
+ // and later for arm64) and is shown once per CPU. "Processor" is used in |
+ // earler versions and is shown only once at the top of /proc/cpuinfo |
+ // regardless of the number CPUs. |
+ const char kModelNamePrefix[] = "model name\t: "; |
+ const char kProcessorPrefix[] = "Processor\t: "; |
+ |
+ // This function also calculates whether we believe that this CPU has a |
+ // broken NEON unit based on these fields from cpuinfo: |
+ unsigned implementer = 0, architecture = 0, variant = 0, part = 0, |
+ revision = 0; |
+ const struct { |
+ const char key[17]; |
+ unsigned *result; |
+ } kUnsignedValues[] = { |
+ {"CPU implementer", &implementer}, |
+ {"CPU architecture", &architecture}, |
+ {"CPU variant", &variant}, |
+ {"CPU part", &part}, |
+ {"CPU revision", &revision}, |
+ }; |
+ |
+ std::string contents; |
+ ReadFileToString(FilePath("/proc/cpuinfo"), &contents); |
+ DCHECK(!contents.empty()); |
+ if (contents.empty()) { |
+ return; |
+ } |
-// Returns the string found in /proc/cpuinfo under the key "model name" or |
-// "Processor". "model name" is used in Linux 3.8 and later (3.7 and later for |
-// arm64) and is shown once per CPU. "Processor" is used in earler versions and |
-// is shown only once at the top of /proc/cpuinfo regardless of the number CPUs. |
-std::string ParseCpuInfo() { |
- const char kModelNamePrefix[] = "model name\t: "; |
- const char kProcessorPrefix[] = "Processor\t: "; |
- std::string contents; |
- ReadFileToString(FilePath("/proc/cpuinfo"), &contents); |
- DCHECK(!contents.empty()); |
- std::string cpu_brand; |
- if (!contents.empty()) { |
std::istringstream iss(contents); |
std::string line; |
while (std::getline(iss, line)) { |
- if (line.compare(0, strlen(kModelNamePrefix), kModelNamePrefix) == 0) { |
- cpu_brand.assign(line.substr(strlen(kModelNamePrefix))); |
- break; |
+ if (brand_.empty() && |
+ (line.compare(0, strlen(kModelNamePrefix), kModelNamePrefix) == 0 || |
+ line.compare(0, strlen(kProcessorPrefix), kProcessorPrefix) == 0)) { |
+ brand_.assign(line.substr(strlen(kModelNamePrefix))); |
} |
- if (line.compare(0, strlen(kProcessorPrefix), kProcessorPrefix) == 0) { |
- cpu_brand.assign(line.substr(strlen(kProcessorPrefix))); |
- break; |
+ |
+ for (size_t i = 0; i < arraysize(kUnsignedValues); i++) { |
+ const char *key = kUnsignedValues[i].key; |
+ const size_t len = strlen(key); |
+ |
+ if (line.compare(0, len, key) == 0 && |
+ line.size() >= len + 1 && |
+ (line[len] == '\t' || line[len] == ' ' || line[len] == ':')) { |
+ size_t colon_pos = line.find(':', len); |
+ if (colon_pos == std::string::npos) { |
+ continue; |
+ } |
+ |
+ const StringPiece line_sp(line); |
+ StringPiece value_sp = line_sp.substr(colon_pos + 1); |
+ while (!value_sp.empty() && |
+ (value_sp[0] == ' ' || value_sp[0] == '\t')) { |
+ value_sp = value_sp.substr(1); |
+ } |
+ |
+ // The string may have leading "0x" or not, so we use strtoul to |
+ // handle that. |
+ char *endptr; |
+ std::string value(value_sp.as_string()); |
+ unsigned long int result = strtoul(value.c_str(), &endptr, 0); |
+ if (*endptr == 0 && result <= UINT_MAX) { |
+ *kUnsignedValues[i].result = result; |
+ } |
+ } |
} |
} |
+ |
+ has_broken_neon_ = |
+ implementer == 0x51 && |
+ architecture == 7 && |
+ variant == 1 && |
+ part == 0x4d && |
+ revision == 0; |
} |
- return cpu_brand; |
-} |
-class LazyCpuInfoValue { |
- public: |
- LazyCpuInfoValue() : value_(ParseCpuInfo()) {} |
- const std::string& value() { return value_; } |
+ const std::string& brand() const { return brand_; } |
+ bool has_broken_neon() const { return has_broken_neon_; } |
private: |
- const std::string value_; |
+ std::string brand_; |
+ bool has_broken_neon_; |
DISALLOW_COPY_AND_ASSIGN(LazyCpuInfoValue); |
}; |
-base::LazyInstance<LazyCpuInfoValue> g_lazy_cpu_brand = |
+base::LazyInstance<LazyCpuInfoValue>::Leaky g_lazy_cpuinfo = |
LAZY_INSTANCE_INITIALIZER; |
-const std::string& CpuBrandInfo() { |
- return g_lazy_cpu_brand.Get().value(); |
-} |
- |
#endif // defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || |
// defined(OS_LINUX)) |
@@ -219,7 +269,8 @@ void CPU::Initialize() { |
has_non_stop_time_stamp_counter_ = (cpu_info[3] & (1 << 8)) != 0; |
} |
#elif defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX)) |
- cpu_brand_.assign(CpuBrandInfo()); |
+ cpu_brand_.assign(g_lazy_cpuinfo.Get().brand()); |
+ has_broken_neon_ = g_lazy_cpuinfo.Get().has_broken_neon(); |
#endif |
} |