From 439d371594c49d35b66493c8d2c33f2d16144f6d Mon Sep 17 00:00:00 2001 From: Guillaume Chatelet Date: Thu, 1 Feb 2018 10:03:09 +0100 Subject: [PATCH] Adding code. Closes #0. --- .clang-format | 4 + BUILD | 393 +++++++++++++++++ CMakeLists.txt | 93 ++++ CMakeLists.txt.in | 15 + CONTRIBUTING.md | 23 + LICENSE | 202 +++++++++ OWNERS | 4 + README.md | 12 +- WORKSPACE | 7 + include/cpu_features_macros.h | 121 ++++++ include/cpuinfo_aarch64.h | 65 +++ include/cpuinfo_arm.h | 80 ++++ include/cpuinfo_mips.h | 53 +++ include/cpuinfo_x86.h | 147 +++++++ include/internal/bit_utils.h | 39 ++ include/internal/cpuid_x86.h | 37 ++ include/internal/filesystem.h | 38 ++ include/internal/hwcaps.h | 73 ++++ include/internal/linux_features_aggregator.h | 58 +++ include/internal/stack_line_reader.h | 49 +++ include/internal/string_view.h | 101 +++++ src/cpuid_x86_clang.c | 32 ++ src/cpuid_x86_gcc.c | 32 ++ src/cpuid_x86_msvc.c | 34 ++ src/cpuinfo_aarch64.c | 140 ++++++ src/cpuinfo_arm.c | 255 +++++++++++ src/cpuinfo_mips.c | 97 +++++ src/cpuinfo_x86.c | 432 +++++++++++++++++++ src/filesystem.c | 53 +++ src/hwcaps.c | 165 +++++++ src/linux_features_aggregator.c | 48 +++ src/list_cpu_features.cc | 111 +++++ src/stack_line_reader.c | 128 ++++++ src/string_view.c | 163 +++++++ test/CMakeLists.txt | 74 ++++ test/bit_utils_test.cc | 53 +++ test/cpuinfo_aarch64_test.cc | 74 ++++ test/cpuinfo_arm_test.cc | 182 ++++++++ test/cpuinfo_mips_test.cc | 125 ++++++ test/cpuinfo_x86_test.cc | 172 ++++++++ test/filesystem_for_testing.cc | 102 +++++ test/filesystem_for_testing.h | 61 +++ test/hwcaps_for_testing.cc | 32 ++ test/hwcaps_for_testing.h | 26 ++ test/linux_features_aggregator_test.cc | 90 ++++ test/stack_line_reader_test.cc | 132 ++++++ test/string_view_test.cc | 138 ++++++ 47 files changed, 4559 insertions(+), 6 deletions(-) create mode 100644 .clang-format create mode 100644 BUILD create mode 100644 CMakeLists.txt create mode 100644 CMakeLists.txt.in create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE create mode 100644 OWNERS create mode 100644 WORKSPACE create mode 100644 include/cpu_features_macros.h create mode 100644 include/cpuinfo_aarch64.h create mode 100644 include/cpuinfo_arm.h create mode 100644 include/cpuinfo_mips.h create mode 100644 include/cpuinfo_x86.h create mode 100644 include/internal/bit_utils.h create mode 100644 include/internal/cpuid_x86.h create mode 100644 include/internal/filesystem.h create mode 100644 include/internal/hwcaps.h create mode 100644 include/internal/linux_features_aggregator.h create mode 100644 include/internal/stack_line_reader.h create mode 100644 include/internal/string_view.h create mode 100644 src/cpuid_x86_clang.c create mode 100644 src/cpuid_x86_gcc.c create mode 100644 src/cpuid_x86_msvc.c create mode 100644 src/cpuinfo_aarch64.c create mode 100644 src/cpuinfo_arm.c create mode 100644 src/cpuinfo_mips.c create mode 100644 src/cpuinfo_x86.c create mode 100644 src/filesystem.c create mode 100644 src/hwcaps.c create mode 100644 src/linux_features_aggregator.c create mode 100644 src/list_cpu_features.cc create mode 100644 src/stack_line_reader.c create mode 100644 src/string_view.c create mode 100644 test/CMakeLists.txt create mode 100644 test/bit_utils_test.cc create mode 100644 test/cpuinfo_aarch64_test.cc create mode 100644 test/cpuinfo_arm_test.cc create mode 100644 test/cpuinfo_mips_test.cc create mode 100644 test/cpuinfo_x86_test.cc create mode 100644 test/filesystem_for_testing.cc create mode 100644 test/filesystem_for_testing.h create mode 100644 test/hwcaps_for_testing.cc create mode 100644 test/hwcaps_for_testing.h create mode 100644 test/linux_features_aggregator_test.cc create mode 100644 test/stack_line_reader_test.cc create mode 100644 test/string_view_test.cc 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 $) +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 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 @@ or not. ## 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 +#include +#include +#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 + +#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 +#include +#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 +#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 +#include +#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 + +#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 +#include +#include +#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 + +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 + +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 +#include // 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 + +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 + +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 +#include + +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 +#include +#include +#include + +#if defined(_MSC_VER) +#include +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 + +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 +#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 +#include +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 +#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 + +#include +#include +#include + +#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 +std::string GetFlags(const HasFeatureFun HasFeature, + const FeatureNameFun FeatureName, + const FeatureType* features, const EnumType last) { + std::vector flags; + for (int i = 0; i < last; ++i) { + const EnumType enum_value = static_cast(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 +#include +#include + +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 +#include +#include + +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 +#include +#include + +#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 configuration) { + cpuid_leaves_ = std::move(configuration); + } + + void SetOsBackupsExtendedRegisters(bool os_backups_extended_registers) { + xcr0_eax_ = os_backups_extended_registers ? -1 : 0; + } + + private: + std::map 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 +#include +#include +#include +#include + +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(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 +#include +#include + +#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> 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 + +#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 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