Adding code. Closes #0.
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..c77f7bb
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,4 @@
+---
+Language:        Cpp
+BasedOnStyle:  Google
+...
\ No newline at end of file
diff --git a/BUILD b/BUILD
new file mode 100644
index 0000000..17c64de
--- /dev/null
+++ b/BUILD
@@ -0,0 +1,393 @@
+# cpu_features, a cross platform C89 library to get cpu features at runtime.
+package(
+    default_copts = [
+        "-DDISABLE_GOOGLE_GLOBAL_USING_DECLARATIONS",
+        "-Wno-implicit-fallthrough",
+        "-Ithird_party/cpu_features/include",
+    ],
+    default_visibility = ["//visibility:public"],
+    features = [
+        "-parse_headers",  # disabled because tests (C++) depends on C target compiled with -std=gnu89.
+        "-layering_check",  # disabled because it depends on parse_headers.
+    ],
+)
+
+licenses(["notice"])
+
+# MOE:begin_strip
+filegroup(
+    name = "opensource_filegroup",
+    srcs = [
+        ".clang-format",
+        "BUILD",
+        "CMakeLists.txt",
+        "CMakeLists.txt.in",
+        "CONTRIBUTING.md",
+        "LICENSE",
+        "OWNERS",
+        "README.md",
+        "WORKSPACE",
+        "include/cpu_features_macros.h",
+        "include/cpuinfo_aarch64.h",
+        "include/cpuinfo_arm.h",
+        "include/cpuinfo_mips.h",
+        "include/cpuinfo_x86.h",
+        "include/internal/bit_utils.h",
+        "include/internal/cpuid_x86.h",
+        "include/internal/filesystem.h",
+        "include/internal/hwcaps.h",
+        "include/internal/linux_features_aggregator.h",
+        "include/internal/stack_line_reader.h",
+        "include/internal/string_view.h",
+        "src/cpuid_x86_clang.c",
+        "src/cpuid_x86_gcc.c",
+        "src/cpuid_x86_msvc.c",
+        "src/cpuinfo_aarch64.c",
+        "src/cpuinfo_arm.c",
+        "src/cpuinfo_mips.c",
+        "src/cpuinfo_x86.c",
+        "src/filesystem.c",
+        "src/hwcaps.c",
+        "src/linux_features_aggregator.c",
+        "src/list_cpu_features.cc",
+        "src/stack_line_reader.c",
+        "src/string_view.c",
+        "test/CMakeLists.txt",
+        "test/bit_utils_test.cc",
+        "test/cpuinfo_aarch64_test.cc",
+        "test/cpuinfo_arm_test.cc",
+        "test/cpuinfo_mips_test.cc",
+        "test/cpuinfo_x86_test.cc",
+        "test/filesystem_for_testing.cc",
+        "test/filesystem_for_testing.h",
+        "test/hwcaps_for_testing.cc",
+        "test/hwcaps_for_testing.h",
+        "test/linux_features_aggregator_test.cc",
+        "test/stack_line_reader_test.cc",
+        "test/string_view_test.cc",
+    ],
+    visibility = ["//third_party/cpu_features:__subpackages__"],
+)
+
+# MOE:end_strip
+
+exports_files(["LICENSE"])
+
+vardef(
+    "GNU89_FLAGS",
+    "-std=gnu89 " +
+    "-Wall " +
+    "-Wdeclaration-after-statement " +
+    "-Wextra " +
+    "-Wmissing-declarations " +
+    "-Wmissing-prototypes " +
+    "-Wold-style-definition " +
+    "-Wshadow " +
+    "-Wsign-compare " +
+    "-Wstrict-prototypes ",
+)
+
+cc_library(
+    name = "cpu_features_macros",
+    srcs = ["include/cpu_features_macros.h"],
+    copts = [varref("GNU89_FLAGS")],
+)
+
+cc_library(
+    name = "bit_utils",
+    srcs = ["include/internal/bit_utils.h"],
+    copts = [varref("GNU89_FLAGS")],
+    deps = [":cpu_features_macros"],
+)
+
+cc_test(
+    name = "bit_utils_test",
+    srcs = ["test/bit_utils_test.cc"],
+    deps = [
+        ":bit_utils",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_library(
+    name = "string_view",
+    srcs = ["src/string_view.c"],
+    hdrs = ["include/internal/string_view.h"],
+    copts = [varref("GNU89_FLAGS")],
+    deps = [":cpu_features_macros"],
+)
+
+cc_test(
+    name = "string_view_test",
+    srcs = ["test/string_view_test.cc"],
+    deps = [
+        ":string_view",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_library(
+    name = "filesystem",
+    srcs = [
+        "include/internal/filesystem.h",
+        "src/filesystem.c",
+    ],
+    copts = [
+        varref("GNU89_FLAGS"),
+    ],
+    deps = [":cpu_features_macros"],
+)
+
+cc_library(
+    name = "filesystem_for_testing",
+    testonly = 1,
+    srcs = [
+        "include/internal/filesystem.h",
+        "test/filesystem_for_testing.cc",
+        "test/filesystem_for_testing.h",
+    ],
+    deps = [
+        ":cpu_features_macros",
+        "//base",
+    ],
+)
+
+cc_library(
+    name = "stack_line_reader",
+    srcs = [
+        "include/internal/stack_line_reader.h",
+        "src/stack_line_reader.c",
+    ],
+    copts = [varref("GNU89_FLAGS")],
+    defines = ["STACK_LINE_READER_BUFFER_SIZE=1024"],
+    deps = [
+        ":cpu_features_macros",
+        ":filesystem",
+        ":string_view",
+    ],
+)
+
+cc_library(
+    name = "stack_line_reader_for_testing",
+    testonly = 1,
+    srcs = [
+        "include/internal/stack_line_reader.h",
+        "src/stack_line_reader.c",
+    ],
+    copts = [varref("GNU89_FLAGS")],
+    defines = ["STACK_LINE_READER_BUFFER_SIZE=1024"],
+    deps = [
+        ":cpu_features_macros",
+        ":filesystem_for_testing",
+        ":string_view",
+    ],
+)
+
+cc_test(
+    name = "stack_line_reader_test",
+    srcs = [
+        "include/internal/stack_line_reader.h",
+        "src/stack_line_reader.c",
+        "test/stack_line_reader_test.cc",
+    ],
+    defines = ["STACK_LINE_READER_BUFFER_SIZE=16"],
+    deps = [
+        ":cpu_features_macros",
+        ":filesystem_for_testing",
+        ":string_view",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_library(
+    name = "hwcaps",
+    srcs = [
+        "include/internal/hwcaps.h",
+        "src/hwcaps.c",
+    ],
+    copts = [varref("GNU89_FLAGS")],
+    deps = [
+        ":cpu_features_macros",
+        ":filesystem",
+    ],
+)
+
+cc_library(
+    name = "hwcaps_for_testing",
+    testonly = 1,
+    srcs = ["test/hwcaps_for_testing.cc"],
+    hdrs = [
+        "include/internal/hwcaps.h",
+        "test/hwcaps_for_testing.h",
+    ],
+    deps = [":cpu_features_macros"],
+)
+
+cc_library(
+    name = "linux_features_aggregator",
+    srcs = [
+        "include/internal/linux_features_aggregator.h",
+        "src/linux_features_aggregator.c",
+    ],
+    copts = [varref("GNU89_FLAGS")],
+    deps = [
+        ":hwcaps",
+        ":string_view",
+    ],
+)
+
+cc_test(
+    name = "linux_features_aggregator_test",
+    srcs = ["test/linux_features_aggregator_test.cc"],
+    deps = [
+        ":linux_features_aggregator",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_library(
+    name = "cpuinfo_mips",
+    srcs = [
+        "include/cpuinfo_mips.h",
+        "src/cpuinfo_mips.c",
+    ],
+    copts = [varref("GNU89_FLAGS")],
+    deps = [
+        ":linux_features_aggregator",
+        ":stack_line_reader",
+        ":string_view",
+    ],
+)
+
+cc_test(
+    name = "cpuinfo_mips_test",
+    srcs = [
+        "include/cpuinfo_mips.h",
+        "src/cpuinfo_mips.c",
+        "test/cpuinfo_mips_test.cc",
+    ],
+    deps = [
+        ":filesystem_for_testing",
+        ":hwcaps_for_testing",
+        ":linux_features_aggregator",
+        ":stack_line_reader",
+        ":string_view",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_library(
+    name = "cpuinfo_aarch64",
+    srcs = [
+        "include/cpuinfo_aarch64.h",
+        "src/cpuinfo_aarch64.c",
+    ],
+    copts = [varref("GNU89_FLAGS")],
+    deps = [
+        ":linux_features_aggregator",
+        ":stack_line_reader",
+        ":string_view",
+    ],
+)
+
+cc_test(
+    name = "cpuinfo_aarch64_test",
+    srcs = [
+        "include/cpuinfo_aarch64.h",
+        "src/cpuinfo_aarch64.c",
+        "test/cpuinfo_aarch64_test.cc",
+    ],
+    deps = [
+        ":filesystem_for_testing",
+        ":hwcaps_for_testing",
+        ":linux_features_aggregator",
+        ":stack_line_reader",
+        ":string_view",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_library(
+    name = "cpuinfo_arm",
+    srcs = [
+        "include/cpuinfo_arm.h",
+        "src/cpuinfo_arm.c",
+    ],
+    copts = [varref("GNU89_FLAGS")],
+    deps = [
+        ":bit_utils",
+        ":linux_features_aggregator",
+        ":stack_line_reader",
+        ":string_view",
+    ],
+)
+
+cc_test(
+    name = "cpuinfo_arm_test",
+    srcs = [
+        "include/cpuinfo_arm.h",
+        "src/cpuinfo_arm.c",
+        "test/cpuinfo_arm_test.cc",
+    ],
+    deps = [
+        ":bit_utils",
+        ":filesystem_for_testing",
+        ":hwcaps_for_testing",
+        ":linux_features_aggregator",
+        ":stack_line_reader",
+        ":string_view",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_library(
+    name = "cpuid_x86",
+    srcs = [
+        "include/internal/cpuid_x86.h",
+        "src/cpuid_x86_clang.c",
+        "src/cpuid_x86_gcc.c",
+        "src/cpuid_x86_msvc.c",
+    ],
+    copts = [varref("GNU89_FLAGS")],
+    deps = [":cpu_features_macros"],
+)
+
+cc_library(
+    name = "cpuinfo_x86",
+    srcs = ["src/cpuinfo_x86.c"],
+    hdrs = ["include/cpuinfo_x86.h"],
+    copts = [varref("GNU89_FLAGS")],
+    deps = [
+        ":bit_utils",
+        ":cpu_features_macros",
+        ":cpuid_x86",
+    ],
+)
+
+cc_test(
+    name = "cpuinfo_x86_test",
+    srcs = [
+        "include/cpuinfo_x86.h",
+        "src/cpuinfo_x86.c",
+        "test/cpuinfo_x86_test.cc",
+    ],
+    defines = ["CPU_FEATURES_TEST"],
+    deps = [
+        ":bit_utils",
+        ":cpu_features_macros",
+        ":cpuid_x86",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_binary(
+    name = "list_cpu_features",
+    srcs = ["src/list_cpu_features.cc"],
+    deps = [
+        ":cpu_features_macros",
+        ":cpuinfo_aarch64",
+        ":cpuinfo_arm",
+        ":cpuinfo_mips",
+        ":cpuinfo_x86",
+    ],
+)
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..3caa233
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,93 @@
+cmake_minimum_required(VERSION 3.0)
+
+project(CpuFeatures)
+
+#
+# library : cpu_features
+#
+
+add_library(cpu_features
+  include/cpuinfo_aarch64.h
+  include/cpuinfo_arm.h
+  include/cpuinfo_mips.h
+  include/cpuinfo_x86.h
+  include/internal/bit_utils.h
+  include/internal/linux_features_aggregator.h
+  include/internal/cpuid_x86.h
+  include/internal/filesystem.h
+  include/internal/hwcaps.h
+  include/internal/stack_line_reader.h
+  include/internal/string_view.h
+  include/cpu_features_macros.h
+  src/linux_features_aggregator.c
+  src/cpuid_x86_clang.c
+  src/cpuid_x86_gcc.c
+  src/cpuid_x86_msvc.c
+  src/cpuinfo_aarch64.c
+  src/cpuinfo_arm.c
+  src/cpuinfo_mips.c
+  src/cpuinfo_x86.c
+  src/filesystem.c
+  src/hwcaps.c
+  src/stack_line_reader.c
+  src/string_view.c
+)
+
+target_include_directories(cpu_features PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>)
+target_include_directories(cpu_features PRIVATE include/internal)
+target_compile_definitions(cpu_features PUBLIC STACK_LINE_READER_BUFFER_SIZE=1024)
+target_link_libraries(cpu_features PUBLIC ${CMAKE_DL_LIBS})
+
+#
+# program : list_cpu_features
+#
+
+add_executable(list_cpu_features src/list_cpu_features.cc)
+target_link_libraries(list_cpu_features PRIVATE cpu_features)
+target_compile_features(list_cpu_features PRIVATE cxx_range_for)
+
+#
+# tests
+#
+
+include(CTest)
+if(BUILD_TESTING)
+  # Download and unpack googletest at configure time.
+  configure_file(CMakeLists.txt.in googletest-download/CMakeLists.txt)
+
+  execute_process(
+    COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
+    RESULT_VARIABLE result
+    WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download )
+
+  if(result)
+    message(FATAL_ERROR "CMake step for googletest failed: ${result}")
+  endif()
+
+  execute_process(
+    COMMAND ${CMAKE_COMMAND} --build .
+    RESULT_VARIABLE result
+    WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download )
+
+  if(result)
+    message(FATAL_ERROR "Build step for googletest failed: ${result}")
+  endif()
+
+  # Prevent overriding the parent project's compiler/linker settings on Windows.
+  set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
+
+  # Add googletest directly to our build. This defines the gtest and gtest_main
+  # targets.
+  add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src
+                   ${CMAKE_BINARY_DIR}/googletest-build
+                   EXCLUDE_FROM_ALL)
+
+  # The gtest/gtest_main targets carry header search path dependencies
+  # automatically when using CMake 2.8.11 or later. Otherwise we have to add
+  # them here ourselves.
+  if (CMAKE_VERSION VERSION_LESS 2.8.11)
+    include_directories("${gtest_SOURCE_DIR}/include")
+  endif()
+  
+  add_subdirectory(test)
+endif()
diff --git a/CMakeLists.txt.in b/CMakeLists.txt.in
new file mode 100644
index 0000000..d60a33e
--- /dev/null
+++ b/CMakeLists.txt.in
@@ -0,0 +1,15 @@
+cmake_minimum_required(VERSION 2.8.2)
+
+project(googletest-download NONE)
+
+include(ExternalProject)
+ExternalProject_Add(googletest
+  GIT_REPOSITORY    https://github.com/google/googletest.git
+  GIT_TAG           master
+  SOURCE_DIR        "${CMAKE_BINARY_DIR}/googletest-src"
+  BINARY_DIR        "${CMAKE_BINARY_DIR}/googletest-build"
+  CONFIGURE_COMMAND ""
+  BUILD_COMMAND     ""
+  INSTALL_COMMAND   ""
+  TEST_COMMAND      ""
+)
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..c980350
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,23 @@
+# How to Contribute
+
+We'd love to accept your patches and contributions to this project. There are
+just a few small guidelines you need to follow.
+
+## Contributor License Agreement
+
+Contributions to this project must be accompanied by a Contributor License
+Agreement. You (or your employer) retain the copyright to your contribution;
+this simply gives us permission to use and redistribute your contributions as
+part of the project. Head over to <https://cla.developers.google.com/> to see
+your current agreements on file or to sign a new one.
+
+You generally only need to submit a CLA once, so if you've already submitted one
+(even if it was for a different project), you probably don't need to do it
+again.
+
+## Code reviews
+
+All submissions, including submissions by project members, require review. We
+use GitHub pull requests for this purpose. Consult
+[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
+information on using pull requests.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..7a4a3ea
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
\ No newline at end of file
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..614c085
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,4 @@
+gchatelet
+courbet
+
+mdb-group:research-compilers-team
diff --git a/README.md b/README.md
index 2bac3c8..4cff8c8 100644
--- a/README.md
+++ b/README.md
@@ -66,12 +66,12 @@
 
 ## What does it currently support
 
-                             | x86 | ARM | aarch64 | mips   | POWER
----------------------------- | :-: | :-: | :-----: | :----: | :-----:
-Features From cpu            | yes | no* | no*     | no yet | not yet
-Features From Linux          | no  | yes | yes     | yes    | not yet
-Micro Architecture Detection | yes | no  | no      | no     | not yet
-Windows support              | yes | no  | no      | no     | not yet
+|                             | x86 | ARM | aarch64 |  mips  |  POWER  |
+|---------------------------- | :-: | :-: | :-----: | :----: | :-----: |
+|Features From cpu            | yes | no* | no*     | no yet | not yet |
+|Features From Linux          | no  | yes | yes     | yes    | not yet |
+|Micro Architecture Detection | yes | no  | no      | no     | not yet |
+|Windows support              | yes | no  | no      | no     | not yet |
 
 -   **Features From Cpuid**: features are retrieved by using the cpuid
     instruction. (*) Unfortunately this instruction is privileged for some
diff --git a/WORKSPACE b/WORKSPACE
new file mode 100644
index 0000000..8ea8a8b
--- /dev/null
+++ b/WORKSPACE
@@ -0,0 +1,7 @@
+# ===== googletest =====
+
+git_repository(
+    name = "com_google_googletest",
+    remote = "https://github.com/google/googletest.git",
+    commit = "c3f65335b79f47b05629e79a54685d899bc53b93",
+)
diff --git a/include/cpu_features_macros.h b/include/cpu_features_macros.h
new file mode 100644
index 0000000..a8d4229
--- /dev/null
+++ b/include/cpu_features_macros.h
@@ -0,0 +1,121 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef THIRD_PARTY_CPU_FEATURES_INCLUDE_CPU_FEATURES_MACROS_H_
+#define THIRD_PARTY_CPU_FEATURES_INCLUDE_CPU_FEATURES_MACROS_H_
+
+////////////////////////////////////////////////////////////////////////////////
+// Architectures
+////////////////////////////////////////////////////////////////////////////////
+
+#if ((defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || \
+      defined(__x86_64__)) &&                                     \
+     !defined(__pnacl__) && !defined(__CLR_VER))
+#define CPU_FEATURES_ARCH_X86
+#endif
+
+#if (defined(__arm__) || defined(_M_ARM))
+#define CPU_FEATURES_ARCH_ARM
+#endif
+
+#if defined(__aarch64__)
+#define CPU_FEATURES_ARCH_AARCH64
+#endif
+
+#if (defined(CPU_FEATURES_ARCH_AARCH64) || defined(CPU_FEATURES_ARCH_ARM))
+#define CPU_FEATURES_ARCH_ANY_ARM
+#endif
+
+#if defined(__mips__)
+#define CPU_FEATURES_ARCH_MIPS
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Os
+////////////////////////////////////////////////////////////////////////////////
+
+#if defined(__linux__)
+#define CPU_FEATURES_OS_LINUX_OR_ANDROID
+#endif
+
+#if defined(__ANDROID__)
+#define CPU_FEATURES_OS_ANDROID
+#endif
+
+#if (defined(_WIN64) || defined(_WIN32))
+#define CPU_FEATURES_OS_WINDOWS
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Compilers
+////////////////////////////////////////////////////////////////////////////////
+
+#if defined(__clang__)
+#define CPU_FEATURES_COMPILER_CLANG
+#endif
+
+#if defined(__GNUC__) && !defined(__clang__)
+#define CPU_FEATURES_COMPILER_GCC
+#endif
+
+#if defined(_MSC_VER)
+#define CPU_FEATURES_COMPILER_MSC
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Cpp
+////////////////////////////////////////////////////////////////////////////////
+
+#if defined(__cplusplus)
+#define START_CPP_NAMESPACE \
+  namespace cpu_features {  \
+  extern "C" {
+#define END_CPP_NAMESPACE \
+  }                       \
+  }
+#else
+#define START_CPP_NAMESPACE
+#define END_CPP_NAMESPACE
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Compiler flags
+////////////////////////////////////////////////////////////////////////////////
+
+// Use the following to check if a feature is known to be available at compile
+// time. See README.md for an example.
+#if defined(CPU_FEATURES_ARCH_X86)
+#define CPU_FEATURES_COMPILED_X86_AES defined(__AES__)
+#define CPU_FEATURES_COMPILED_X86_F16C defined(__F16C__)
+#define CPU_FEATURES_COMPILED_X86_BMI defined(__BMI__)
+#define CPU_FEATURES_COMPILED_X86_BMI2 defined(__BMI2__)
+#define CPU_FEATURES_COMPILED_X86_SSE (defined(__SSE__) || (_M_IX86_FP >= 1))
+#define CPU_FEATURES_COMPILED_X86_SSE2 (defined(__SSE2__) || (_M_IX86_FP >= 2))
+#define CPU_FEATURES_COMPILED_X86_SSE3 defined(__SSE3__)
+#define CPU_FEATURES_COMPILED_X86_SSSE3 defined(__SSSE3__)
+#define CPU_FEATURES_COMPILED_X86_SSE4_1 defined(__SSE4_1__)
+#define CPU_FEATURES_COMPILED_X86_SSE4_2 defined(__SSE4_2__)
+#define CPU_FEATURES_COMPILED_X86_AVX defined(__AVX__)
+#define CPU_FEATURES_COMPILED_x86_AVX2 defined(__AVX2__)
+#endif
+
+#if defined(CPU_FEATURES_ARCH_ANY_ARM)
+#define CPU_FEATURES_COMPILED_ANY_ARM_NEON defined(__ARM_NEON__)
+#endif
+
+#if defined(CPU_FEATURES_ARCH_MIPS)
+#define CPU_FEATURES_COMPILED_MIPS_MSA defined(__mips_msa)
+#endif
+
+#endif  // THIRD_PARTY_CPU_FEATURES_INCLUDE_CPU_FEATURES_MACROS_H_
diff --git a/include/cpuinfo_aarch64.h b/include/cpuinfo_aarch64.h
new file mode 100644
index 0000000..3948153
--- /dev/null
+++ b/include/cpuinfo_aarch64.h
@@ -0,0 +1,65 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef THIRD_PARTY_CPU_FEATURES_INCLUDE_CPUINFO_AARCH64_H_
+#define THIRD_PARTY_CPU_FEATURES_INCLUDE_CPUINFO_AARCH64_H_
+
+#include "cpu_features_macros.h"
+
+START_CPP_NAMESPACE
+
+typedef struct {
+  int fp : 1;     // Floating-point.
+  int asimd : 1;  // Advanced SIMD.
+  int aes : 1;    // Hardware-accelerated Advanced Encryption Standard.
+  int pmull : 1;  // Polynomial multiply long.
+  int sha1 : 1;   // Hardware-accelerated SHA1.
+  int sha2 : 1;   // Hardware-accelerated SHA2-256.
+  int crc32 : 1;  // Hardware-accelerated CRC-32.
+
+  // Make sure to update Aarch64FeaturesEnum below if you add a field here.
+} Aarch64Features;
+
+typedef struct {
+  Aarch64Features features;
+  int implementer;
+  int variant;
+  int part;
+  int revision;
+} Aarch64Info;
+
+Aarch64Info GetAarch64Info(void);
+
+////////////////////////////////////////////////////////////////////////////////
+// Introspection functions
+
+typedef enum {
+  AARCH64_FP,
+  AARCH64_ASIMD,
+  AARCH64_AES,
+  AARCH64_PMULL,
+  AARCH64_SHA1,
+  AARCH64_SHA2,
+  AARCH64_CRC32,
+  AARCH64_LAST_,
+} Aarch64FeaturesEnum;
+
+int GetAarch64FeaturesEnumValue(const Aarch64Features* features,
+                                Aarch64FeaturesEnum value);
+
+const char* GetAarch64FeaturesEnumName(Aarch64FeaturesEnum);
+
+END_CPP_NAMESPACE
+
+#endif  // THIRD_PARTY_CPU_FEATURES_INCLUDE_CPUINFO_AARCH64_H_
diff --git a/include/cpuinfo_arm.h b/include/cpuinfo_arm.h
new file mode 100644
index 0000000..cc7e2d9
--- /dev/null
+++ b/include/cpuinfo_arm.h
@@ -0,0 +1,80 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef THIRD_PARTY_CPU_FEATURES_INCLUDE_CPUINFO_ARM_H_
+#define THIRD_PARTY_CPU_FEATURES_INCLUDE_CPUINFO_ARM_H_
+
+#include "cpu_features_macros.h"
+
+START_CPP_NAMESPACE
+
+typedef struct {
+  int vfp : 1;       // Vector Floating Point.
+  int iwmmxt : 1;    // Intel Wireless MMX Technology.
+  int neon : 1;      // Advanced SIMD.
+  int vfpv3 : 1;     // VFP version 3
+  int vfpv3d16 : 1;  // VFP version 3 with 16 D-registers
+  int vfpv4 : 1;     // VFP version 4 with fast context switching
+  int idiva : 1;     // SDIV and UDIV hardware division in ARM mode.
+  int idivt : 1;     // SDIV and UDIV hardware division in Thumb mode.
+  int aes : 1;       // Hardware-accelerated Advanced Encryption Standard.
+  int pmull : 1;     // Polynomial multiply long.
+  int sha1 : 1;      // Hardware-accelerated SHA1.
+  int sha2 : 1;      // Hardware-accelerated SHA2-256.
+  int crc32 : 1;     // Hardware-accelerated CRC-32.
+
+  // Make sure to update ArmFeaturesEnum below if you add a field here.
+} ArmFeatures;
+
+typedef struct {
+  ArmFeatures features;
+  int implementer;
+  int architecture;
+  int variant;
+  int part;
+  int revision;
+} ArmInfo;
+
+// TODO(user): Add macros to know which features are present at compile
+// time.
+
+ArmInfo GetArmInfo(void);
+
+////////////////////////////////////////////////////////////////////////////////
+// Introspection functions
+
+typedef enum {
+  ARM_VFP,
+  ARM_IWMMXT,
+  ARM_NEON,
+  ARM_VFPV3,
+  ARM_VFPV3D16,
+  ARM_VFPV4,
+  ARM_IDIVA,
+  ARM_IDIVT,
+  ARM_AES,
+  ARM_PMULL,
+  ARM_SHA1,
+  ARM_SHA2,
+  ARM_CRC32,
+  ARM_LAST_,
+} ArmFeaturesEnum;
+
+int GetArmFeaturesEnumValue(const ArmFeatures* features, ArmFeaturesEnum value);
+
+const char* GetArmFeaturesEnumName(ArmFeaturesEnum);
+
+END_CPP_NAMESPACE
+
+#endif  // THIRD_PARTY_CPU_FEATURES_INCLUDE_CPUINFO_ARM_H_
diff --git a/include/cpuinfo_mips.h b/include/cpuinfo_mips.h
new file mode 100644
index 0000000..bcece1e
--- /dev/null
+++ b/include/cpuinfo_mips.h
@@ -0,0 +1,53 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef THIRD_PARTY_CPU_FEATURES_INCLUDE_CPUINFO_MIPS_H_
+#define THIRD_PARTY_CPU_FEATURES_INCLUDE_CPUINFO_MIPS_H_
+
+#include "cpu_features_macros.h"
+
+START_CPP_NAMESPACE
+
+typedef struct {
+  int msa : 1;  // MIPS SIMD Architecture
+                // https://www.mips.com/products/architectures/ase/simd/
+  int eva : 1;  // Enhanced Virtual Addressing
+                // https://www.mips.com/products/architectures/mips64/
+
+  // Make sure to update MipsFeaturesEnum below if you add a field here.
+} MipsFeatures;
+
+typedef struct {
+  MipsFeatures features;
+} MipsInfo;
+
+MipsInfo GetMipsInfo(void);
+
+////////////////////////////////////////////////////////////////////////////////
+// Introspection functions
+
+typedef enum {
+  MIPS_MSA,
+  MIPS_EVA,
+  MIPS_LAST_,
+} MipsFeaturesEnum;
+
+int GetMipsFeaturesEnumValue(const MipsFeatures* features,
+                             MipsFeaturesEnum value);
+
+const char* GetMipsFeaturesEnumName(MipsFeaturesEnum);
+
+END_CPP_NAMESPACE
+
+#endif  // THIRD_PARTY_CPU_FEATURES_INCLUDE_CPUINFO_MIPS_H_
diff --git a/include/cpuinfo_x86.h b/include/cpuinfo_x86.h
new file mode 100644
index 0000000..bad8e4d
--- /dev/null
+++ b/include/cpuinfo_x86.h
@@ -0,0 +1,147 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef THIRD_PARTY_CPU_FEATURES_INCLUDE_CPUINFO_X86_H_
+#define THIRD_PARTY_CPU_FEATURES_INCLUDE_CPUINFO_X86_H_
+
+#include "cpu_features_macros.h"
+
+START_CPP_NAMESPACE
+
+// See https://en.wikipedia.org/wiki/CPUID for a list of x86 cpu features.
+typedef struct {
+  int aes : 1;
+  int erms : 1;
+  int f16c : 1;
+  int fma3 : 1;
+  int vpclmulqdq : 1;
+  int bmi1 : 1;
+  int bmi2 : 1;
+
+  int ssse3 : 1;
+  int sse4_1 : 1;
+  int sse4_2 : 1;
+
+  int avx : 1;
+  int avx2 : 1;
+
+  int avx512f : 1;
+  int avx512cd : 1;
+  int avx512er : 1;
+  int avx512pf : 1;
+  int avx512bw : 1;
+  int avx512dq : 1;
+  int avx512vl : 1;
+  int avx512ifma : 1;
+  int avx512vbmi : 1;
+  int avx512vbmi2 : 1;
+  int avx512vnni : 1;
+  int avx512bitalg : 1;
+  int avx512vpopcntdq : 1;
+  int avx512_4vnniw : 1;
+  int avx512_4vbmi2 : 1;
+
+  // Make sure to update X86FeaturesEnum below if you add a field here.
+} X86Features;
+
+typedef struct {
+  X86Features features;
+  int family;
+  int model;
+  int stepping;
+  char vendor[13];  // 0 terminated string
+} X86Info;
+
+// Calls cpuid and returns an initialized X86info.
+// This function is guaranteed to be malloc, memset and memcpy free.
+X86Info GetX86Info(void);
+
+typedef enum {
+  X86_UNKNOWN,
+  INTEL_CORE,      // CORE
+  INTEL_PNR,       // PENRYN
+  INTEL_NHM,       // NEHALEM
+  INTEL_ATOM_BNL,  // BONNELL
+  INTEL_WSM,       // WESTMERE
+  INTEL_SNB,       // SANDYBRIDGE
+  INTEL_IVB,       // IVYBRIDGE
+  INTEL_ATOM_SMT,  // SILVERMONT
+  INTEL_HSW,       // HASWELL
+  INTEL_BDW,       // BROADWELL
+  INTEL_SKL,       // SKYLAKE
+  INTEL_ATOM_GMT,  // GOLDMONT
+  INTEL_KBL,       // KABY LAKE
+  INTEL_CFL,       // COFFEE LAKE
+  INTEL_CNL,       // CANON LAKE
+  AMD_HAMMER,      // K8
+  AMD_K10,         // K10
+  AMD_BOBCAT,      // K14
+  AMD_BULLDOZER,   // K15
+  AMD_JAGUAR,      // K16
+  AMD_ZEN,         // K17
+} X86Microarchitecture;
+
+// Returns the underlying microarchitecture by looking at X86Info's vendor,
+// family and model.
+X86Microarchitecture GetX86Microarchitecture(const X86Info* info);
+
+// Calls cpuid and fills the brand_string.
+// - brand_string *must* be of size 49 (beware of array decaying).
+// - brand_string will be zero terminated.
+// - This function calls memcpy.
+void FillX86BrandString(char brand_string[49]);
+
+////////////////////////////////////////////////////////////////////////////////
+// Introspection functions
+
+typedef enum {
+  X86_AES,
+  X86_ERMS,
+  X86_F16C,
+  X86_FMA3,
+  X86_VPCLMULQDQ,
+  X86_BMI1,
+  X86_BMI2,
+  X86_SSSE3,
+  X86_SSE4_1,
+  X86_SSE4_2,
+  X86_AVX,
+  X86_AVX2,
+  X86_AVX512F,
+  X86_AVX512CD,
+  X86_AVX512ER,
+  X86_AVX512PF,
+  X86_AVX512BW,
+  X86_AVX512DQ,
+  X86_AVX512VL,
+  X86_AVX512IFMA,
+  X86_AVX512VBMI,
+  X86_AVX512VBMI2,
+  X86_AVX512VNNI,
+  X86_AVX512BITALG,
+  X86_AVX512VPOPCNTDQ,
+  X86_AVX512_4VNNIW,
+  X86_AVX512_4VBMI2,
+  X86_LAST_,
+} X86FeaturesEnum;
+
+int GetX86FeaturesEnumValue(const X86Features* features, X86FeaturesEnum value);
+
+const char* GetX86FeaturesEnumName(X86FeaturesEnum);
+
+const char* GetX86MicroarchitectureName(X86Microarchitecture);
+
+END_CPP_NAMESPACE
+
+#endif  // THIRD_PARTY_CPU_FEATURES_INCLUDE_CPUINFO_X86_H_
diff --git a/include/internal/bit_utils.h b/include/internal/bit_utils.h
new file mode 100644
index 0000000..b2d42fe
--- /dev/null
+++ b/include/internal/bit_utils.h
@@ -0,0 +1,39 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef THIRD_PARTY_CPU_FEATURES_INCLUDE_INTERNAL_BIT_UTILS_H_
+#define THIRD_PARTY_CPU_FEATURES_INCLUDE_INTERNAL_BIT_UTILS_H_
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include "cpu_features_macros.h"
+
+START_CPP_NAMESPACE
+
+inline static bool IsBitSet(uint32_t reg, uint32_t bit) {
+  return (reg >> bit) & 0x1;
+}
+
+inline static uint32_t ExtractBitRange(uint32_t reg, uint32_t msb,
+                                       uint32_t lsb) {
+  const uint64_t bits = msb - lsb + 1;
+  const uint64_t mask = (1ULL << bits) - 1ULL;
+  assert(msb >= lsb);
+  return (reg >> lsb) & mask;
+}
+
+END_CPP_NAMESPACE
+
+#endif  // THIRD_PARTY_CPU_FEATURES_INCLUDE_INTERNAL_BIT_UTILS_H_
diff --git a/include/internal/cpuid_x86.h b/include/internal/cpuid_x86.h
new file mode 100644
index 0000000..dea1ffd
--- /dev/null
+++ b/include/internal/cpuid_x86.h
@@ -0,0 +1,37 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef THIRD_PARTY_CPU_FEATURES_INCLUDE_INTERNAL_CPUID_X86_H_
+#define THIRD_PARTY_CPU_FEATURES_INCLUDE_INTERNAL_CPUID_X86_H_
+
+#include <stdint.h>
+
+#include "cpu_features_macros.h"
+
+START_CPP_NAMESPACE
+
+// A struct to hold the result of a call to cpuid.
+typedef struct {
+  uint32_t eax, ebx, ecx, edx;
+} Leaf;
+
+// Retrieves the leaf for a particular cpuid.
+Leaf CpuId(uint32_t leaf_id);
+
+// Returns the eax value of the XCR0 register.
+uint32_t GetXCR0Eax(void);
+
+END_CPP_NAMESPACE
+
+#endif  // THIRD_PARTY_CPU_FEATURES_INCLUDE_INTERNAL_CPUID_X86_H_
diff --git a/include/internal/filesystem.h b/include/internal/filesystem.h
new file mode 100644
index 0000000..da4a789
--- /dev/null
+++ b/include/internal/filesystem.h
@@ -0,0 +1,38 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// An interface for the filesystem that allows mocking the filesystem in
+// unittests.
+#ifndef THIRD_PARTY_CPU_FEATURES_INCLUDE_INTERNAL_FILESYSTEM_H_
+#define THIRD_PARTY_CPU_FEATURES_INCLUDE_INTERNAL_FILESYSTEM_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include "cpu_features_macros.h"
+
+START_CPP_NAMESPACE
+
+// Same as linux "open(filename, O_RDONLY)", retries automatically on EINTR.
+int OpenFile(const char* filename);
+
+// Same as linux "read(file_descriptor, buffer, buffer_size)", retries
+// automatically on EINTR.
+int ReadFile(int file_descriptor, void* buffer, size_t buffer_size);
+
+// Same as linux "close(file_descriptor)".
+void CloseFile(int file_descriptor);
+
+END_CPP_NAMESPACE
+
+#endif  // THIRD_PARTY_CPU_FEATURES_INCLUDE_INTERNAL_FILESYSTEM_H_
diff --git a/include/internal/hwcaps.h b/include/internal/hwcaps.h
new file mode 100644
index 0000000..e220b33
--- /dev/null
+++ b/include/internal/hwcaps.h
@@ -0,0 +1,73 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Interface to retrieve hardware capabilities. It relies on Linux's getauxval
+// or `/proc/self/auxval` under the hood.
+#ifndef THIRD_PARTY_CPU_FEATURES_INCLUDE_INTERNAL_HWCAPS_H_
+#define THIRD_PARTY_CPU_FEATURES_INCLUDE_INTERNAL_HWCAPS_H_
+
+#include <stdint.h>
+#include "cpu_features_macros.h"
+
+START_CPP_NAMESPACE
+
+// To avoid depending on the linux kernel we reproduce the architecture specific
+// constants here.
+
+// http://elixir.free-electrons.com/linux/latest/source/arch/arm64/include/uapi/asm/hwcap.h
+#define AARCH64_HWCAP_FP (1UL << 0)
+#define AARCH64_HWCAP_ASIMD (1UL << 1)
+#define AARCH64_HWCAP_AES (1UL << 3)
+#define AARCH64_HWCAP_PMULL (1UL << 4)
+#define AARCH64_HWCAP_SHA1 (1UL << 5)
+#define AARCH64_HWCAP_SHA2 (1UL << 6)
+#define AARCH64_HWCAP_CRC32 (1UL << 7)
+
+// http://elixir.free-electrons.com/linux/latest/source/arch/arm/include/uapi/asm/hwcap.h
+#define ARM_HWCAP_VFP (1UL << 6)
+#define ARM_HWCAP_IWMMXT (1UL << 9)
+#define ARM_HWCAP_NEON (1UL << 12)
+#define ARM_HWCAP_VFPV3 (1UL << 13)
+#define ARM_HWCAP_VFPV3D16 (1UL << 14)
+#define ARM_HWCAP_VFPV4 (1UL << 16)
+#define ARM_HWCAP_IDIVA (1UL << 17)
+#define ARM_HWCAP_IDIVT (1UL << 18)
+#define ARM_HWCAP2_AES (1UL << 0)
+#define ARM_HWCAP2_PMULL (1UL << 1)
+#define ARM_HWCAP2_SHA1 (1UL << 2)
+#define ARM_HWCAP2_SHA2 (1UL << 3)
+#define ARM_HWCAP2_CRC32 (1UL << 4)
+
+// http://elixir.free-electrons.com/linux/latest/source/arch/mips/include/uapi/asm/hwcap.h
+#define MIPS_HWCAP_VZ (1UL << 0)
+#define MIPS_HWCAP_EVA (1UL << 1)
+#define MIPS_HWCAP_HTW (1UL << 2)
+#define MIPS_HWCAP_FPU (1UL << 3)
+#define MIPS_HWCAP_MIPS32R2 (1UL << 4)
+#define MIPS_HWCAP_MIPS32R5 (1UL << 5)
+#define MIPS_HWCAP_MIPS64R6 (1UL << 6)
+#define MIPS_HWCAP_DSPR1 (1UL << 7)
+#define MIPS_HWCAP_DSPR2 (1UL << 8)
+#define MIPS_HWCAP_MSA (1UL << 9)
+
+typedef struct {
+  uint32_t hwcaps;
+  uint32_t hwcaps2;
+} HardwareCapabilities;
+
+HardwareCapabilities GetHardwareCapabilities(void);
+
+END_CPP_NAMESPACE
+
+#endif  // THIRD_PARTY_CPU_FEATURES_INCLUDE_INTERNAL_HWCAPS_H_
diff --git a/include/internal/linux_features_aggregator.h b/include/internal/linux_features_aggregator.h
new file mode 100644
index 0000000..5939565
--- /dev/null
+++ b/include/internal/linux_features_aggregator.h
@@ -0,0 +1,58 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// CapabilityConfig provides a way to map cpu features to hardware caps and
+// /proc/cpuinfo flags. We then provide functions to update capabilities from
+// either source.
+#ifndef THIRD_PARTY_CPU_FEATURES_INCLUDE_INTERNAL_LINUX_FEATURES_AGGREGATOR_H_
+#define THIRD_PARTY_CPU_FEATURES_INCLUDE_INTERNAL_LINUX_FEATURES_AGGREGATOR_H_
+
+#include <ctype.h>
+#include <stdint.h>
+#include "cpu_features_macros.h"
+#include "internal/hwcaps.h"
+#include "internal/string_view.h"
+
+START_CPP_NAMESPACE
+
+// Use the following macro to declare setter functions to be used in
+// CapabilityConfig.
+#define DECLARE_SETTER(FeatureType, FeatureName)                    \
+  static void set_##FeatureName(void* const features, bool value) { \
+    ((FeatureType*)features)->FeatureName = value;                  \
+  }
+
+// Describes the relationship between hardware caps and /proc/cpuinfo flags.
+typedef struct {
+  const HardwareCapabilities hwcaps_mask;
+  const char* const proc_cpuinfo_flag;
+  void (*set_bit)(void* const, bool);  // setter for the corresponding bit.
+} CapabilityConfig;
+
+// For every config, looks into flags_line for the presence of the
+// corresponding proc_cpuinfo_flag, calls `set_bit` accordingly.
+// Note: features is a pointer to the underlying Feature struct.
+void SetFromFlags(const size_t configs_size, const CapabilityConfig* configs,
+                  const StringView flags_line, void* const features);
+
+// For every config, looks into hwcaps for the presence of the feature. Calls
+// `set_bit` with true if the hardware capability is found.
+// Note: features is a pointer to the underlying Feature struct.
+void OverrideFromHwCaps(const size_t configs_size,
+                        const CapabilityConfig* configs,
+                        const HardwareCapabilities hwcaps,
+                        void* const features);
+
+END_CPP_NAMESPACE
+#endif  // THIRD_PARTY_CPU_FEATURES_INCLUDE_INTERNAL_LINUX_FEATURES_AGGREGATOR_H_
diff --git a/include/internal/stack_line_reader.h b/include/internal/stack_line_reader.h
new file mode 100644
index 0000000..4fde348
--- /dev/null
+++ b/include/internal/stack_line_reader.h
@@ -0,0 +1,49 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Reads a file line by line and stores the data on the stack. This allows
+// parsing files in one go without allocating.
+#ifndef THIRD_PARTY_CPU_FEATURES_INCLUDE_INTERNAL_STACK_LINE_READER_H_
+#define THIRD_PARTY_CPU_FEATURES_INCLUDE_INTERNAL_STACK_LINE_READER_H_
+
+#include <stdbool.h>
+
+#include "cpu_features_macros.h"
+#include "internal/string_view.h"
+
+START_CPP_NAMESPACE
+
+typedef struct {
+  char buffer[STACK_LINE_READER_BUFFER_SIZE];
+  StringView view;
+  int fd;
+  bool skip_mode;
+} StackLineReader;
+
+// Initializes a StackLineReader.
+void StackLineReader_Initialize(StackLineReader* reader, int fd);
+
+typedef struct {
+  StringView line;  // A view of the line.
+  bool eof;         // Nothing more to read, we reached EOF.
+  bool full_line;   // If false the line was truncated to
+                    // STACK_LINE_READER_BUFFER_SIZE.
+} LineResult;
+
+// Reads the file pointed to by fd and tries to read a full line.
+LineResult StackLineReader_NextLine(StackLineReader* reader);
+
+END_CPP_NAMESPACE
+
+#endif  // THIRD_PARTY_CPU_FEATURES_INCLUDE_INTERNAL_STACK_LINE_READER_H_
diff --git a/include/internal/string_view.h b/include/internal/string_view.h
new file mode 100644
index 0000000..a528ad4
--- /dev/null
+++ b/include/internal/string_view.h
@@ -0,0 +1,101 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// A view over a piece of string. The view is not 0 terminated.
+#ifndef THIRD_PARTY_CPU_FEATURES_INCLUDE_INTERNAL_STRING_VIEW_H_
+#define THIRD_PARTY_CPU_FEATURES_INCLUDE_INTERNAL_STRING_VIEW_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include "cpu_features_macros.h"
+
+START_CPP_NAMESPACE
+
+typedef struct {
+  const char* ptr;
+  size_t size;
+} StringView;
+
+#ifdef __cplusplus
+static const StringView kEmptyStringView = {NULL, 0};
+#else
+static const StringView kEmptyStringView;
+#endif
+
+// Returns a StringView from the provided string.
+// Passing NULL is valid only if size is 0.
+static inline StringView view(const char* str, const size_t size) {
+  StringView view;
+  view.ptr = str;
+  view.size = size;
+  return view;
+}
+
+static inline StringView str(const char* str) { return view(str, strlen(str)); }
+
+// Returns the index of the first occurrence of c in view or -1 if not found.
+int IndexOfChar(const StringView view, char c);
+
+// Returns the index of the first occurrence of sub_view in view or -1 if not
+// found.
+int IndexOf(const StringView view, const StringView sub_view);
+
+// Returns whether a is equal to b (same content).
+bool IsEquals(const StringView a, const StringView b);
+
+// Returns whether a starts with b.
+bool StartsWith(const StringView a, const StringView b);
+
+// Removes count characters from the beginning of view or kEmptyStringView if
+// count if greater than view.size.
+StringView PopFront(const StringView view, size_t count);
+
+// Removes count characters from the end of view or kEmptyStringView if count if
+// greater than view.size.
+StringView PopBack(const StringView str_view, size_t count);
+
+// Keeps the count first characters of view or view if count if greater than
+// view.size.
+StringView KeepFront(const StringView view, size_t count);
+
+// Retrieves the first character of view. If view is empty the behavior is
+// undefined.
+char Front(const StringView view);
+
+// Retrieves the last character of view. If view is empty the behavior is
+// undefined.
+char Back(const StringView view);
+
+// Removes leading and tailing space characters.
+StringView TrimWhitespace(StringView view);
+
+// Convert StringView to positive integer. e.g. "42", "0x2a".
+// Returns -1 on error.
+int ParsePositiveNumber(const StringView view);
+
+// Copies src StringView to dst buffer.
+void CopyString(const StringView src, char* dst, size_t dst_size);
+
+// Checks if line contains the specified whitespace separated word.
+bool HasWord(const StringView line, const char* const word);
+
+// Get key/value from line. key and value are separated by ": ".
+// key and value are cleaned up from leading and trailing whitespaces.
+bool GetAttributeKeyValue(const StringView line, StringView* key,
+                          StringView* value);
+
+END_CPP_NAMESPACE
+
+#endif  // THIRD_PARTY_CPU_FEATURES_INCLUDE_INTERNAL_STRING_VIEW_H_
diff --git a/src/cpuid_x86_clang.c b/src/cpuid_x86_clang.c
new file mode 100644
index 0000000..fcc786a
--- /dev/null
+++ b/src/cpuid_x86_clang.c
@@ -0,0 +1,32 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "internal/cpuid_x86.h"
+
+#if defined(CPU_FEATURES_ARCH_X86) && defined(CPU_FEATURES_COMPILER_CLANG)
+#include <cpuid.h>
+
+Leaf CpuId(uint32_t leaf_id) {
+  Leaf leaf;
+  __cpuid_count(leaf_id, 0, leaf.eax, leaf.ebx, leaf.ecx, leaf.edx);
+  return leaf;
+}
+
+uint32_t GetXCR0Eax(void) {
+  uint32_t eax, edx;
+  __asm("XGETBV" : "=a"(eax), "=d"(edx) : "c"(0));
+  return eax;
+}
+
+#endif  // defined(CPU_FEATURES_ARCH_X86) && defined(CPU_FEATURES_COMPILER_CLANG)
diff --git a/src/cpuid_x86_gcc.c b/src/cpuid_x86_gcc.c
new file mode 100644
index 0000000..bf0139b
--- /dev/null
+++ b/src/cpuid_x86_gcc.c
@@ -0,0 +1,32 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "internal/cpuid_x86.h"
+
+#if defined(CPU_FEATURES_ARCH_X86) && defined(CPU_FEATURES_COMPILER_GCC)
+#include <cpuid.h>
+
+Leaf CpuId(uint32_t leaf_id) {
+  Leaf leaf;
+  __cpuid(leaf_id, leaf.eax, leaf.ebx, leaf.ecx, leaf.edx);
+  return leaf;
+}
+
+uint32_t GetXCR0Eax(void) {
+  uint32_t eax, edx;
+  __asm("XGETBV" : "=a"(eax), "=d"(edx) : "c"(0));
+  return eax;
+}
+
+#endif  // defined(CPU_FEATURES_ARCH_X86) && defined(CPU_FEATURES_COMPILER_GCC)
diff --git a/src/cpuid_x86_msvc.c b/src/cpuid_x86_msvc.c
new file mode 100644
index 0000000..cd8f19f
--- /dev/null
+++ b/src/cpuid_x86_msvc.c
@@ -0,0 +1,34 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "internal/cpuid_x86.h"
+
+#if defined(CPU_FEATURES_ARCH_X86) && defined(CPU_FEATURES_COMPILER_MSC)
+#include <immintrin.h>
+#include <intrin.h>  // For __cpuidex()
+
+Leaf CpuId(uint32_t leaf_id) {
+  Leaf leaf;
+  int data[4];
+  __cpuid(data, leaf_id);
+  leaf.eax = data[0];
+  leaf.ebx = data[1];
+  leaf.ecx = data[2];
+  leaf.edx = data[3];
+  return leaf;
+}
+
+uint32_t GetXCR0Eax(void) { return _xgetbv(0); }
+
+#endif  // defined(CPU_FEATURES_ARCH_X86) && defined(CPU_FEATURES_COMPILER_MSC)
diff --git a/src/cpuinfo_aarch64.c b/src/cpuinfo_aarch64.c
new file mode 100644
index 0000000..aad971e
--- /dev/null
+++ b/src/cpuinfo_aarch64.c
@@ -0,0 +1,140 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cpuinfo_aarch64.h"
+
+#include "internal/filesystem.h"
+#include "internal/hwcaps.h"
+#include "internal/linux_features_aggregator.h"
+#include "internal/stack_line_reader.h"
+#include "internal/string_view.h"
+
+#include <ctype.h>
+
+DECLARE_SETTER(Aarch64Features, fp)
+DECLARE_SETTER(Aarch64Features, asimd)
+DECLARE_SETTER(Aarch64Features, aes)
+DECLARE_SETTER(Aarch64Features, pmull)
+DECLARE_SETTER(Aarch64Features, sha1)
+DECLARE_SETTER(Aarch64Features, sha2)
+DECLARE_SETTER(Aarch64Features, crc32)
+
+static const CapabilityConfig kConfigs[] = {
+    {{AARCH64_HWCAP_FP, 0}, "fp", &set_fp},           //
+    {{AARCH64_HWCAP_ASIMD, 0}, "asimd", &set_asimd},  //
+    {{AARCH64_HWCAP_AES, 0}, "aes", &set_aes},        //
+    {{AARCH64_HWCAP_PMULL, 0}, "pmull", &set_pmull},  //
+    {{AARCH64_HWCAP_SHA1, 0}, "sha1", &set_sha1},     //
+    {{AARCH64_HWCAP_SHA2, 0}, "sha2", &set_sha2},     //
+    {{AARCH64_HWCAP_CRC32, 0}, "crc32", &set_crc32},  //
+};
+
+static const size_t kConfigsSize = sizeof(kConfigs) / sizeof(CapabilityConfig);
+
+static bool HandleAarch64Line(const LineResult result,
+                              Aarch64Info* const info) {
+  StringView line = result.line;
+  StringView key, value;
+  if (GetAttributeKeyValue(line, &key, &value)) {
+    if (IsEquals(key, str("Features"))) {
+      SetFromFlags(kConfigsSize, kConfigs, value, &info->features);
+    } else if (IsEquals(key, str("CPU implementer"))) {
+      info->implementer = ParsePositiveNumber(value);
+    } else if (IsEquals(key, str("CPU variant"))) {
+      info->variant = ParsePositiveNumber(value);
+    } else if (IsEquals(key, str("CPU part"))) {
+      info->part = ParsePositiveNumber(value);
+    } else if (IsEquals(key, str("CPU revision"))) {
+      info->revision = ParsePositiveNumber(value);
+    }
+  }
+  return !result.eof;
+}
+
+static void FillProcCpuInfoData(Aarch64Info* const info) {
+  const int fd = OpenFile("/proc/cpuinfo");
+  if (fd >= 0) {
+    StackLineReader reader;
+    StackLineReader_Initialize(&reader, fd);
+    for (;;) {
+      if (!HandleAarch64Line(StackLineReader_NextLine(&reader), info)) {
+        break;
+      }
+    }
+    CloseFile(fd);
+  }
+}
+
+static const Aarch64Info kEmptyAarch64Info;
+
+Aarch64Info GetAarch64Info(void) {
+  // capabilities are fetched from both getauxval and /proc/cpuinfo so we can
+  // have some information if the executable is sandboxed (aka no access to
+  // /proc/cpuinfo).
+  Aarch64Info info = kEmptyAarch64Info;
+
+  FillProcCpuInfoData(&info);
+  OverrideFromHwCaps(kConfigsSize, kConfigs, GetHardwareCapabilities(),
+                     &info.features);
+
+  return info;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Introspection functions
+
+int GetAarch64FeaturesEnumValue(const Aarch64Features* features,
+                                Aarch64FeaturesEnum value) {
+  switch (value) {
+    case AARCH64_FP:
+      return features->fp;
+    case AARCH64_ASIMD:
+      return features->asimd;
+    case AARCH64_AES:
+      return features->aes;
+    case AARCH64_PMULL:
+      return features->pmull;
+    case AARCH64_SHA1:
+      return features->sha1;
+    case AARCH64_SHA2:
+      return features->sha2;
+    case AARCH64_CRC32:
+      return features->crc32;
+    case AARCH64_LAST_:
+      break;
+  }
+  return false;
+}
+
+const char* GetAarch64FeaturesEnumName(Aarch64FeaturesEnum value) {
+  switch (value) {
+    case AARCH64_FP:
+      return "fp";
+    case AARCH64_ASIMD:
+      return "asimd";
+    case AARCH64_AES:
+      return "aes";
+    case AARCH64_PMULL:
+      return "pmull";
+    case AARCH64_SHA1:
+      return "sha1";
+    case AARCH64_SHA2:
+      return "sha2";
+    case AARCH64_CRC32:
+      return "crc32";
+    case AARCH64_LAST_:
+      break;
+  }
+  return "unknown feature";
+}
diff --git a/src/cpuinfo_arm.c b/src/cpuinfo_arm.c
new file mode 100644
index 0000000..8db8d08
--- /dev/null
+++ b/src/cpuinfo_arm.c
@@ -0,0 +1,255 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cpuinfo_arm.h"
+
+#include "internal/bit_utils.h"
+#include "internal/filesystem.h"
+#include "internal/hwcaps.h"
+#include "internal/linux_features_aggregator.h"
+#include "internal/stack_line_reader.h"
+#include "internal/string_view.h"
+
+#include <ctype.h>
+
+DECLARE_SETTER(ArmFeatures, vfp)
+DECLARE_SETTER(ArmFeatures, iwmmxt)
+DECLARE_SETTER(ArmFeatures, neon)
+DECLARE_SETTER(ArmFeatures, vfpv3)
+DECLARE_SETTER(ArmFeatures, vfpv3d16)
+DECLARE_SETTER(ArmFeatures, vfpv4)
+DECLARE_SETTER(ArmFeatures, idiva)
+DECLARE_SETTER(ArmFeatures, idivt)
+DECLARE_SETTER(ArmFeatures, aes)
+DECLARE_SETTER(ArmFeatures, pmull)
+DECLARE_SETTER(ArmFeatures, sha1)
+DECLARE_SETTER(ArmFeatures, sha2)
+DECLARE_SETTER(ArmFeatures, crc32)
+
+static const CapabilityConfig kConfigs[] = {
+    {{ARM_HWCAP_VFP, 0}, "vfp", &set_vfp},                 //
+    {{ARM_HWCAP_IWMMXT, 0}, "iwmmxt", &set_iwmmxt},        //
+    {{ARM_HWCAP_NEON, 0}, "neon", &set_neon},              //
+    {{ARM_HWCAP_VFPV3, 0}, "vfpv3", &set_vfpv3},           //
+    {{ARM_HWCAP_VFPV3D16, 0}, "vfpv3d16", &set_vfpv3d16},  //
+    {{ARM_HWCAP_VFPV4, 0}, "vfpv4", &set_vfpv4},           //
+    {{ARM_HWCAP_IDIVA, 0}, "idiva", &set_idiva},           //
+    {{ARM_HWCAP_IDIVT, 0}, "idivt", &set_idivt},           //
+    {{0, ARM_HWCAP2_AES}, "aes", &set_aes},                //
+    {{0, ARM_HWCAP2_PMULL}, "pmull", &set_pmull},          //
+    {{0, ARM_HWCAP2_SHA1}, "sha1", &set_sha1},             //
+    {{0, ARM_HWCAP2_SHA2}, "sha2", &set_sha2},             //
+    {{0, ARM_HWCAP2_CRC32}, "crc32", &set_crc32},          //
+};
+
+static const size_t kConfigsSize = sizeof(kConfigs) / sizeof(CapabilityConfig);
+
+typedef struct {
+  bool processor_reports_armv6;
+  bool hardware_reports_goldfish;
+} ProcCpuInfoData;
+
+static int IndexOfNonDigit(StringView str) {
+  size_t index = 0;
+  while (str.size && isdigit(Front(str))) {
+    str = PopFront(str, 1);
+    ++index;
+  }
+  return index;
+}
+
+static bool HandleArmLine(const LineResult result, ArmInfo* const info,
+                          ProcCpuInfoData* const proc_info) {
+  StringView line = result.line;
+  StringView key, value;
+  if (GetAttributeKeyValue(line, &key, &value)) {
+    if (IsEquals(key, str("Features"))) {
+      SetFromFlags(kConfigsSize, kConfigs, value, &info->features);
+    } else if (IsEquals(key, str("CPU implementer"))) {
+      info->implementer = ParsePositiveNumber(value);
+    } else if (IsEquals(key, str("CPU variant"))) {
+      info->variant = ParsePositiveNumber(value);
+    } else if (IsEquals(key, str("CPU part"))) {
+      info->part = ParsePositiveNumber(value);
+    } else if (IsEquals(key, str("CPU revision"))) {
+      info->revision = ParsePositiveNumber(value);
+    } else if (IsEquals(key, str("CPU architecture"))) {
+      // CPU architecture is a number that may be followed by letters. e.g.
+      // "6TEJ", "7".
+      const StringView digits = KeepFront(value, IndexOfNonDigit(value));
+      info->architecture = ParsePositiveNumber(digits);
+    } else if (IsEquals(key, str("Processor"))) {
+      proc_info->processor_reports_armv6 = IndexOf(value, str("(v6l)")) >= 0;
+    } else if (IsEquals(key, str("Hardware"))) {
+      proc_info->hardware_reports_goldfish = IsEquals(value, str("Goldfish"));
+    }
+  }
+  return !result.eof;
+}
+
+static uint32_t GetCpuId(const ArmInfo* const info) {
+  return (ExtractBitRange(info->implementer, 7, 0) << 24) |
+         (ExtractBitRange(info->variant, 3, 0) << 20) |
+         (ExtractBitRange(info->part, 11, 0) << 4) |
+         (ExtractBitRange(info->revision, 3, 0) << 0);
+}
+
+static void FixErrors(ArmInfo* const info,
+                      ProcCpuInfoData* const proc_cpu_info_data) {
+  // Fixing Samsung kernel reporting invalid cpu architecture.
+  // http://code.google.com/p/android/issues/detail?id=10812
+  if (proc_cpu_info_data->processor_reports_armv6 && info->architecture >= 7) {
+    info->architecture = 6;
+  }
+
+  // Handle kernel configuration bugs that prevent the correct reporting of CPU
+  // features.
+  switch (GetCpuId(info)) {
+    case 0x4100C080:
+      // Special case: The emulator-specific Android 4.2 kernel fails to report
+      // support for the 32-bit ARM IDIV instruction. Technically, this is a
+      // feature of the virtual CPU implemented by the emulator. Note that it
+      // could also support Thumb IDIV in the future, and this will have to be
+      // slightly updated.
+      if (info->architecture >= 7 &&
+          proc_cpu_info_data->hardware_reports_goldfish) {
+        info->features.idiva = true;
+      }
+      break;
+    case 0x511004D0:
+      // https://crbug.com/341598.
+      info->features.neon = false;
+      break;
+    case 0x510006F2:
+    case 0x510006F3:
+      // The Nexus 4 (Qualcomm Krait) kernel configuration forgets to report
+      // IDIV support.
+      info->features.idiva = true;
+      info->features.idivt = true;
+      break;
+  }
+
+  // Propagate cpu features.
+  if (info->features.vfpv4) info->features.vfpv3 = true;
+  if (info->features.neon) info->features.vfpv3 = true;
+  if (info->features.vfpv3) info->features.vfp = true;
+}
+
+static void FillProcCpuInfoData(ArmInfo* const info,
+                                ProcCpuInfoData* proc_cpu_info_data) {
+  const int fd = OpenFile("/proc/cpuinfo");
+  if (fd >= 0) {
+    StackLineReader reader;
+    StackLineReader_Initialize(&reader, fd);
+    for (;;) {
+      if (!HandleArmLine(StackLineReader_NextLine(&reader), info,
+                         proc_cpu_info_data)) {
+        break;
+      }
+    }
+    CloseFile(fd);
+  }
+}
+
+static const ArmInfo kEmptyArmInfo;
+
+static const ProcCpuInfoData kEmptyProcCpuInfoData;
+
+ArmInfo GetArmInfo(void) {
+  // capabilities are fetched from both getauxval and /proc/cpuinfo so we can
+  // have some information if the executable is sandboxed (aka no access to
+  // /proc/cpuinfo).
+  ArmInfo info = kEmptyArmInfo;
+  ProcCpuInfoData proc_cpu_info_data = kEmptyProcCpuInfoData;
+
+  FillProcCpuInfoData(&info, &proc_cpu_info_data);
+  OverrideFromHwCaps(kConfigsSize, kConfigs, GetHardwareCapabilities(),
+                     &info.features);
+
+  FixErrors(&info, &proc_cpu_info_data);
+
+  return info;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Introspection functions
+
+int GetArmFeaturesEnumValue(const ArmFeatures* features,
+                            ArmFeaturesEnum value) {
+  switch (value) {
+    case ARM_VFP:
+      return features->vfp;
+    case ARM_IWMMXT:
+      return features->iwmmxt;
+    case ARM_NEON:
+      return features->neon;
+    case ARM_VFPV3:
+      return features->vfpv3;
+    case ARM_VFPV3D16:
+      return features->vfpv3d16;
+    case ARM_VFPV4:
+      return features->vfpv4;
+    case ARM_IDIVA:
+      return features->idiva;
+    case ARM_IDIVT:
+      return features->idivt;
+    case ARM_AES:
+      return features->aes;
+    case ARM_PMULL:
+      return features->pmull;
+    case ARM_SHA1:
+      return features->sha1;
+    case ARM_SHA2:
+      return features->sha2;
+    case ARM_CRC32:
+      return features->crc32;
+    case ARM_LAST_:
+      break;
+  }
+  return false;
+}
+
+const char* GetArmFeaturesEnumName(ArmFeaturesEnum value) {
+  switch (value) {
+    case ARM_VFP:
+      return "vfp";
+    case ARM_IWMMXT:
+      return "iwmmxt";
+    case ARM_NEON:
+      return "neon";
+    case ARM_VFPV3:
+      return "vfpv3";
+    case ARM_VFPV3D16:
+      return "vfpv3d16";
+    case ARM_VFPV4:
+      return "vfpv4";
+    case ARM_IDIVA:
+      return "idiva";
+    case ARM_IDIVT:
+      return "idivt";
+    case ARM_AES:
+      return "aes";
+    case ARM_PMULL:
+      return "pmull";
+    case ARM_SHA1:
+      return "sha1";
+    case ARM_SHA2:
+      return "sha2";
+    case ARM_CRC32:
+      return "crc32";
+    case ARM_LAST_:
+      break;
+  }
+  return "unknown feature";
+}
diff --git a/src/cpuinfo_mips.c b/src/cpuinfo_mips.c
new file mode 100644
index 0000000..3c6a4fb
--- /dev/null
+++ b/src/cpuinfo_mips.c
@@ -0,0 +1,97 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cpuinfo_mips.h"
+
+#include "internal/filesystem.h"
+#include "internal/linux_features_aggregator.h"
+#include "internal/stack_line_reader.h"
+#include "internal/string_view.h"
+
+DECLARE_SETTER(MipsFeatures, msa)
+DECLARE_SETTER(MipsFeatures, eva)
+
+static const CapabilityConfig kConfigs[] = {
+    {{MIPS_HWCAP_MSA, 0}, "msa", &set_msa},  //
+    {{MIPS_HWCAP_EVA, 0}, "eva", &set_eva},  //
+};
+static const size_t kConfigsSize = sizeof(kConfigs) / sizeof(CapabilityConfig);
+
+static bool HandleMipsLine(const LineResult result,
+                           MipsFeatures* const features) {
+  StringView key, value;
+  // See tests for an example.
+  if (GetAttributeKeyValue(result.line, &key, &value)) {
+    if (IsEquals(key, str("ASEs implemented"))) {
+      SetFromFlags(kConfigsSize, kConfigs, value, features);
+    }
+  }
+  return !result.eof;
+}
+
+static void FillProcCpuInfoData(MipsFeatures* const features) {
+  const int fd = OpenFile("/proc/cpuinfo");
+  if (fd >= 0) {
+    StackLineReader reader;
+    StackLineReader_Initialize(&reader, fd);
+    for (;;) {
+      if (!HandleMipsLine(StackLineReader_NextLine(&reader), features)) {
+        break;
+      }
+    }
+    CloseFile(fd);
+  }
+}
+
+static const MipsInfo kEmptyMipsInfo;
+
+MipsInfo GetMipsInfo(void) {
+  // capabilities are fetched from both getauxval and /proc/cpuinfo so we can
+  // have some information if the executable is sandboxed (aka no access to
+  // /proc/cpuinfo).
+  MipsInfo info = kEmptyMipsInfo;
+
+  FillProcCpuInfoData(&info.features);
+  OverrideFromHwCaps(kConfigsSize, kConfigs, GetHardwareCapabilities(),
+                     &info.features);
+  return info;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Introspection functions
+
+int GetMipsFeaturesEnumValue(const MipsFeatures* features,
+                             MipsFeaturesEnum value) {
+  switch (value) {
+    case MIPS_MSA:
+      return features->msa;
+    case MIPS_EVA:
+      return features->eva;
+    case MIPS_LAST_:
+      break;
+  }
+  return false;
+}
+
+const char* GetMipsFeaturesEnumName(MipsFeaturesEnum value) {
+  switch (value) {
+    case MIPS_MSA:
+      return "msa";
+    case MIPS_EVA:
+      return "eva";
+    case MIPS_LAST_:
+      break;
+  }
+  return "unknown feature";
+}
diff --git a/src/cpuinfo_x86.c b/src/cpuinfo_x86.c
new file mode 100644
index 0000000..3238ab1
--- /dev/null
+++ b/src/cpuinfo_x86.c
@@ -0,0 +1,432 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cpuinfo_x86.h"
+#include "internal/bit_utils.h"
+#include "internal/cpuid_x86.h"
+
+#include <stdbool.h>
+#include <string.h>
+
+static const Leaf kEmptyLeaf;
+
+static Leaf SafeCpuId(uint32_t max_cpuid_leaf, uint32_t leaf_id) {
+  if (leaf_id <= max_cpuid_leaf) {
+    return CpuId(leaf_id);
+  } else {
+    return kEmptyLeaf;
+  }
+}
+
+#define MASK_XMM 0x2
+#define MASK_YMM 0x4
+#define MASK_MASKREG 0x20
+#define MASK_ZMM0_15 0x40
+#define MASK_ZMM16_31 0x80
+
+static bool HasMask(uint32_t value, uint32_t mask) {
+  return (value & mask) == mask;
+}
+
+// Checks that operating system saves and restores xmm registers during context
+// switches.
+static bool HasXmmOsXSave(uint32_t xcr0_eax) {
+  return HasMask(xcr0_eax, MASK_XMM);
+}
+
+// Checks that operating system saves and restores ymm registers during context
+// switches.
+static bool HasYmmOsXSave(uint32_t xcr0_eax) {
+  return HasMask(xcr0_eax, MASK_XMM | MASK_YMM);
+}
+
+// Checks that operating system saves and restores zmm registers during context
+// switches.
+static bool HasZmmOsXSave(uint32_t xcr0_eax) {
+  return HasMask(xcr0_eax, MASK_XMM | MASK_YMM | MASK_MASKREG | MASK_ZMM0_15 |
+                               MASK_ZMM16_31);
+}
+
+static void SetVendor(const Leaf leaf, char* const vendor) {
+  *(uint32_t*)(vendor) = leaf.ebx;
+  *(uint32_t*)(vendor + 4) = leaf.edx;
+  *(uint32_t*)(vendor + 8) = leaf.ecx;
+  vendor[12] = '\0';
+}
+
+static int IsVendor(const Leaf leaf, const char* const name) {
+  const uint32_t ebx = *(const uint32_t*)(name);
+  const uint32_t edx = *(const uint32_t*)(name + 4);
+  const uint32_t ecx = *(const uint32_t*)(name + 8);
+  return leaf.ebx == ebx && leaf.ecx == ecx && leaf.edx == edx;
+}
+
+// Reference https://en.wikipedia.org/wiki/CPUID.
+static void ParseCpuId(const uint32_t max_cpuid_leaf, X86Info* info) {
+  const Leaf leaf_1 = SafeCpuId(max_cpuid_leaf, 1);
+  const Leaf leaf_7 = SafeCpuId(max_cpuid_leaf, 7);
+
+  const bool have_xsave = IsBitSet(leaf_1.ecx, 26);
+  const bool have_osxsave = IsBitSet(leaf_1.ecx, 27);
+  const uint32_t xcr0_eax = (have_xsave && have_osxsave) ? GetXCR0Eax() : 0;
+  const bool have_sse_os_support = HasXmmOsXSave(xcr0_eax);
+  const bool have_avx_os_support = HasYmmOsXSave(xcr0_eax);
+  const bool have_avx512_os_support = HasZmmOsXSave(xcr0_eax);
+
+  const uint32_t family = ExtractBitRange(leaf_1.eax, 11, 8);
+  const uint32_t extended_family = ExtractBitRange(leaf_1.eax, 27, 20);
+  const uint32_t model = ExtractBitRange(leaf_1.eax, 7, 4);
+  const uint32_t extended_model = ExtractBitRange(leaf_1.eax, 19, 16);
+
+  X86Features* const features = &info->features;
+
+  info->family = extended_family + family;
+  info->model = (extended_model << 4) + model;
+  info->stepping = ExtractBitRange(leaf_1.eax, 3, 0);
+
+  features->aes = IsBitSet(leaf_1.ecx, 25);
+  features->erms = IsBitSet(leaf_7.ebx, 9);
+  features->f16c = IsBitSet(leaf_1.ecx, 29);
+  features->bmi1 = IsBitSet(leaf_7.ebx, 3);
+  features->bmi2 = IsBitSet(leaf_7.ebx, 8);
+  features->vpclmulqdq = IsBitSet(leaf_7.ecx, 10);
+
+  if (have_sse_os_support) {
+    features->ssse3 = IsBitSet(leaf_1.ecx, 9);
+    features->sse4_1 = IsBitSet(leaf_1.ecx, 19);
+    features->sse4_2 = IsBitSet(leaf_1.ecx, 20);
+  }
+
+  if (have_avx_os_support) {
+    features->fma3 = IsBitSet(leaf_1.ecx, 12);
+    features->avx = IsBitSet(leaf_1.ecx, 28);
+    features->avx2 = IsBitSet(leaf_7.ebx, 5);
+  }
+
+  if (have_avx512_os_support) {
+    features->avx512f = IsBitSet(leaf_7.ebx, 16);
+    features->avx512cd = IsBitSet(leaf_7.ebx, 28);
+    features->avx512er = IsBitSet(leaf_7.ebx, 27);
+    features->avx512pf = IsBitSet(leaf_7.ebx, 26);
+    features->avx512bw = IsBitSet(leaf_7.ebx, 30);
+    features->avx512dq = IsBitSet(leaf_7.ebx, 17);
+    features->avx512vl = IsBitSet(leaf_7.ebx, 31);
+    features->avx512ifma = IsBitSet(leaf_7.ebx, 21);
+    features->avx512vbmi = IsBitSet(leaf_7.ecx, 1);
+    features->avx512vbmi2 = IsBitSet(leaf_7.ecx, 6);
+    features->avx512vnni = IsBitSet(leaf_7.ecx, 11);
+    features->avx512bitalg = IsBitSet(leaf_7.ecx, 12);
+    features->avx512vpopcntdq = IsBitSet(leaf_7.ecx, 14);
+    features->avx512_4vnniw = IsBitSet(leaf_7.edx, 2);
+    features->avx512_4vbmi2 = IsBitSet(leaf_7.edx, 3);
+  }
+}
+
+static const X86Info kEmptyX86Info;
+
+X86Info GetX86Info(void) {
+  X86Info info = kEmptyX86Info;
+  const Leaf leaf_0 = CpuId(0);
+  const uint32_t max_cpuid_leaf = leaf_0.eax;
+  SetVendor(leaf_0, info.vendor);
+  if (IsVendor(leaf_0, "GenuineIntel") || IsVendor(leaf_0, "AuthenticAMD")) {
+    ParseCpuId(max_cpuid_leaf, &info);
+  }
+  return info;
+}
+
+#define CPUID(FAMILY, MODEL) (((FAMILY & 0xFF) << 8) | (MODEL & 0xFF))
+
+X86Microarchitecture GetX86Microarchitecture(const X86Info* info) {
+  if (memcmp(info->vendor, "GenuineIntel", sizeof(info->vendor)) == 0) {
+    switch (CPUID(info->family, info->model)) {
+      case CPUID(0x06, 0x35):
+      case CPUID(0x06, 0x36):
+        // https://en.wikipedia.org/wiki/Bonnell_(microarchitecture)
+        return INTEL_ATOM_BNL;
+      case CPUID(0x06, 0x37):
+      case CPUID(0x06, 0x4C):
+        // https://en.wikipedia.org/wiki/Silvermont
+        return INTEL_ATOM_SMT;
+      case CPUID(0x06, 0x5C):
+        // https://en.wikipedia.org/wiki/Goldmont
+        return INTEL_ATOM_GMT;
+      case CPUID(0x06, 0x0F):
+      case CPUID(0x06, 0x16):
+        // https://en.wikipedia.org/wiki/Intel_Core_(microarchitecture)
+        return INTEL_CORE;
+      case CPUID(0x06, 0x17):
+      case CPUID(0x06, 0x1D):
+        // https://en.wikipedia.org/wiki/Penryn_(microarchitecture)
+        return INTEL_PNR;
+      case CPUID(0x06, 0x1A):
+      case CPUID(0x06, 0x1E):
+      case CPUID(0x06, 0x1F):
+      case CPUID(0x06, 0x2E):
+        // https://en.wikipedia.org/wiki/Nehalem_(microarchitecture)
+        return INTEL_NHM;
+      case CPUID(0x06, 0x25):
+      case CPUID(0x06, 0x2C):
+      case CPUID(0x06, 0x2F):
+        // https://en.wikipedia.org/wiki/Westmere_(microarchitecture)
+        return INTEL_WSM;
+      case CPUID(0x06, 0x2A):
+      case CPUID(0x06, 0x2D):
+        // https://en.wikipedia.org/wiki/Sandy_Bridge#Models_and_steppings
+        return INTEL_SNB;
+      case CPUID(0x06, 0x3A):
+      case CPUID(0x06, 0x3E):
+        // https://en.wikipedia.org/wiki/Ivy_Bridge_(microarchitecture)#Models_and_steppings
+        return INTEL_IVB;
+      case CPUID(0x06, 0x3C):
+      case CPUID(0x06, 0x3F):
+      case CPUID(0x06, 0x45):
+      case CPUID(0x06, 0x46):
+        // https://en.wikipedia.org/wiki/Haswell_(microarchitecture)
+        return INTEL_HSW;
+      case CPUID(0x06, 0x3D):
+      case CPUID(0x06, 0x47):
+      case CPUID(0x06, 0x4F):
+      case CPUID(0x06, 0x56):
+        // https://en.wikipedia.org/wiki/Broadwell_(microarchitecture)
+        return INTEL_BDW;
+      case CPUID(0x06, 0x4E):
+      case CPUID(0x06, 0x55):
+      case CPUID(0x06, 0x5E):
+        // https://en.wikipedia.org/wiki/Skylake_(microarchitecture)
+        return INTEL_SKL;
+      case CPUID(0x06, 0x8E):
+      case CPUID(0x06, 0x9E):
+        // https://en.wikipedia.org/wiki/Kaby_Lake
+        return INTEL_KBL;
+      default:
+        return X86_UNKNOWN;
+    }
+  }
+  if (memcmp(info->vendor, "AuthenticAMD", sizeof(info->vendor)) == 0) {
+    switch (info->family) {
+        // https://en.wikipedia.org/wiki/List_of_AMD_CPU_microarchitectures
+      case 0x0F:
+        return AMD_HAMMER;
+      case 0x10:
+        return AMD_K10;
+      case 0x14:
+        return AMD_BOBCAT;
+      case 0x15:
+        return AMD_BULLDOZER;
+      case 0x16:
+        return AMD_JAGUAR;
+      case 0x17:
+        return AMD_ZEN;
+      default:
+        return X86_UNKNOWN;
+    }
+  }
+  return X86_UNKNOWN;
+}
+
+static void SetString(const uint32_t max_cpuid_ext_leaf, const uint32_t leaf_id,
+                      char* buffer) {
+  const Leaf leaf = SafeCpuId(max_cpuid_ext_leaf, leaf_id);
+  // We allow calling memcpy from SetString which is only called when requesting
+  // X86BrandString.
+  memcpy(buffer, &leaf, sizeof(Leaf));
+}
+
+void FillX86BrandString(char brand_string[49]) {
+  const Leaf leaf_ext_0 = CpuId(0x80000000);
+  const uint32_t max_cpuid_leaf_ext = leaf_ext_0.eax;
+  SetString(max_cpuid_leaf_ext, 0x80000002, brand_string);
+  SetString(max_cpuid_leaf_ext, 0x80000003, brand_string + 16);
+  SetString(max_cpuid_leaf_ext, 0x80000004, brand_string + 32);
+  brand_string[48] = '\0';
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Introspection functions
+
+int GetX86FeaturesEnumValue(const X86Features* features,
+                            X86FeaturesEnum value) {
+  switch (value) {
+    case X86_AES:
+      return features->aes;
+    case X86_ERMS:
+      return features->erms;
+    case X86_F16C:
+      return features->f16c;
+    case X86_FMA3:
+      return features->fma3;
+    case X86_VPCLMULQDQ:
+      return features->vpclmulqdq;
+    case X86_BMI1:
+      return features->bmi1;
+    case X86_BMI2:
+      return features->bmi2;
+    case X86_SSSE3:
+      return features->ssse3;
+    case X86_SSE4_1:
+      return features->sse4_1;
+    case X86_SSE4_2:
+      return features->sse4_2;
+    case X86_AVX:
+      return features->avx;
+    case X86_AVX2:
+      return features->avx2;
+    case X86_AVX512F:
+      return features->avx512f;
+    case X86_AVX512CD:
+      return features->avx512cd;
+    case X86_AVX512ER:
+      return features->avx512er;
+    case X86_AVX512PF:
+      return features->avx512pf;
+    case X86_AVX512BW:
+      return features->avx512bw;
+    case X86_AVX512DQ:
+      return features->avx512dq;
+    case X86_AVX512VL:
+      return features->avx512vl;
+    case X86_AVX512IFMA:
+      return features->avx512ifma;
+    case X86_AVX512VBMI:
+      return features->avx512vbmi;
+    case X86_AVX512VBMI2:
+      return features->avx512vbmi2;
+    case X86_AVX512VNNI:
+      return features->avx512vnni;
+    case X86_AVX512BITALG:
+      return features->avx512bitalg;
+    case X86_AVX512VPOPCNTDQ:
+      return features->avx512vpopcntdq;
+    case X86_AVX512_4VNNIW:
+      return features->avx512_4vnniw;
+    case X86_AVX512_4VBMI2:
+      return features->avx512_4vbmi2;
+    case X86_LAST_:
+      break;
+  }
+  return false;
+}
+
+const char* GetX86FeaturesEnumName(X86FeaturesEnum value) {
+  switch (value) {
+    case X86_AES:
+      return "aes";
+    case X86_ERMS:
+      return "erms";
+    case X86_F16C:
+      return "f16c";
+    case X86_FMA3:
+      return "fma3";
+    case X86_VPCLMULQDQ:
+      return "vpclmulqdq";
+    case X86_BMI1:
+      return "bmi1";
+    case X86_BMI2:
+      return "bmi2";
+    case X86_SSSE3:
+      return "ssse3";
+    case X86_SSE4_1:
+      return "sse4_1";
+    case X86_SSE4_2:
+      return "sse4_2";
+    case X86_AVX:
+      return "avx";
+    case X86_AVX2:
+      return "avx2";
+    case X86_AVX512F:
+      return "avx512f";
+    case X86_AVX512CD:
+      return "avx512cd";
+    case X86_AVX512ER:
+      return "avx512er";
+    case X86_AVX512PF:
+      return "avx512pf";
+    case X86_AVX512BW:
+      return "avx512bw";
+    case X86_AVX512DQ:
+      return "avx512dq";
+    case X86_AVX512VL:
+      return "avx512vl";
+    case X86_AVX512IFMA:
+      return "avx512ifma";
+    case X86_AVX512VBMI:
+      return "avx512vbmi";
+    case X86_AVX512VBMI2:
+      return "avx512vbmi2";
+    case X86_AVX512VNNI:
+      return "avx512vnni";
+    case X86_AVX512BITALG:
+      return "avx512bitalg";
+    case X86_AVX512VPOPCNTDQ:
+      return "avx512vpopcntdq";
+    case X86_AVX512_4VNNIW:
+      return "avx512_4vnniw";
+    case X86_AVX512_4VBMI2:
+      return "avx512_4vbmi2";
+    case X86_LAST_:
+      break;
+  }
+  return "unknown_feature";
+}
+
+const char* GetX86MicroarchitectureName(X86Microarchitecture uarch) {
+  switch (uarch) {
+    case X86_UNKNOWN:
+      return "X86_UNKNOWN";
+    case INTEL_CORE:
+      return "INTEL_CORE";
+    case INTEL_PNR:
+      return "INTEL_PNR";
+    case INTEL_NHM:
+      return "INTEL_NHM";
+    case INTEL_ATOM_BNL:
+      return "INTEL_ATOM_BNL";
+    case INTEL_WSM:
+      return "INTEL_WSM";
+    case INTEL_SNB:
+      return "INTEL_SNB";
+    case INTEL_IVB:
+      return "INTEL_IVB";
+    case INTEL_ATOM_SMT:
+      return "INTEL_ATOM_SMT";
+    case INTEL_HSW:
+      return "INTEL_HSW";
+    case INTEL_BDW:
+      return "INTEL_BDW";
+    case INTEL_SKL:
+      return "INTEL_SKL";
+    case INTEL_ATOM_GMT:
+      return "INTEL_ATOM_GMT";
+    case INTEL_KBL:
+      return "INTEL_KBL";
+    case INTEL_CFL:
+      return "INTEL_CFL";
+    case INTEL_CNL:
+      return "INTEL_CNL";
+    case AMD_HAMMER:
+      return "AMD_HAMMER";
+    case AMD_K10:
+      return "AMD_K10";
+    case AMD_BOBCAT:
+      return "AMD_BOBCAT";
+    case AMD_BULLDOZER:
+      return "AMD_BULLDOZER";
+    case AMD_JAGUAR:
+      return "AMD_JAGUAR";
+    case AMD_ZEN:
+      return "AMD_ZEN";
+  }
+  return "unknown microarchitecture";
+}
diff --git a/src/filesystem.c b/src/filesystem.c
new file mode 100644
index 0000000..5049354
--- /dev/null
+++ b/src/filesystem.c
@@ -0,0 +1,53 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "internal/filesystem.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#if defined(_MSC_VER)
+#include <io.h>
+int OpenFile(const char* filename) { return _open(filename, _O_RDONLY); }
+
+void CloseFile(int file_descriptor) { _close(file_descriptor); }
+
+int ReadFile(int file_descriptor, void* buffer, size_t buffer_size) {
+  return _read(file_descriptor, buffer, buffer_size);
+}
+
+#else
+#include <unistd.h>
+
+int OpenFile(const char* filename) {
+  int result;
+  do {
+    result = open(filename, O_RDONLY);
+  } while (result == -1L && errno == EINTR);
+  return result;
+}
+
+void CloseFile(int file_descriptor) { close(file_descriptor); }
+
+int ReadFile(int file_descriptor, void* buffer, size_t buffer_size) {
+  int result;
+  do {
+    result = read(file_descriptor, buffer, buffer_size);
+  } while (result == -1L && errno == EINTR);
+  return result;
+}
+
+#endif
diff --git a/src/hwcaps.c b/src/hwcaps.c
new file mode 100644
index 0000000..d511bab
--- /dev/null
+++ b/src/hwcaps.c
@@ -0,0 +1,165 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "internal/hwcaps.h"
+#include "cpu_features_macros.h"
+#include "internal/filesystem.h"
+
+#if defined(NDEBUG)
+#define D(...)
+#else
+#include <stdio.h>
+#define D(...)           \
+  do {                   \
+    printf(__VA_ARGS__); \
+    fflush(stdout);      \
+  } while (0)
+#endif
+
+#if defined(CPU_FEATURES_ARCH_MIPS) || defined(CPU_FEATURES_ARCH_ANY_ARM)
+#define HWCAPS_ANDROID_MIPS_OR_ARM
+#endif
+
+#if defined(CPU_FEATURES_OS_LINUX_OR_ANDROID) && \
+    !defined(HWCAPS_ANDROID_MIPS_OR_ARM)
+#define HWCAPS_REGULAR_LINUX
+#endif
+
+#if defined(HWCAPS_ANDROID_MIPS_OR_ARM) || defined(HWCAPS_REGULAR_LINUX)
+#define HWCAPS_SUPPORTED
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Implementation of GetElfHwcapFromGetauxval
+////////////////////////////////////////////////////////////////////////////////
+
+// On Linux we simply use getauxval.
+#if defined(HWCAPS_REGULAR_LINUX)
+#include <dlfcn.h>
+#include <sys/auxv.h>
+static uint32_t GetElfHwcapFromGetauxval(uint32_t hwcap_type) {
+  return getauxval(hwcap_type);
+}
+#endif  // defined(HWCAPS_REGULAR_LINUX)
+
+// On Android we probe the system's C library for a 'getauxval' function and
+// call it if it exits, or return 0 for failure. This function is available
+// since API level 20.
+//
+// This code does *NOT* check for '__ANDROID_API__ >= 20' to support the edge
+// case where some NDK developers use headers for a platform that is newer than
+// the one really targetted by their application. This is typically done to use
+// newer native APIs only when running on more recent Android versions, and
+// requires careful symbol management.
+//
+// Note that getauxval() can't really be re-implemented here, because its
+// implementation does not parse /proc/self/auxv. Instead it depends on values
+// that are passed by the kernel at process-init time to the C runtime
+// initialization layer.
+#if defined(HWCAPS_ANDROID_MIPS_OR_ARM)
+#include <dlfcn.h>
+#define AT_HWCAP 16
+#define AT_HWCAP2 26
+typedef unsigned long getauxval_func_t(unsigned long);
+
+static uint32_t GetElfHwcapFromGetauxval(uint32_t hwcap_type) {
+  uint32_t ret = 0;
+  void* libc_handle = NULL;
+  getauxval_func_t* func = NULL;
+
+  dlerror();  // Cleaning error state before calling dlopen.
+  libc_handle = dlopen("libc.so", RTLD_NOW);
+  if (!libc_handle) {
+    D("Could not dlopen() C library: %s\n", dlerror());
+    return 0;
+  }
+  func = (getauxval_func_t*)dlsym(libc_handle, "getauxval");
+  if (!func) {
+    D("Could not find getauxval() in C library\n");
+  } else {
+    // Note: getauxval() returns 0 on failure. Doesn't touch errno.
+    ret = (uint32_t)(*func)(hwcap_type);
+  }
+  dlclose(libc_handle);
+  return ret;
+}
+#endif  // defined(HWCAPS_ANDROID_MIPS_OR_ARM)
+
+#if defined(HWCAPS_SUPPORTED)
+////////////////////////////////////////////////////////////////////////////////
+// Implementation of GetHardwareCapabilities for Android and Linux
+////////////////////////////////////////////////////////////////////////////////
+
+// Fallback when getauxval is not available, retrieves hwcaps from
+// "/proc/self/auxv".
+static uint32_t GetElfHwcapFromProcSelfAuxv(uint32_t hwcap_type) {
+  struct {
+    uint32_t tag;
+    uint32_t value;
+  } entry;
+  uint32_t result = 0;
+  const char filepath[] = "/proc/self/auxv";
+  const int fd = OpenFile(filepath);
+  if (fd < 0) {
+    D("Could not open %s\n", filepath);
+    return 0;
+  }
+  for (;;) {
+    const int ret = ReadFile(fd, (char*)&entry, sizeof entry);
+    if (ret < 0) {
+      D("Error while reading %s\n", filepath);
+      break;
+    }
+    // Detect end of list.
+    if (ret == 0 || (entry.tag == 0 && entry.value == 0)) {
+      break;
+    }
+    if (entry.tag == hwcap_type) {
+      result = entry.value;
+      break;
+    }
+  }
+  CloseFile(fd);
+  return result;
+}
+
+// Retrieves hardware capabilities by first trying to call getauxval, if not
+// available falls back to reading "/proc/self/auxv".
+static uint32_t GetHardwareCapabilitiesFor(uint32_t type) {
+  uint32_t hwcaps = GetElfHwcapFromGetauxval(type);
+  if (!hwcaps) {
+    D("Parsing /proc/self/auxv to extract ELF hwcaps!\n");
+    hwcaps = GetElfHwcapFromProcSelfAuxv(type);
+  }
+  return hwcaps;
+}
+
+HardwareCapabilities GetHardwareCapabilities(void) {
+  HardwareCapabilities capabilities;
+  capabilities.hwcaps = GetHardwareCapabilitiesFor(AT_HWCAP);
+  capabilities.hwcaps2 = GetHardwareCapabilitiesFor(AT_HWCAP2);
+  return capabilities;
+}
+
+#else  // (defined(HWCAPS_SUPPORTED)
+
+////////////////////////////////////////////////////////////////////////////////
+// Implementation of GetHardwareCapabilities for unsupported platforms.
+////////////////////////////////////////////////////////////////////////////////
+
+const HardwareCapabilities kEmptyHardwareCapabilities;
+HardwareCapabilities GetHardwareCapabilities(void) {
+  return kEmptyHardwareCapabilities;
+}
+#endif
diff --git a/src/linux_features_aggregator.c b/src/linux_features_aggregator.c
new file mode 100644
index 0000000..6383347
--- /dev/null
+++ b/src/linux_features_aggregator.c
@@ -0,0 +1,48 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "internal/linux_features_aggregator.h"
+#include "internal/string_view.h"
+
+void SetFromFlags(const size_t configs_size, const CapabilityConfig* configs,
+                  const StringView flags_line, void* const features) {
+  size_t i = 0;
+  for (; i < configs_size; ++i) {
+    const CapabilityConfig config = configs[i];
+    config.set_bit(features, HasWord(flags_line, config.proc_cpuinfo_flag));
+  }
+}
+
+static bool IsSet(const uint32_t mask, const uint32_t value) {
+  return (value & mask) == mask;
+}
+
+static bool IsHwCapsSet(const HardwareCapabilities hwcaps_mask,
+                        const HardwareCapabilities hwcaps) {
+  return IsSet(hwcaps_mask.hwcaps, hwcaps.hwcaps) &&
+         IsSet(hwcaps_mask.hwcaps2, hwcaps.hwcaps2);
+}
+
+void OverrideFromHwCaps(const size_t configs_size,
+                        const CapabilityConfig* configs,
+                        const HardwareCapabilities hwcaps,
+                        void* const features) {
+  size_t i = 0;
+  for (; i < configs_size; ++i) {
+    const CapabilityConfig* config = &configs[i];
+    if (IsHwCapsSet(config->hwcaps_mask, hwcaps)) {
+      config->set_bit(features, true);
+    }
+  }
+}
diff --git a/src/list_cpu_features.cc b/src/list_cpu_features.cc
new file mode 100644
index 0000000..64f3da3
--- /dev/null
+++ b/src/list_cpu_features.cc
@@ -0,0 +1,111 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <stdio.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "cpu_features_macros.h"
+#include "cpuinfo_aarch64.h"
+#include "cpuinfo_arm.h"
+#include "cpuinfo_mips.h"
+#include "cpuinfo_x86.h"
+
+namespace cpu_features {
+
+// Prints a named numeric value in both decimal and hexadecimal.
+void PrintN(const char* field, int value) {
+  printf("%-15s : %3d (0x%02X)\n", field, value, value);
+}
+
+// Prints a named string.
+void PrintS(const char* field, const char* value) {
+  printf("%-15s : %s\n", field, value);
+}
+
+template <typename HasFeatureFun, typename FeatureNameFun, typename FeatureType,
+          typename EnumType>
+std::string GetFlags(const HasFeatureFun HasFeature,
+                     const FeatureNameFun FeatureName,
+                     const FeatureType* features, const EnumType last) {
+  std::vector<std::string> flags;
+  for (int i = 0; i < last; ++i) {
+    const EnumType enum_value = static_cast<EnumType>(i);
+    if (HasFeature(features, enum_value)) {
+      flags.push_back(FeatureName(enum_value));
+    }
+  }
+  std::sort(flags.begin(), flags.end());
+  std::string buffer;
+  for (const auto& flag : flags) {
+    if (!buffer.empty()) buffer += ' ';
+    buffer += flag;
+  }
+  return buffer;
+}
+
+void Main() {
+#if defined(CPU_FEATURES_ARCH_X86)
+  char brand_string[49];
+  const X86Info info = GetX86Info();
+  const auto flags = GetFlags(&GetX86FeaturesEnumValue, &GetX86FeaturesEnumName,
+                              &info.features, X86FeaturesEnum::X86_LAST_);
+  FillX86BrandString(brand_string);
+  PrintS("arch", "x86");
+  PrintS("brand", brand_string);
+  PrintN("family", info.family);
+  PrintN("model", info.model);
+  PrintN("stepping", info.stepping);
+  PrintS("uarch", GetX86MicroarchitectureName(GetX86Microarchitecture(&info)));
+  PrintS("flags", flags.c_str());
+#elif defined(CPU_FEATURES_ARCH_ARM)
+  const ArmInfo info = GetArmInfo();
+  const auto flags = GetFlags(&GetArmFeaturesEnumValue, &GetArmFeaturesEnumName,
+                              &info.features, ArmFeaturesEnum::ARM_LAST_);
+  PrintS("arch", "ARM");
+  PrintN("implementer", info.implementer);
+  PrintN("architecture", info.architecture);
+  PrintN("variant", info.variant);
+  PrintN("part", info.part);
+  PrintN("revision", info.revision);
+  PrintS("flags", flags.c_str());
+#elif defined(CPU_FEATURES_ARCH_AARCH64)
+  const Aarch64Info info = GetAarch64Info();
+  const auto flags =
+      GetFlags(&GetAarch64FeaturesEnumValue, &GetAarch64FeaturesEnumName,
+               &info.features, Aarch64FeaturesEnum::AARCH64_LAST_);
+  PrintS("arch", "aarch64");
+  PrintN("implementer", info.implementer);
+  PrintN("variant", info.variant);
+  PrintN("part", info.part);
+  PrintN("revision", info.revision);
+  PrintS("flags", flags.c_str());
+#elif defined(CPU_FEATURES_ARCH_MIPS)
+  const MipsInfo info = GetMipsInfo();
+  const auto flags =
+      GetFlags(&GetMipsFeaturesEnumValue, &GetMipsFeaturesEnumName,
+               &info.features, MipsFeaturesEnum::MIPS_LAST_);
+  PrintS("arch", "mips");
+  PrintS("flags", flags.c_str());
+#endif
+}
+
+}  // namespace cpu_features
+
+int main(int argc, char** argv) {
+  cpu_features::Main();
+  return 0;
+}
diff --git a/src/stack_line_reader.c b/src/stack_line_reader.c
new file mode 100644
index 0000000..7f1d65b
--- /dev/null
+++ b/src/stack_line_reader.c
@@ -0,0 +1,128 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "internal/stack_line_reader.h"
+#include "internal/filesystem.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+
+void StackLineReader_Initialize(StackLineReader* reader, int fd) {
+  reader->view.ptr = reader->buffer;
+  reader->view.size = 0;
+  reader->skip_mode = false;
+  reader->fd = fd;
+}
+
+// Replaces the content of buffer with bytes from the file.
+static int LoadFullBuffer(StackLineReader* reader) {
+  const int read =
+      ReadFile(reader->fd, reader->buffer, STACK_LINE_READER_BUFFER_SIZE);
+  assert(read >= 0);
+  reader->view.ptr = reader->buffer;
+  reader->view.size = read;
+  return read;
+}
+
+// Appends with bytes from the file to buffer, filling the remaining space.
+static int LoadMore(StackLineReader* reader) {
+  char* const ptr = reader->buffer + reader->view.size;
+  const size_t size_to_read = STACK_LINE_READER_BUFFER_SIZE - reader->view.size;
+  const int read = ReadFile(reader->fd, ptr, size_to_read);
+  assert(read >= 0);
+  assert(read <= (int)size_to_read);
+  reader->view.size += read;
+  return read;
+}
+
+static int IndexOfEol(StackLineReader* reader) {
+  return IndexOfChar(reader->view, '\n');
+}
+
+// Relocate buffer's pending bytes at the beginning of the array and fills the
+// remaining space with bytes from the file.
+static int BringToFrontAndLoadMore(StackLineReader* reader) {
+  if (reader->view.size && reader->view.ptr != reader->buffer) {
+    memmove(reader->buffer, reader->view.ptr, reader->view.size);
+  }
+  reader->view.ptr = reader->buffer;
+  return LoadMore(reader);
+}
+
+// Loads chunks of buffer size from disks until it contains a newline character
+// or end of file.
+static void SkipToNextLine(StackLineReader* reader) {
+  for (;;) {
+    const int read = LoadFullBuffer(reader);
+    if (read == 0) {
+      break;
+    } else {
+      const int eol_index = IndexOfEol(reader);
+      if (eol_index >= 0) {
+        reader->view = PopFront(reader->view, eol_index + 1);
+        break;
+      }
+    }
+  }
+}
+
+static LineResult CreateLineResult(bool eof, bool full_line, StringView view) {
+  LineResult result;
+  result.eof = eof;
+  result.full_line = full_line;
+  result.line = view;
+  return result;
+}
+
+// Helper methods to provide clearer semantic in StackLineReader_NextLine.
+static LineResult CreateEOFLineResult(StringView view) {
+  return CreateLineResult(true, true, view);
+}
+
+static LineResult CreateTruncatedLineResult(StringView view) {
+  return CreateLineResult(false, false, view);
+}
+
+static LineResult CreateValidLineResult(StringView view) {
+  return CreateLineResult(false, true, view);
+}
+
+LineResult StackLineReader_NextLine(StackLineReader* reader) {
+  if (reader->skip_mode) {
+    SkipToNextLine(reader);
+    reader->skip_mode = false;
+  }
+  {
+    const bool can_load_more =
+        reader->view.size < STACK_LINE_READER_BUFFER_SIZE;
+    int eol_index = IndexOfEol(reader);
+    if (eol_index < 0 && can_load_more) {
+      const int read = BringToFrontAndLoadMore(reader);
+      if (read == 0) {
+        return CreateEOFLineResult(reader->view);
+      }
+      eol_index = IndexOfEol(reader);
+    }
+    if (eol_index < 0) {
+      reader->skip_mode = true;
+      return CreateTruncatedLineResult(reader->view);
+    }
+    {
+      StringView line = KeepFront(reader->view, eol_index);
+      reader->view = PopFront(reader->view, eol_index + 1);
+      return CreateValidLineResult(line);
+    }
+  }
+}
diff --git a/src/string_view.c b/src/string_view.c
new file mode 100644
index 0000000..9aae6e1
--- /dev/null
+++ b/src/string_view.c
@@ -0,0 +1,163 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "internal/string_view.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <string.h>
+
+int IndexOfChar(const StringView view, char c) {
+  if (view.ptr && view.size) {
+    const char* const found = (const char*)memchr(view.ptr, c, view.size);
+    if (found) {
+      return found - view.ptr;
+    }
+  }
+  return -1;
+}
+
+int IndexOf(const StringView view, const StringView sub_view) {
+  if (sub_view.size) {
+    StringView remainder = view;
+    while (remainder.size >= sub_view.size) {
+      const int found_index = IndexOfChar(remainder, sub_view.ptr[0]);
+      if (found_index < 0) break;
+      remainder = PopFront(remainder, found_index);
+      if (StartsWith(remainder, sub_view)) {
+        return remainder.ptr - view.ptr;
+      }
+      remainder = PopFront(remainder, 1);
+    }
+  }
+  return -1;
+}
+
+bool IsEquals(const StringView a, const StringView b) {
+  if (a.size == b.size) {
+    return a.ptr == b.ptr || memcmp(a.ptr, b.ptr, b.size) == 0;
+  }
+  return false;
+}
+
+bool StartsWith(const StringView a, const StringView b) {
+  return a.ptr && b.ptr && b.size && a.size >= b.size
+             ? memcmp(a.ptr, b.ptr, b.size) == 0
+             : false;
+}
+
+StringView PopFront(const StringView str_view, size_t count) {
+  if (count > str_view.size) {
+    return kEmptyStringView;
+  }
+  return view(str_view.ptr + count, str_view.size - count);
+}
+
+StringView PopBack(const StringView str_view, size_t count) {
+  if (count > str_view.size) {
+    return kEmptyStringView;
+  }
+  return view(str_view.ptr, str_view.size - count);
+}
+
+StringView KeepFront(const StringView str_view, size_t count) {
+  return count <= str_view.size ? view(str_view.ptr, count) : str_view;
+}
+
+char Front(const StringView view) {
+  assert(view.size);
+  assert(view.ptr);
+  return view.ptr[0];
+}
+
+char Back(const StringView view) {
+  assert(view.size);
+  return view.ptr[view.size - 1];
+}
+
+StringView TrimWhitespace(StringView view) {
+  while (view.size && isspace(Front(view))) view = PopFront(view, 1);
+  while (view.size && isspace(Back(view))) view = PopBack(view, 1);
+  return view;
+}
+
+static int HexValue(const char c) {
+  if (c >= '0' && c <= '9') return c - '0';
+  if (c >= 'a' && c <= 'f') return c - 'a' + 10;
+  if (c >= 'A' && c <= 'F') return c - 'A' + 10;
+  return -1;
+}
+
+// Returns -1 if view contains non digits.
+static int ParsePositiveNumberWithBase(const StringView view, int base) {
+  int result = 0;
+  StringView remainder = view;
+  for (; remainder.size; remainder = PopFront(remainder, 1)) {
+    const int value = HexValue(Front(remainder));
+    if (value < 0 || value >= base) return -1;
+    result = (result * base) + value;
+  }
+  return result;
+}
+
+int ParsePositiveNumber(const StringView view) {
+  if (view.size) {
+    const StringView hex_prefix = str("0x");
+    if (StartsWith(view, hex_prefix)) {
+      const StringView span_no_prefix = PopFront(view, hex_prefix.size);
+      return ParsePositiveNumberWithBase(span_no_prefix, 16);
+    }
+    return ParsePositiveNumberWithBase(view, 10);
+  }
+  return -1;
+}
+
+void CopyString(const StringView src, char* dst, size_t dst_size) {
+  if (dst_size > 0) {
+    const size_t max_copy_size = dst_size - 1;
+    const size_t copy_size =
+        src.size > max_copy_size ? max_copy_size : src.size;
+    memcpy(dst, src.ptr, copy_size);
+    dst[copy_size] = '\0';
+  }
+}
+
+bool HasWord(const StringView line, const char* const word_str) {
+  const StringView word = str(word_str);
+  StringView remainder = line;
+  for (;;) {
+    const int index_of_word = IndexOf(remainder, word);
+    if (index_of_word < 0) {
+      return false;
+    } else {
+      const StringView before = KeepFront(line, index_of_word);
+      const StringView after = PopFront(line, index_of_word + word.size);
+      const bool valid_before = before.size == 0 || Back(before) == ' ';
+      const bool valid_after = after.size == 0 || Front(after) == ' ';
+      if (valid_before && valid_after) return true;
+      remainder = PopFront(remainder, index_of_word + word.size);
+    }
+  }
+  return false;
+}
+
+bool GetAttributeKeyValue(const StringView line, StringView* key,
+                          StringView* value) {
+  const StringView sep = str(": ");
+  const int index_of_separator = IndexOf(line, sep);
+  if (index_of_separator < 0) return false;
+  *value = TrimWhitespace(PopFront(line, index_of_separator + sep.size));
+  *key = TrimWhitespace(KeepFront(line, index_of_separator));
+  return true;
+}
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
new file mode 100644
index 0000000..f18f228
--- /dev/null
+++ b/test/CMakeLists.txt
@@ -0,0 +1,74 @@
+#
+# libraries for tests
+#
+
+set(CMAKE_CXX_STANDARD 11)
+set(CXX_STANDARD_REQUIRED ON)
+set(CMAKE_CXX_EXTENSIONS OFF) # prefer use of -std11 instead of -gnustd11
+
+include_directories(../include)
+add_definitions(-DCPU_FEATURES_TEST)
+
+##------------------------------------------------------------------------------
+add_library(string_view ../src/string_view.c)
+##------------------------------------------------------------------------------
+add_library(filesystem_for_testing filesystem_for_testing.cc)
+##------------------------------------------------------------------------------
+add_library(hwcaps_for_testing hwcaps_for_testing.cc)
+target_link_libraries(hwcaps_for_testing filesystem_for_testing)
+##------------------------------------------------------------------------------
+add_library(stack_line_reader ../src/stack_line_reader.c)
+target_compile_definitions(stack_line_reader PUBLIC STACK_LINE_READER_BUFFER_SIZE=1024)
+target_link_libraries(stack_line_reader string_view)
+##------------------------------------------------------------------------------
+add_library(stack_line_reader_for_test ../src/stack_line_reader.c)
+target_compile_definitions(stack_line_reader_for_test PUBLIC STACK_LINE_READER_BUFFER_SIZE=16)
+target_link_libraries(stack_line_reader_for_test string_view filesystem_for_testing)
+##------------------------------------------------------------------------------
+add_library(all_libraries ../src/stack_line_reader.c ../src/linux_features_aggregator.c)
+target_link_libraries(all_libraries hwcaps_for_testing stack_line_reader string_view)
+
+#
+# tests
+#
+link_libraries(gtest gmock_main)
+
+## bit_utils_test
+add_executable(bit_utils_test bit_utils_test.cc)
+target_link_libraries(bit_utils_test)
+add_test(NAME bit_utils_test COMMAND bit_utils_test)
+##------------------------------------------------------------------------------
+## string_view_test
+add_executable(string_view_test string_view_test.cc ../src/string_view.c)
+target_link_libraries(string_view_test string_view)
+add_test(NAME string_view_test COMMAND string_view_test)
+##------------------------------------------------------------------------------
+## stack_line_reader_test
+add_executable(stack_line_reader_test stack_line_reader_test.cc)
+target_link_libraries(stack_line_reader_test stack_line_reader_for_test)
+add_test(NAME stack_line_reader_test COMMAND stack_line_reader_test)
+##------------------------------------------------------------------------------
+## linux_features_aggregator_test
+add_executable(linux_features_aggregator_test linux_features_aggregator_test.cc)
+target_link_libraries(linux_features_aggregator_test all_libraries)
+add_test(NAME linux_features_aggregator_test COMMAND linux_features_aggregator_test)
+##------------------------------------------------------------------------------
+## cpuinfo_x86_test
+add_executable(cpuinfo_x86_test cpuinfo_x86_test.cc ../src/cpuinfo_x86.c)
+target_link_libraries(cpuinfo_x86_test all_libraries)
+add_test(NAME cpuinfo_x86_test COMMAND cpuinfo_x86_test)
+##------------------------------------------------------------------------------
+## cpuinfo_arm_test
+add_executable(cpuinfo_arm_test cpuinfo_arm_test.cc ../src/cpuinfo_arm.c)
+target_link_libraries(cpuinfo_arm_test all_libraries)
+add_test(NAME cpuinfo_arm_test COMMAND cpuinfo_arm_test)
+##------------------------------------------------------------------------------
+## cpuinfo_aarch64_test
+add_executable(cpuinfo_aarch64_test cpuinfo_aarch64_test.cc ../src/cpuinfo_aarch64.c)
+target_link_libraries(cpuinfo_aarch64_test all_libraries)
+add_test(NAME cpuinfo_aarch64_test COMMAND cpuinfo_aarch64_test)
+##------------------------------------------------------------------------------
+## cpuinfo_mips_test
+add_executable(cpuinfo_mips_test cpuinfo_mips_test.cc  ../src/cpuinfo_mips.c)
+target_link_libraries(cpuinfo_mips_test all_libraries)
+add_test(NAME cpuinfo_mips_test COMMAND cpuinfo_mips_test)
diff --git a/test/bit_utils_test.cc b/test/bit_utils_test.cc
new file mode 100644
index 0000000..8937cbc
--- /dev/null
+++ b/test/bit_utils_test.cc
@@ -0,0 +1,53 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "internal/bit_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace cpu_features {
+namespace {
+
+TEST(UtilsTest, IsBitSet) {
+  for (size_t bit_set = 0; bit_set < 32; ++bit_set) {
+    const uint32_t value = 1UL << bit_set;
+    for (size_t i = 0; i < 32; ++i) {
+      EXPECT_EQ(IsBitSet(value, i), i == bit_set);
+    }
+  }
+
+  // testing 0, all bits should be 0.
+  for (size_t i = 0; i < 32; ++i) {
+    EXPECT_FALSE(IsBitSet(0, i));
+  }
+
+  // testing ~0, all bits should be 1.
+  for (size_t i = 0; i < 32; ++i) {
+    EXPECT_TRUE(IsBitSet(-1, i));
+  }
+}
+
+TEST(UtilsTest, ExtractBitRange) {
+  // Extracting all bits gives the same number.
+  EXPECT_EQ(ExtractBitRange(123, 31, 0), 123);
+  // Extracting 1 bit gives parity.
+  EXPECT_EQ(ExtractBitRange(123, 0, 0), 1);
+  EXPECT_EQ(ExtractBitRange(122, 0, 0), 0);
+
+  EXPECT_EQ(ExtractBitRange(0xF0, 7, 4), 0xF);
+  EXPECT_EQ(ExtractBitRange(0x42 << 2, 10, 2), 0x42);
+}
+
+}  // namespace
+}  // namespace cpu_features
diff --git a/test/cpuinfo_aarch64_test.cc b/test/cpuinfo_aarch64_test.cc
new file mode 100644
index 0000000..bdb4d17
--- /dev/null
+++ b/test/cpuinfo_aarch64_test.cc
@@ -0,0 +1,74 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cpuinfo_aarch64.h"
+#include "filesystem_for_testing.h"
+#include "hwcaps_for_testing.h"
+
+#include "gtest/gtest.h"
+
+namespace cpu_features {
+namespace {
+
+void DisableHardwareCapabilities() { SetHardwareCapabilities(0, 0); }
+
+TEST(CpuinfoAarch64Test, FromHardwareCap) {
+  SetHardwareCapabilities(AARCH64_HWCAP_FP | AARCH64_HWCAP_AES, 0);
+  GetEmptyFilesystem();  // disabling /proc/cpuinfo
+  const auto info = GetAarch64Info();
+  EXPECT_TRUE(info.features.fp);
+  EXPECT_FALSE(info.features.asimd);
+  EXPECT_TRUE(info.features.aes);
+  EXPECT_FALSE(info.features.pmull);
+  EXPECT_FALSE(info.features.sha1);
+  EXPECT_FALSE(info.features.sha2);
+  EXPECT_FALSE(info.features.crc32);
+}
+
+TEST(CpuinfoAarch64Test, ARMCortexA53) {
+  DisableHardwareCapabilities();
+  auto& fs = GetEmptyFilesystem();
+  fs.CreateFile("/proc/cpuinfo",
+                R"(Processor   : AArch64 Processor rev 3 (aarch64)
+processor   : 0
+processor   : 1
+processor   : 2
+processor   : 3
+processor   : 4
+processor   : 5
+processor   : 6
+processor   : 7
+Features    : fp asimd evtstrm aes pmull sha1 sha2 crc32
+CPU implementer : 0x41
+CPU architecture: AArch64
+CPU variant : 0x0
+CPU part    : 0xd03
+CPU revision    : 3)");
+  const auto info = GetAarch64Info();
+  EXPECT_EQ(info.implementer, 0x41);
+  EXPECT_EQ(info.variant, 0x0);
+  EXPECT_EQ(info.part, 0xd03);
+  EXPECT_EQ(info.revision, 3);
+
+  EXPECT_TRUE(info.features.fp);
+  EXPECT_TRUE(info.features.asimd);
+  EXPECT_TRUE(info.features.aes);
+  EXPECT_TRUE(info.features.pmull);
+  EXPECT_TRUE(info.features.sha1);
+  EXPECT_TRUE(info.features.sha2);
+  EXPECT_TRUE(info.features.crc32);
+}
+
+}  // namespace
+}  // namespace cpu_features
diff --git a/test/cpuinfo_arm_test.cc b/test/cpuinfo_arm_test.cc
new file mode 100644
index 0000000..a72c566
--- /dev/null
+++ b/test/cpuinfo_arm_test.cc
@@ -0,0 +1,182 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cpuinfo_arm.h"
+#include "filesystem_for_testing.h"
+#include "hwcaps_for_testing.h"
+
+#include "gtest/gtest.h"
+
+namespace cpu_features {
+namespace {
+
+void DisableHardwareCapabilities() { SetHardwareCapabilities(0, 0); }
+
+TEST(CpuinfoArmTest, FromHardwareCap) {
+  SetHardwareCapabilities(ARM_HWCAP_NEON, ARM_HWCAP2_AES | ARM_HWCAP2_CRC32);
+  GetEmptyFilesystem();  // disabling /proc/cpuinfo
+  const auto info = GetArmInfo();
+  EXPECT_TRUE(info.features.vfp);    // triggered by vfpv3
+  EXPECT_TRUE(info.features.vfpv3);  // triggered by neon
+  EXPECT_TRUE(info.features.neon);
+  EXPECT_TRUE(info.features.aes);
+  EXPECT_TRUE(info.features.crc32);
+
+  EXPECT_FALSE(info.features.vfpv4);
+  EXPECT_FALSE(info.features.iwmmxt);
+  EXPECT_FALSE(info.features.vfpv3d16);
+  EXPECT_FALSE(info.features.idiva);
+  EXPECT_FALSE(info.features.idivt);
+  EXPECT_FALSE(info.features.pmull);
+  EXPECT_FALSE(info.features.sha1);
+  EXPECT_FALSE(info.features.sha2);
+}
+
+TEST(CpuinfoArmTest, ODroidFromCpuInfo) {
+  DisableHardwareCapabilities();
+  auto& fs = GetEmptyFilesystem();
+  fs.CreateFile("/proc/cpuinfo", R"(processor       : 0
+model name      : ARMv7 Processor rev 3 (v71)
+BogoMIPS        : 120.00
+Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae
+CPU implementer : 0x41
+CPU architecture: 7
+CPU variant     : 0x2
+CPU part        : 0xc0f
+CPU revision    : 3)");
+  const auto info = GetArmInfo();
+  EXPECT_EQ(info.implementer, 0x41);
+  EXPECT_EQ(info.variant, 0x2);
+  EXPECT_EQ(info.part, 0xc0f);
+  EXPECT_EQ(info.revision, 3);
+  EXPECT_EQ(info.architecture, 7);
+
+  EXPECT_TRUE(info.features.vfp);
+  EXPECT_FALSE(info.features.iwmmxt);
+  EXPECT_TRUE(info.features.neon);
+  EXPECT_TRUE(info.features.vfpv3);
+  EXPECT_FALSE(info.features.vfpv3d16);
+  EXPECT_TRUE(info.features.vfpv4);
+  EXPECT_TRUE(info.features.idiva);
+  EXPECT_TRUE(info.features.idivt);
+  EXPECT_FALSE(info.features.aes);
+  EXPECT_FALSE(info.features.pmull);
+  EXPECT_FALSE(info.features.sha1);
+  EXPECT_FALSE(info.features.sha2);
+  EXPECT_FALSE(info.features.crc32);
+}
+
+// http://code.google.com/p/android/issues/detail?id=10812
+TEST(CpuinfoArmTest, InvalidArmv7) {
+  DisableHardwareCapabilities();
+  auto& fs = GetEmptyFilesystem();
+  fs.CreateFile("/proc/cpuinfo",
+                R"(Processor       : ARMv6-compatible processor rev 6 (v6l) 
+BogoMIPS        : 199.47 
+Features        : swp half thumb fastmult vfp edsp java 
+CPU implementer : 0x41 
+CPU architecture: 7 
+CPU variant     : 0x0 
+CPU part        : 0xb76 
+CPU revision    : 6 
+
+Hardware        : SPICA 
+Revision        : 0020 
+Serial          : 33323613546d00ec )");
+  const auto info = GetArmInfo();
+  EXPECT_EQ(info.architecture, 6);
+}
+
+// https://crbug.com/341598.
+TEST(CpuinfoArmTest, InvalidNeon) {
+  auto& fs = GetEmptyFilesystem();
+  fs.CreateFile("/proc/cpuinfo",
+                R"(Processor: ARMv7 Processory rev 0 (v71)
+processor: 0
+BogoMIPS: 13.50
+
+Processor: 1
+BogoMIPS: 13.50
+
+Features: swp half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt
+CPU implementer : 0x51
+CPU architecture: 7
+CPU variant: 0x1
+CPU part: 0x04d
+CPU revision: 0
+
+Hardware: SAMSUNG M2
+Revision: 0010
+Serial: 00001e030000354e)");
+  const auto info = GetArmInfo();
+  EXPECT_FALSE(info.features.neon);
+}
+
+// The Nexus 4 (Qualcomm Krait) kernel configuration forgets to report IDIV
+// support.
+TEST(CpuinfoArmTest, Nexus4_0x510006f2) {
+  DisableHardwareCapabilities();
+  auto& fs = GetEmptyFilesystem();
+  fs.CreateFile("/proc/cpuinfo",
+                R"(CPU implementer	: 0x51
+CPU architecture: 7
+CPU variant	: 0x0
+CPU part	: 0x6f
+CPU revision	: 2)");
+  const auto info = GetArmInfo();
+  EXPECT_TRUE(info.features.idiva);
+  EXPECT_TRUE(info.features.idivt);
+}
+
+// The Nexus 4 (Qualcomm Krait) kernel configuration forgets to report IDIV
+// support.
+TEST(CpuinfoArmTest, Nexus4_0x510006f3) {
+  DisableHardwareCapabilities();
+  auto& fs = GetEmptyFilesystem();
+  fs.CreateFile("/proc/cpuinfo",
+                R"(CPU implementer	: 0x51
+CPU architecture: 7
+CPU variant	: 0x0
+CPU part	: 0x6f
+CPU revision	: 3)");
+  const auto info = GetArmInfo();
+  EXPECT_TRUE(info.features.idiva);
+  EXPECT_TRUE(info.features.idivt);
+}
+
+// The emulator-specific Android 4.2 kernel fails to report support for the
+// 32-bit ARM IDIV instruction. Technically, this is a feature of the virtual
+// CPU implemented by the emulator.
+TEST(CpuinfoArmTest, EmulatorSpecificIdiv) {
+  DisableHardwareCapabilities();
+  auto& fs = GetEmptyFilesystem();
+  fs.CreateFile("/proc/cpuinfo",
+                R"(Processor	: ARMv7 Processor rev 0 (v7l)
+BogoMIPS	: 629.14
+Features	: swp half thumb fastmult vfp edsp neon vfpv3
+CPU implementer	: 0x41
+CPU architecture: 7
+CPU variant	: 0x0
+CPU part	: 0xc08
+CPU revision	: 0
+
+Hardware	: Goldfish
+Revision	: 0000
+Serial		: 0000000000000000)");
+  const auto info = GetArmInfo();
+  EXPECT_TRUE(info.features.idiva);
+}
+
+}  // namespace
+}  // namespace cpu_features
diff --git a/test/cpuinfo_mips_test.cc b/test/cpuinfo_mips_test.cc
new file mode 100644
index 0000000..7c5a675
--- /dev/null
+++ b/test/cpuinfo_mips_test.cc
@@ -0,0 +1,125 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cpuinfo_mips.h"
+#include "filesystem_for_testing.h"
+#include "hwcaps_for_testing.h"
+#include "internal/stack_line_reader.h"
+#include "internal/string_view.h"
+
+#include "gtest/gtest.h"
+
+namespace cpu_features {
+
+namespace {
+
+void DisableHardwareCapabilities() { SetHardwareCapabilities(0, 0); }
+
+TEST(CpuinfoMipsTest, FromHardwareCapBoth) {
+  SetHardwareCapabilities(MIPS_HWCAP_EVA | MIPS_HWCAP_MSA, 0);
+  GetEmptyFilesystem();  // disabling /proc/cpuinfo
+  const auto info = GetMipsInfo();
+  EXPECT_TRUE(info.features.msa);
+  EXPECT_TRUE(info.features.eva);
+}
+
+TEST(CpuinfoMipsTest, FromHardwareCapOnlyOne) {
+  SetHardwareCapabilities(MIPS_HWCAP_MSA, 0);
+  GetEmptyFilesystem();  // disabling /proc/cpuinfo
+  const auto info = GetMipsInfo();
+  EXPECT_TRUE(info.features.msa);
+  EXPECT_FALSE(info.features.eva);
+}
+
+TEST(CpuinfoMipsTest, Ci40) {
+  DisableHardwareCapabilities();
+  auto& fs = GetEmptyFilesystem();
+  fs.CreateFile("/proc/cpuinfo", R"(system type : IMG Pistachio SoC (B0)
+machine : IMG Marduk – Ci40 with cc2520
+processor : 0
+cpu model : MIPS interAptiv (multi) V2.0 FPU V0.0
+BogoMIPS : 363.72
+wait instruction : yes
+microsecond timers : yes
+tlb_entries : 64
+extra interrupt vector : yes
+hardware watchpoint : yes, count: 4, address/irw mask: [0x0ffc, 0x0ffc, 0x0ffb, 0x0ffb]
+isa : mips1 mips2 mips32r1 mips32r2
+ASEs implemented : mips16 dsp mt eva
+shadow register sets : 1
+kscratch registers : 0
+package : 0
+core : 0
+VCED exceptions : not available
+VCEI exceptions : not available
+VPE : 0
+)");
+  const auto info = GetMipsInfo();
+  EXPECT_FALSE(info.features.msa);
+  EXPECT_TRUE(info.features.eva);
+}
+
+TEST(CpuinfoMipsTest, AR7161) {
+  DisableHardwareCapabilities();
+  auto& fs = GetEmptyFilesystem();
+  fs.CreateFile("/proc/cpuinfo",
+                R"(system type             : Atheros AR7161 rev 2
+machine                 : NETGEAR WNDR3700/WNDR3800/WNDRMAC
+processor               : 0
+cpu model               : MIPS 24Kc V7.4
+BogoMIPS                : 452.19
+wait instruction        : yes
+microsecond timers      : yes
+tlb_entries             : 16
+extra interrupt vector  : yes
+hardware watchpoint     : yes, count: 4, address/irw mask: [0x0000, 0x0f98, 0x0f78, 0x0df8]
+ASEs implemented        : mips16
+shadow register sets    : 1
+kscratch registers      : 0
+core                    : 0
+VCED exceptions         : not available
+VCEI exceptions         : not available
+)");
+  const auto info = GetMipsInfo();
+  EXPECT_FALSE(info.features.msa);
+  EXPECT_FALSE(info.features.eva);
+}
+
+TEST(CpuinfoMipsTest, Goldfish) {
+  DisableHardwareCapabilities();
+  auto& fs = GetEmptyFilesystem();
+  fs.CreateFile("/proc/cpuinfo", R"(system type		: MIPS-Goldfish
+Hardware		: goldfish
+Revison		: 1
+processor		: 0
+cpu model		: MIPS 24Kc V0.0  FPU V0.0
+BogoMIPS		: 1042.02
+wait instruction	: yes
+microsecond timers	: yes
+tlb_entries		: 16
+extra interrupt vector	: yes
+hardware watchpoint	: yes, count: 1, address/irw mask: [0x0ff8]
+ASEs implemented	:
+shadow register sets	: 1
+core			: 0
+VCED exceptions		: not available
+VCEI exceptions		: not available
+)");
+  const auto info = GetMipsInfo();
+  EXPECT_FALSE(info.features.msa);
+  EXPECT_FALSE(info.features.eva);
+}
+
+}  // namespace
+}  // namespace cpu_features
diff --git a/test/cpuinfo_x86_test.cc b/test/cpuinfo_x86_test.cc
new file mode 100644
index 0000000..f7fc081
--- /dev/null
+++ b/test/cpuinfo_x86_test.cc
@@ -0,0 +1,172 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <cassert>
+#include <cstdio>
+#include <map>
+
+#include "gtest/gtest.h"
+
+#include "cpuinfo_x86.h"
+#include "internal/cpuid_x86.h"
+
+namespace cpu_features {
+
+class FakeCpu {
+ public:
+  Leaf CpuId(uint32_t leaf_id) const {
+    const auto itr = cpuid_leaves_.find(leaf_id);
+    EXPECT_TRUE(itr != cpuid_leaves_.end()) << "Missing leaf " << leaf_id;
+    return itr->second;
+  }
+
+  uint32_t GetXCR0Eax() const { return xcr0_eax_; }
+
+  void SetLeaves(std::map<uint32_t, Leaf> configuration) {
+    cpuid_leaves_ = std::move(configuration);
+  }
+
+  void SetOsBackupsExtendedRegisters(bool os_backups_extended_registers) {
+    xcr0_eax_ = os_backups_extended_registers ? -1 : 0;
+  }
+
+ private:
+  std::map<uint32_t, Leaf> cpuid_leaves_;
+  uint32_t xcr0_eax_;
+};
+
+auto* g_fake_cpu = new FakeCpu();
+
+extern "C" Leaf CpuId(uint32_t leaf_id) { return g_fake_cpu->CpuId(leaf_id); }
+extern "C" uint32_t GetXCR0Eax(void) { return g_fake_cpu->GetXCR0Eax(); }
+
+namespace {
+
+TEST(CpuidX86Test, SandyBridge) {
+  g_fake_cpu->SetOsBackupsExtendedRegisters(true);
+  g_fake_cpu->SetLeaves({
+      {0x00000000, Leaf{0x0000000D, 0x756E6547, 0x6C65746E, 0x49656E69}},
+      {0x00000001, Leaf{0x000206A6, 0x00100800, 0x1F9AE3BF, 0xBFEBFBFF}},
+      {0x00000007, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
+  });
+  const auto info = GetX86Info();
+  EXPECT_STREQ(info.vendor, "GenuineIntel");
+  EXPECT_EQ(info.family, 0x06);
+  EXPECT_EQ(info.model, 0x02A);
+  EXPECT_EQ(info.stepping, 0x06);
+  // Leaf 7 is zeroed out so none of the Leaf 7 flags are set.
+  const auto features = info.features;
+  EXPECT_FALSE(features.erms);
+  EXPECT_FALSE(features.avx2);
+  EXPECT_FALSE(features.avx512f);
+  EXPECT_FALSE(features.avx512cd);
+  EXPECT_FALSE(features.avx512er);
+  EXPECT_FALSE(features.avx512pf);
+  EXPECT_FALSE(features.avx512bw);
+  EXPECT_FALSE(features.avx512dq);
+  EXPECT_FALSE(features.avx512vl);
+  EXPECT_FALSE(features.avx512ifma);
+  EXPECT_FALSE(features.avx512vbmi);
+  EXPECT_FALSE(features.avx512vbmi2);
+  EXPECT_FALSE(features.avx512vnni);
+  EXPECT_FALSE(features.avx512bitalg);
+  EXPECT_FALSE(features.avx512vpopcntdq);
+  EXPECT_FALSE(features.avx512_4vnniw);
+  EXPECT_FALSE(features.avx512_4vbmi2);
+  // All old cpu features should be set.
+  EXPECT_TRUE(features.aes);
+  EXPECT_TRUE(features.ssse3);
+  EXPECT_TRUE(features.sse4_1);
+  EXPECT_TRUE(features.sse4_2);
+  EXPECT_TRUE(features.avx);
+}
+
+TEST(CpuidX86Test, SandyBridgeTestOsSupport) {
+  g_fake_cpu->SetLeaves({
+      {0x00000000, Leaf{0x0000000D, 0x756E6547, 0x6C65746E, 0x49656E69}},
+      {0x00000001, Leaf{0x000206A6, 0x00100800, 0x1F9AE3BF, 0xBFEBFBFF}},
+      {0x00000007, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
+  });
+  // avx is disabled if os does not support backing up ymm registers.
+  g_fake_cpu->SetOsBackupsExtendedRegisters(false);
+  EXPECT_FALSE(GetX86Info().features.avx);
+  // avx is disabled if os does not support backing up ymm registers.
+  g_fake_cpu->SetOsBackupsExtendedRegisters(true);
+  EXPECT_TRUE(GetX86Info().features.avx);
+}
+
+TEST(CpuidX86Test, SkyLake) {
+  g_fake_cpu->SetOsBackupsExtendedRegisters(true);
+  g_fake_cpu->SetLeaves({
+      {0x00000000, Leaf{0x00000016, 0x756E6547, 0x6C65746E, 0x49656E69}},
+      {0x00000001, Leaf{0x000406E3, 0x00100800, 0x7FFAFBBF, 0xBFEBFBFF}},
+      {0x00000007, Leaf{0x00000000, 0x029C67AF, 0x00000000, 0x00000000}},
+  });
+  const auto info = GetX86Info();
+  EXPECT_STREQ(info.vendor, "GenuineIntel");
+  EXPECT_EQ(info.family, 0x06);
+  EXPECT_EQ(info.model, 0x04E);
+  EXPECT_EQ(info.stepping, 0x03);
+  EXPECT_EQ(GetX86Microarchitecture(&info), X86Microarchitecture::INTEL_SKL);
+}
+
+TEST(CpuidX86Test, Branding) {
+  g_fake_cpu->SetLeaves({
+      {0x00000000, Leaf{0x00000016, 0x756E6547, 0x6C65746E, 0x49656E69}},
+      {0x00000001, Leaf{0x000406E3, 0x00100800, 0x7FFAFBBF, 0xBFEBFBFF}},
+      {0x00000007, Leaf{0x00000000, 0x029C67AF, 0x00000000, 0x00000000}},
+      {0x80000000, Leaf{0x80000008, 0x00000000, 0x00000000, 0x00000000}},
+      {0x80000001, Leaf{0x00000000, 0x00000000, 0x00000121, 0x2C100000}},
+      {0x80000002, Leaf{0x65746E49, 0x2952286C, 0x726F4320, 0x4D542865}},
+      {0x80000003, Leaf{0x37692029, 0x3035362D, 0x43205530, 0x40205550}},
+      {0x80000004, Leaf{0x352E3220, 0x7A484730, 0x00000000, 0x00000000}},
+  });
+  char brand_string[49];
+  FillX86BrandString(brand_string);
+  EXPECT_STREQ(brand_string, "Intel(R) Core(TM) i7-6500U CPU @ 2.50GHz");
+}
+
+// http://users.atw.hu/instlatx64/AuthenticAMD0630F81_K15_Godavari_CPUID.txt
+TEST(CpuidX86Test, AMD_K15) {
+  g_fake_cpu->SetLeaves({
+      {0x00000000, Leaf{0x0000000D, 0x68747541, 0x444D4163, 0x69746E65}},
+      {0x00000001, Leaf{0x00630F81, 0x00040800, 0x3E98320B, 0x178BFBFF}},
+      {0x00000007, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
+      {0x80000000, Leaf{0x8000001E, 0x68747541, 0x444D4163, 0x69746E65}},
+      {0x80000001, Leaf{0x00630F81, 0x10000000, 0x0FEBBFFF, 0x2FD3FBFF}},
+      {0x80000002, Leaf{0x20444D41, 0x372D3841, 0x4B303736, 0x64615220}},
+      {0x80000003, Leaf{0x206E6F65, 0x202C3752, 0x43203031, 0x75706D6F}},
+      {0x80000004, Leaf{0x43206574, 0x7365726F, 0x2B433420, 0x00204736}},
+      {0x80000005, Leaf{0xFF40FF18, 0xFF40FF30, 0x10040140, 0x60030140}},
+  });
+  const auto info = GetX86Info();
+
+  EXPECT_STREQ(info.vendor, "AuthenticAMD");
+  EXPECT_EQ(info.family, 0x15);
+  EXPECT_EQ(info.model, 0x38);
+  EXPECT_EQ(info.stepping, 0x01);
+  EXPECT_EQ(GetX86Microarchitecture(&info),
+            X86Microarchitecture::AMD_BULLDOZER);
+
+  char brand_string[49];
+  FillX86BrandString(brand_string);
+  EXPECT_STREQ(brand_string, "AMD A8-7670K Radeon R7, 10 Compute Cores 4C+6G ");
+}
+
+// TODO(user): test what happens when xsave/osxsave are not present.
+// TODO(user): test what happens when xmm/ymm/zmm os support are not
+// present.
+
+}  // namespace
+}  // namespace cpu_features
diff --git a/test/filesystem_for_testing.cc b/test/filesystem_for_testing.cc
new file mode 100644
index 0000000..886d510
--- /dev/null
+++ b/test/filesystem_for_testing.cc
@@ -0,0 +1,102 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "filesystem_for_testing.h"
+
+#include <cassert>
+#include <climits>
+#include <cstdio>
+#include <cstring>
+#include <utility>
+
+namespace cpu_features {
+
+FakeFile::FakeFile(int file_descriptor, const char* content)
+    : file_descriptor_(file_descriptor), content_(content) {}
+
+FakeFile::~FakeFile() { assert(!opened_); }
+
+void FakeFile::Open() {
+  assert(!opened_);
+  opened_ = true;
+}
+
+void FakeFile::Close() {
+  assert(opened_);
+  opened_ = false;
+}
+
+int FakeFile::Read(int fd, void* buf, size_t count) {
+  assert(count < INT_MAX);
+  assert(fd == file_descriptor_);
+  const size_t remainder = content_.size() - head_index_;
+  const size_t read = count > remainder ? remainder : count;
+  memcpy(buf, content_.data() + head_index_, read);
+  head_index_ += read;
+  assert(read < INT_MAX);
+  return read;
+}
+
+void FakeFilesystem::Reset() { files_.clear(); }
+
+FakeFile* FakeFilesystem::CreateFile(const std::string& filename,
+                                     const char* content) {
+  auto& file = files_[filename];
+  file =
+      std::unique_ptr<FakeFile>(new FakeFile(next_file_descriptor_++, content));
+  return file.get();
+}
+
+FakeFile* FakeFilesystem::FindFileOrNull(const std::string& filename) const {
+  const auto itr = files_.find(filename);
+  return itr == files_.end() ? nullptr : itr->second.get();
+}
+
+FakeFile* FakeFilesystem::FindFileOrDie(const int file_descriptor) const {
+  for (const auto& filename_file_pair : files_) {
+    FakeFile* const file_ptr = filename_file_pair.second.get();
+    if (file_ptr->GetFileDescriptor() == file_descriptor) {
+      return file_ptr;
+    }
+  }
+  assert(false);
+  return nullptr;
+}
+
+static FakeFilesystem* kFilesystem = new FakeFilesystem();
+
+FakeFilesystem& GetEmptyFilesystem() {
+  kFilesystem->Reset();
+  return *kFilesystem;
+}
+
+extern "C" int OpenFile(const char* filename) {
+  auto* const file = kFilesystem->FindFileOrNull(filename);
+  if (file) {
+    file->Open();
+    return file->GetFileDescriptor();
+  }
+  return -1;
+}
+
+extern "C" void CloseFile(int file_descriptor) {
+  kFilesystem->FindFileOrDie(file_descriptor)->Close();
+}
+
+extern "C" int ReadFile(int file_descriptor, void* buf, size_t count) {
+  return kFilesystem->FindFileOrDie(file_descriptor)
+      ->Read(file_descriptor, buf, count);
+}
+
+}  // namespace cpu_features
diff --git a/test/filesystem_for_testing.h b/test/filesystem_for_testing.h
new file mode 100644
index 0000000..46b3a49
--- /dev/null
+++ b/test/filesystem_for_testing.h
@@ -0,0 +1,61 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Implements a fake filesystem, useful for tests.
+#ifndef THIRD_PARTY_CPU_FEATURES_TEST_FILESYSTEM_FOR_TESTING_H_
+#define THIRD_PARTY_CPU_FEATURES_TEST_FILESYSTEM_FOR_TESTING_H_
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "internal/filesystem.h"
+
+namespace cpu_features {
+
+class FakeFile {
+ public:
+  explicit FakeFile(int file_descriptor, const char* content);
+  ~FakeFile();
+
+  void Open();
+  void Close();
+  int Read(int fd, void* buf, size_t count);
+
+  int GetFileDescriptor() const { return file_descriptor_; }
+
+ private:
+  const int file_descriptor_;
+  const std::string content_;
+  bool opened_ = false;
+  size_t head_index_ = 0;
+};
+
+class FakeFilesystem {
+ public:
+  void Reset();
+  FakeFile* CreateFile(const std::string& filename, const char* content);
+  FakeFile* FindFileOrDie(const int file_descriptor) const;
+  FakeFile* FindFileOrNull(const std::string& filename) const;
+
+ private:
+  size_t next_file_descriptor_ = 0;
+  std::unordered_map<std::string, std::unique_ptr<FakeFile>> files_;
+};
+
+FakeFilesystem& GetEmptyFilesystem();
+
+}  // namespace cpu_features
+
+#endif  // THIRD_PARTY_CPU_FEATURES_TEST_FILESYSTEM_FOR_TESTING_H_
diff --git a/test/hwcaps_for_testing.cc b/test/hwcaps_for_testing.cc
new file mode 100644
index 0000000..f7e4729
--- /dev/null
+++ b/test/hwcaps_for_testing.cc
@@ -0,0 +1,32 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "hwcaps_for_testing.h"
+
+namespace cpu_features {
+
+namespace {
+static auto* const g_hardware_capabilities = new HardwareCapabilities();
+}  // namespace
+
+void SetHardwareCapabilities(uint32_t hwcaps, uint32_t hwcaps2) {
+  g_hardware_capabilities->hwcaps = hwcaps;
+  g_hardware_capabilities->hwcaps2 = hwcaps2;
+}
+
+HardwareCapabilities GetHardwareCapabilities(void) {
+  return *g_hardware_capabilities;
+}
+
+}  // namespace cpu_features
diff --git a/test/hwcaps_for_testing.h b/test/hwcaps_for_testing.h
new file mode 100644
index 0000000..d7bf470
--- /dev/null
+++ b/test/hwcaps_for_testing.h
@@ -0,0 +1,26 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef THIRD_PARTY_CPU_FEATURES_TEST_HWCAPS_FOR_TESTING_H_
+#define THIRD_PARTY_CPU_FEATURES_TEST_HWCAPS_FOR_TESTING_H_
+
+#include "internal/hwcaps.h"
+
+namespace cpu_features {
+
+void SetHardwareCapabilities(uint32_t hwcaps, uint32_t hwcaps2);
+
+}  // namespace cpu_features
+
+#endif  // THIRD_PARTY_CPU_FEATURES_TEST_HWCAPS_FOR_TESTING_H_
diff --git a/test/linux_features_aggregator_test.cc b/test/linux_features_aggregator_test.cc
new file mode 100644
index 0000000..8410f63
--- /dev/null
+++ b/test/linux_features_aggregator_test.cc
@@ -0,0 +1,90 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <array>
+
+#include "internal/linux_features_aggregator.h"
+
+#include "gtest/gtest.h"
+
+namespace cpu_features {
+
+namespace {
+
+struct Features {
+  bool a = false;
+  bool b = false;
+  bool c = false;
+};
+
+DECLARE_SETTER(Features, a)
+DECLARE_SETTER(Features, b)
+DECLARE_SETTER(Features, c)
+
+class LinuxFeatureAggregatorTest : public testing::Test {
+ public:
+  const std::array<CapabilityConfig, 3> kConfigs = {
+      {{{0b0001, 0b0000}, "a", &set_a},
+       {{0b0010, 0b0000}, "b", &set_b},
+       {{0b0000, 0b1100}, "c", &set_c}}};
+};
+
+TEST_F(LinuxFeatureAggregatorTest, FromFlagsEmpty) {
+  Features features;
+  SetFromFlags(kConfigs.size(), kConfigs.data(), str(""), &features);
+  EXPECT_FALSE(features.a);
+  EXPECT_FALSE(features.b);
+  EXPECT_FALSE(features.c);
+}
+
+TEST_F(LinuxFeatureAggregatorTest, FromFlagsAllSet) {
+  Features features;
+  SetFromFlags(kConfigs.size(), kConfigs.data(), str("a c b"), &features);
+  EXPECT_TRUE(features.a);
+  EXPECT_TRUE(features.b);
+  EXPECT_TRUE(features.c);
+}
+
+TEST_F(LinuxFeatureAggregatorTest, FromFlagsOnlyA) {
+  Features features;
+  SetFromFlags(kConfigs.size(), kConfigs.data(), str("a"), &features);
+  EXPECT_TRUE(features.a);
+  EXPECT_FALSE(features.b);
+  EXPECT_FALSE(features.c);
+}
+
+TEST_F(LinuxFeatureAggregatorTest, FromHwcapsNone) {
+  HardwareCapabilities capability;
+  capability.hwcaps = 0;   // matches none
+  capability.hwcaps2 = 0;  // matches none
+  Features features;
+  OverrideFromHwCaps(kConfigs.size(), kConfigs.data(), capability, &features);
+  EXPECT_FALSE(features.a);
+  EXPECT_FALSE(features.b);
+  EXPECT_FALSE(features.c);
+}
+
+TEST_F(LinuxFeatureAggregatorTest, FromHwcapsSet) {
+  HardwareCapabilities capability;
+  capability.hwcaps = 0b0010;   // matches b but not a
+  capability.hwcaps2 = 0b1111;  // matches c
+  Features features;
+  OverrideFromHwCaps(kConfigs.size(), kConfigs.data(), capability, &features);
+  EXPECT_FALSE(features.a);
+  EXPECT_TRUE(features.b);
+  EXPECT_TRUE(features.c);
+}
+
+}  // namespace
+}  // namespace cpu_features
diff --git a/test/stack_line_reader_test.cc b/test/stack_line_reader_test.cc
new file mode 100644
index 0000000..d255a9b
--- /dev/null
+++ b/test/stack_line_reader_test.cc
@@ -0,0 +1,132 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "internal/stack_line_reader.h"
+#include "filesystem_for_testing.h"
+
+#include "gtest/gtest.h"
+
+namespace cpu_features {
+
+bool operator==(const StringView& a, const StringView& b) {
+  return IsEquals(a, b);
+}
+
+namespace {
+
+std::string ToString(StringView view) { return {view.ptr, view.size}; }
+
+TEST(StackLineReaderTest, Empty) {
+  auto& fs = GetEmptyFilesystem();
+  auto* file = fs.CreateFile("/proc/cpuinfo", "");
+  StackLineReader reader;
+  StackLineReader_Initialize(&reader, file->GetFileDescriptor());
+  {
+    const auto result = StackLineReader_NextLine(&reader);
+    EXPECT_TRUE(result.eof);
+    EXPECT_TRUE(result.full_line);
+    EXPECT_EQ(result.line, str(""));
+  }
+}
+
+TEST(StackLineReaderTest, ManySmallLines) {
+  auto& fs = GetEmptyFilesystem();
+  auto* file = fs.CreateFile("/proc/cpuinfo", "a\nb\nc");
+
+  StackLineReader reader;
+  StackLineReader_Initialize(&reader, file->GetFileDescriptor());
+  {
+    const auto result = StackLineReader_NextLine(&reader);
+    EXPECT_FALSE(result.eof);
+    EXPECT_TRUE(result.full_line);
+    EXPECT_EQ(result.line, str("a"));
+  }
+  {
+    const auto result = StackLineReader_NextLine(&reader);
+    EXPECT_FALSE(result.eof);
+    EXPECT_TRUE(result.full_line);
+    EXPECT_EQ(result.line, str("b"));
+  }
+  {
+    const auto result = StackLineReader_NextLine(&reader);
+    EXPECT_TRUE(result.eof);
+    EXPECT_TRUE(result.full_line);
+    EXPECT_EQ(result.line, str("c"));
+  }
+}
+
+TEST(StackLineReaderTest, TruncatedLine) {
+  auto& fs = GetEmptyFilesystem();
+  auto* file = fs.CreateFile("/proc/cpuinfo", R"(First
+Second
+More than 16 characters, this will be truncated.
+last)");
+
+  StackLineReader reader;
+  StackLineReader_Initialize(&reader, file->GetFileDescriptor());
+  {
+    const auto result = StackLineReader_NextLine(&reader);
+    EXPECT_FALSE(result.eof);
+    EXPECT_TRUE(result.full_line);
+    EXPECT_EQ(result.line, str("First"));
+  }
+  {
+    const auto result = StackLineReader_NextLine(&reader);
+    EXPECT_FALSE(result.eof);
+    EXPECT_TRUE(result.full_line);
+    EXPECT_EQ(result.line, str("Second"));
+  }
+  {
+    const auto result = StackLineReader_NextLine(&reader);
+    EXPECT_FALSE(result.eof);
+    EXPECT_FALSE(result.full_line);
+    EXPECT_EQ(result.line, str("More than 16 cha"));
+  }
+  {
+    const auto result = StackLineReader_NextLine(&reader);
+    EXPECT_TRUE(result.eof);
+    EXPECT_TRUE(result.full_line);
+    EXPECT_EQ(result.line, str("last"));
+  }
+}
+
+TEST(StackLineReaderTest, TruncatedLines) {
+  auto& fs = GetEmptyFilesystem();
+  auto* file = fs.CreateFile("/proc/cpuinfo", R"(More than 16 characters
+Another line that is too long)");
+
+  StackLineReader reader;
+  StackLineReader_Initialize(&reader, file->GetFileDescriptor());
+  {
+    const auto result = StackLineReader_NextLine(&reader);
+    EXPECT_FALSE(result.eof);
+    EXPECT_FALSE(result.full_line);
+    EXPECT_EQ(result.line, str("More than 16 cha"));
+  }
+  {
+    const auto result = StackLineReader_NextLine(&reader);
+    EXPECT_FALSE(result.eof);
+    EXPECT_FALSE(result.full_line);
+    EXPECT_EQ(result.line, str("Another line tha"));
+  }
+  {
+    const auto result = StackLineReader_NextLine(&reader);
+    EXPECT_TRUE(result.eof);
+    EXPECT_TRUE(result.full_line);
+    EXPECT_EQ(result.line, str(""));
+  }
+}
+
+}  // namespace
+}  // namespace cpu_features
diff --git a/test/string_view_test.cc b/test/string_view_test.cc
new file mode 100644
index 0000000..f9fa3da
--- /dev/null
+++ b/test/string_view_test.cc
@@ -0,0 +1,138 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "internal/string_view.h"
+
+#include "gtest/gtest.h"
+
+namespace cpu_features {
+
+bool operator==(const StringView& a, const StringView& b) {
+  return IsEquals(a, b);
+}
+
+namespace {
+
+TEST(StringViewTest, Empty) {
+  EXPECT_EQ(kEmptyStringView.ptr, nullptr);
+  EXPECT_EQ(kEmptyStringView.size, 0);
+}
+
+TEST(StringViewTest, Build) {
+  const auto view = str("test");
+  EXPECT_EQ(view.ptr[0], 't');
+  EXPECT_EQ(view.size, 4);
+}
+
+TEST(StringViewTest, IndexOfChar) {
+  // Found.
+  EXPECT_EQ(IndexOfChar(str("test"), 'e'), 1);
+  // Not found.
+  EXPECT_EQ(IndexOfChar(str("test"), 'z'), -1);
+  // Empty.
+  EXPECT_EQ(IndexOfChar(kEmptyStringView, 'z'), -1);
+}
+
+TEST(StringViewTest, IndexOf) {
+  // Found.
+  EXPECT_EQ(IndexOf(str("test"), str("es")), 1);
+  // Not found.
+  EXPECT_EQ(IndexOf(str("test"), str("aa")), -1);
+  // Empty.
+  EXPECT_EQ(IndexOf(kEmptyStringView, str("aa")), -1);
+  EXPECT_EQ(IndexOf(str("aa"), kEmptyStringView), -1);
+}
+
+TEST(StringViewTest, StartsWith) {
+  EXPECT_TRUE(StartsWith(str("test"), str("te")));
+  EXPECT_FALSE(StartsWith(str("test"), str("")));
+  EXPECT_FALSE(StartsWith(str("test"), kEmptyStringView));
+  EXPECT_FALSE(StartsWith(kEmptyStringView, str("test")));
+}
+
+TEST(StringViewTest, IsEquals) {
+  EXPECT_TRUE(IsEquals(kEmptyStringView, kEmptyStringView));
+  EXPECT_TRUE(IsEquals(kEmptyStringView, str("")));
+  EXPECT_TRUE(IsEquals(str(""), kEmptyStringView));
+  EXPECT_TRUE(IsEquals(str("a"), str("a")));
+  EXPECT_FALSE(IsEquals(str("a"), str("b")));
+  EXPECT_FALSE(IsEquals(str("a"), kEmptyStringView));
+  EXPECT_FALSE(IsEquals(kEmptyStringView, str("a")));
+}
+
+TEST(StringViewTest, PopFront) {
+  EXPECT_EQ(PopFront(str("test"), 2), str("st"));
+  EXPECT_EQ(PopFront(str("test"), 0), str("test"));
+  EXPECT_EQ(PopFront(str("test"), 4), str(""));
+  EXPECT_EQ(PopFront(str("test"), 100), str(""));
+}
+
+TEST(StringViewTest, ParsePositiveNumber) {
+  EXPECT_EQ(ParsePositiveNumber(str("42")), 42);
+  EXPECT_EQ(ParsePositiveNumber(str("0x2a")), 42);
+  EXPECT_EQ(ParsePositiveNumber(str("0x2A")), 42);
+
+  EXPECT_EQ(ParsePositiveNumber(str("-0x2A")), -1);
+  EXPECT_EQ(ParsePositiveNumber(str("abc")), -1);
+  EXPECT_EQ(ParsePositiveNumber(str("")), -1);
+}
+
+TEST(StringViewTest, CopyString) {
+  char buf[4];
+  buf[0] = 'X';
+
+  // Empty
+  CopyString(str(""), buf, sizeof(buf));
+  EXPECT_STREQ(buf, "");
+
+  // Less
+  CopyString(str("a"), buf, sizeof(buf));
+  EXPECT_STREQ(buf, "a");
+
+  // exact
+  CopyString(str("abc"), buf, sizeof(buf));
+  EXPECT_STREQ(buf, "abc");
+
+  // More
+  CopyString(str("abcd"), buf, sizeof(buf));
+  EXPECT_STREQ(buf, "abc");
+}
+
+TEST(StringViewTest, HasWord) {
+  // Find flags at beginning, middle and end.
+  EXPECT_TRUE(HasWord(str("first middle last"), "first"));
+  EXPECT_TRUE(HasWord(str("first middle last"), "middle"));
+  EXPECT_TRUE(HasWord(str("first middle last"), "last"));
+  // Do not match partial flags
+  EXPECT_FALSE(HasWord(str("first middle last"), "irst"));
+  EXPECT_FALSE(HasWord(str("first middle last"), "mid"));
+  EXPECT_FALSE(HasWord(str("first middle last"), "las"));
+}
+
+TEST(StringViewTest, GetAttributeKeyValue) {
+  const StringView line = str(" key :   first middle last   ");
+  StringView key, value;
+  EXPECT_TRUE(GetAttributeKeyValue(line, &key, &value));
+  EXPECT_EQ(key, str("key"));
+  EXPECT_EQ(value, str("first middle last"));
+}
+
+TEST(StringViewTest, FailingGetAttributeKeyValue) {
+  const StringView line = str("key  first middle last");
+  StringView key, value;
+  EXPECT_FALSE(GetAttributeKeyValue(line, &key, &value));
+}
+
+}  // namespace
+}  // namespace cpu_features