Index: CMakeLists.txt =================================================================== --- CMakeLists.txt +++ CMakeLists.txt @@ -69,6 +69,13 @@ "Define the sanitizer used to build the library and tests") endif() +option(LIBCXX_ENABLE_BENCHMARKS "Enable the benchmark tests." OFF) + +# Allow the user to specify the path to the benchmark source directory. +# If no path is given the library is downloaded from SVN. +set(LIBCXX_BENCHMARK_SOURCE_PATH "" CACHE PATH + "The path to the benchmark source directory.") + if (LIBCXX_ENABLE_STATIC_ABI_LIBRARY) if (APPLE) message(FATAL_ERROR "LIBCXX_ENABLE_STATIC_ABI_LIBRARY is not supported on OS X") @@ -296,6 +303,7 @@ # Add source code. This also contains all of the logic for deciding linker flags # soname, etc... add_subdirectory(lib) +add_subdirectory(external) #=============================================================================== # Setup Tests Index: external/CMakeLists.txt =================================================================== --- /dev/null +++ external/CMakeLists.txt @@ -0,0 +1,32 @@ + +if (LIBCXX_ENABLE_BENCHMARKS) + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/Toolchain.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/Toolchain.cmake + @ONLY) + + include(ExternalProject) + + list(APPEND BENCHMARK_CMAKE_ARGS + -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR} + -DCMAKE_TOOLCHAIN_FILE:PATH=${CMAKE_CURRENT_BINARY_DIR}/Toolchain.cmake + -DCMAKE_BUILD_TYPE=RELEASE + -DBENCHMARK_ENABLE_TESTING:BOOL=OFF + -DBENCHMARK_ENABLE_SHARED:BOOL=ON + ) + + if (LIBCXX_BENCHMARK_SOURCE_PATH) + ExternalProject_Add( + Benchmark + SOURCE_DIR ${LIBCXX_BENCHMARK_SOURCE_PATH} + CMAKE_ARGS ${BENCHMARK_CMAKE_ARGS} + ) + else() + ExternalProject_Add( + Benchmark + SVN_REPOSITORY https://github.com/google/benchmark/branches/api-merge + CMAKE_ARGS ${BENCHMARK_CMAKE_ARGS} + ) + endif() + +endif() Index: external/Toolchain.cmake.in =================================================================== --- /dev/null +++ external/Toolchain.cmake.in @@ -0,0 +1,10 @@ + +set(CMAKE_CXX_COMPILER @CMAKE_CXX_COMPILER@) +set(CMAKE_C_COMPILER @CMAKE_C_COMPILER@) + +# Try to staticly link the C++ standard library so that we don't have libstdc++ +# and libc++ dynamically linked into our tests. +if (NOT APPLE AND NOT @CMAKE_SYSTEM_NAME@ STREQUAL "FreeBSD") + set(CMAKE_SHARED_LINKER_FLAGS "-static-libgcc -static-libstdc++" CACHE STRING "") + set(CMAKE_MODULE_LINKER_FLAGS "-static-libgcc -static-libstdc++" CACHE STRING "") +endif() Index: test/CMakeLists.txt =================================================================== --- test/CMakeLists.txt +++ test/CMakeLists.txt @@ -44,6 +44,7 @@ pythonize_bool(LIBCXX_BUILD_32_BITS) pythonize_bool(LIBCXX_ENABLE_THREADS) pythonize_bool(LIBCXX_ENABLE_MONOTONIC_CLOCK) + # The tests shouldn't link to any ABI library when it has been linked into # libc++ statically. if (LIBCXX_ENABLE_STATIC_ABI_LIBRARY) Index: test/benchmark/lit.cfg =================================================================== --- /dev/null +++ test/benchmark/lit.cfg @@ -0,0 +1,42 @@ +# -*- Python -*- vim: set ft=python ts=4 sw=4 expandtab tw=79: +# Configuration file for the 'lit' test runner. +import os +import site +import sys + +site.addsitedir(os.path.join(os.path.dirname(__file__), '..')) +import libcxx.test.config + +# Tell pylint that we know config and lit_config exist somewhere. +if 'PYLINT_IMPORT' in os.environ: + config = object() + lit_config = object() + +# name: The name of this test suite. +config.name = 'libc++-benchmark' + +config.suffixes = ['.bench.cpp'] + +# test_source_root: The root path where tests are located. +config.test_source_root = os.path.dirname(__file__) + +# Infer the test_exec_root from the libcxx_object root. +obj_root = getattr(config, 'libcxx_obj_root', None) + +# Check that the test exec root is known. +if obj_root is None: + import libcxx.test.config + libcxx.test.config.loadSiteConfig( + lit_config, config, 'libcxx_site_config', 'LIBCXX_SITE_CONFIG') + obj_root = getattr(config, 'libcxx_obj_root', None) + if obj_root is None: + import tempfile + obj_root = tempfile.mkdtemp(prefix='libcxx-benchmark-') + lit_config.warning('Creating temporary directory for object root: %s' % + obj_root) + +config.test_exec_root = os.path.join(obj_root, 'test') + +configuration = libcxx.test.config.BenchmarkConfiguration(lit_config, config) +configuration.configure() +config.test_format = configuration.get_test_format() Index: test/benchmark/test.bench.cpp =================================================================== --- /dev/null +++ test/benchmark/test.bench.cpp @@ -0,0 +1,8 @@ +#include "benchmark/minimal_benchmark.h" + +static void BM_test_empty(benchmark::State& state) { + while (state.KeepRunning()) {} +} +BENCHMARK(BM_test_empty); + +BENCHMARK_MAIN() Index: test/libcxx/test/benchmark.py =================================================================== --- /dev/null +++ test/libcxx/test/benchmark.py @@ -0,0 +1,205 @@ +import json +import re + +import lit +import lit.Test + + +def stringToCode(str_code): + if str_code == 'PASS': + return lit.Test.PASS + elif str_code == 'XFAIL': + return lit.Test.XFAIL + elif str_code == 'FAIL': + return lit.Test.FAIL + elif str_code == 'XPASS': + return lit.Test.XPASS + elif str_code == 'UNRESOLVED': + return lit.Test.UNRESOLVED + elif str_code == 'UNSUPPORTED': + return lit.Test.UNSUPPORTED + else: + assert False + + +def loadTestResults(from_file): + """ + Read in the output of a benchmark test run. + """ + with open(from_file, 'r') as output_file: + output = json.load(output_file) + raw_tests = output['tests'] + tests = {} + for rt in raw_tests: + test = { + 'name': rt['name'], + 'code': stringToCode(rt['code']), + 'output': rt['output'], + 'benchmarks': rt['metrics']['benchmarks'] + } + tests[rt['name']] = test + return tests + + +# Regex to parse a single line of a benchmarks output. The basic format is as +# follows: