From 798640f6290c49b51b5e2edf18f147ceb5bb8169 Mon Sep 17 00:00:00 2001 From: calixteman Date: Mon, 14 Jan 2019 10:28:16 +0100 Subject: [PATCH] Reader (#230) * Add a rust gcno/gcda reader * Forget to remove debug stuff * Chech for funtion execution only one time * Fix an error in cycle stuff * Sort the destination edges * Rethrow errors from write! * Plug the new reader and remove all the LLVM stuff * Remove useless stuff and improve error msg * Fix tests * Remove references to llvm-config * Don't remove an empty line * Change llvm@7 to llvm for osx build * Don't download llvm for OSX --- .travis.yml | 7 - Cargo.lock | 89 +- Cargo.toml | 7 +- appveyor.yml | 3 - build.rs | 96 -- src/c/GCOVOutput.hxx | 77 -- src/c/llvmgcov.cpp | 165 --- src/defs.rs | 18 +- src/lib.rs | 78 +- src/output.rs | 34 +- src/parser.rs | 317 +---- src/producer.rs | 115 +- src/reader.rs | 1191 +++++++++++++++++ test/llvm/file_branch.c | 33 + test/llvm/file_branch.gcda | Bin 0 -> 396 bytes test/llvm/file_branch.gcno | Bin 0 -> 1580 bytes test/llvm/reader.c | 30 + test/llvm/reader.c.0.gcov | 35 + test/llvm/reader.c.1.gcov | 35 + test/llvm/reader.c.2.gcov | 35 + test/llvm/reader.gcda | Bin 0 -> 324 bytes test/llvm/reader.gcno | Bin 0 -> 1408 bytes test/llvm/reader.gcno.0.dump | 77 ++ test/llvm/reader.gcno.1.dump | 77 ++ test/llvm/reader.gcno.2.dump | 77 ++ tests/basic/expected_llvm.coveralls | 2 +- tests/basic/expected_llvm_8.coveralls | 2 +- tests/basic_zip_dir/expected_llvm.coveralls | 2 +- tests/basic_zip_dir/expected_llvm_8.coveralls | 2 +- tests/basic_zip_zip/expected_llvm.coveralls | 2 +- tests/basic_zip_zip/expected_llvm_8.coveralls | 2 +- .../expected_two_gcda_llvm.coveralls | 2 +- .../expected_two_gcda_llvm_8.coveralls | 2 +- tests/class/expected_llvm_6.coveralls | 6 +- tests/class/expected_llvm_7_linux.coveralls | 2 +- tests/class/expected_llvm_8.coveralls | 8 +- tests/include/expected_llvm_6.coveralls | 2 +- tests/include/expected_llvm_7_linux.coveralls | 2 +- tests/include/expected_llvm_8.coveralls | 4 +- tests/include2/expected_llvm_6.coveralls | 4 +- .../include2/expected_llvm_7_linux.coveralls | 2 +- tests/include2/expected_llvm_8.coveralls | 4 +- tests/switch/expected_llvm.coveralls | 60 +- tests/switch/expected_llvm_8.coveralls | 2 +- tests/template/expected_llvm_6.coveralls | 6 +- .../template/expected_llvm_7_linux.coveralls | 2 +- tests/template/expected_llvm_8.coveralls | 8 +- tests/test.rs | 24 +- 48 files changed, 1879 insertions(+), 869 deletions(-) delete mode 100755 build.rs delete mode 100644 src/c/GCOVOutput.hxx delete mode 100755 src/c/llvmgcov.cpp create mode 100644 src/reader.rs create mode 100644 test/llvm/file_branch.c create mode 100644 test/llvm/file_branch.gcda create mode 100644 test/llvm/file_branch.gcno create mode 100644 test/llvm/reader.c create mode 100644 test/llvm/reader.c.0.gcov create mode 100644 test/llvm/reader.c.1.gcov create mode 100644 test/llvm/reader.c.2.gcov create mode 100644 test/llvm/reader.gcda create mode 100644 test/llvm/reader.gcno create mode 100644 test/llvm/reader.gcno.0.dump create mode 100644 test/llvm/reader.gcno.1.dump create mode 100644 test/llvm/reader.gcno.2.dump diff --git a/.travis.yml b/.travis.yml index 681a3d6..a4357f6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,6 @@ matrix: - GCC_CXX=g++-5 - GCOV=gcov-5 - CLANG_CXX=clang++-7 - - LLVM_CONFIG=llvm-config-7 - DEPLOY=0 - os: linux @@ -37,7 +36,6 @@ matrix: - GCC_CXX=g++-5 - GCOV=gcov-5 - CLANG_CXX=clang++-7 - - LLVM_CONFIG=llvm-config-7 - DEPLOY=0 - os: linux @@ -56,7 +54,6 @@ matrix: - GCC_CXX=g++-6 - GCOV=gcov-6 - CLANG_CXX=clang++-7 - - LLVM_CONFIG=llvm-config-7 - DEPLOY=0 - os: linux @@ -75,7 +72,6 @@ matrix: - GCC_CXX=g++-7 - GCOV=gcov-7 - CLANG_CXX=clang++-7 - - LLVM_CONFIG=llvm-config-7 - DEPLOY=1 - os: linux @@ -94,14 +90,12 @@ matrix: - GCC_CXX=g++-7 - GCOV=gcov-7 - CLANG_CXX=clang++-8 - - LLVM_CONFIG=llvm-config-8 - DEPLOY=0 - os: osx rust: nightly env: - CLANG_CXX=clang++ - - LLVM_CONFIG=llvm-config - DEPLOY=1 before_install: - | @@ -112,7 +106,6 @@ before_install: install: - if [ "$TRAVIS_OS_NAME" = "osx" ]; then sudo softwareupdate -i "Command Line Tools (macOS High Sierra version 10.13) for Xcode-9.4"; fi - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew cask uninstall --force oclint; fi - - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew install llvm@7 && brew link --overwrite --force llvm@7; fi script: - export CXX=$CLANG_CXX - $CXX --version diff --git a/Cargo.lock b/Cargo.lock index 2c85718..f5c6865 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,6 +19,11 @@ dependencies = [ "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "autocfg" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "bitflags" version = "1.0.4" @@ -140,7 +145,7 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crc32fast 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", "libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", "miniz_oxide_c_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -180,15 +185,14 @@ dependencies = [ name = "grcov" version = "0.3.2" dependencies = [ - "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "globset 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", "md5 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "uuid 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -208,7 +212,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.45" +version = "0.2.46" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -217,7 +221,7 @@ version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -250,7 +254,7 @@ version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -274,7 +278,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", "crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", "miniz_oxide 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -283,7 +287,7 @@ name = "msdos_time" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "time 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -297,7 +301,7 @@ name = "num_cpus" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -322,7 +326,7 @@ name = "parking_lot_core" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -346,36 +350,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand" -version = "0.6.1" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_os 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_xorshift 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand_chacha" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -407,6 +410,19 @@ dependencies = [ "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rand_os" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rand_pcg" version = "0.1.1" @@ -418,7 +434,15 @@ dependencies = [ [[package]] name = "rand_xorshift" -version = "0.1.0" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rdrand" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -530,8 +554,8 @@ version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.50 (registry+https://github.com/rust-lang/crates.io-index)", "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -547,10 +571,10 @@ dependencies = [ [[package]] name = "time" -version = "0.1.41" +version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.50 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -646,13 +670,14 @@ dependencies = [ "flate2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "msdos_time 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [metadata] "checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" "checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e" "checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" +"checksum autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e5f34df7a019573fb8bdc7e24a2bfebe51a2a1d6bfdbaeccedb3c41fc574727" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" "checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" "checksum cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4a8b715cb4597106ea87c7c84b2f1d452c7492033765df7f32651e66fcf749" @@ -674,7 +699,7 @@ dependencies = [ "checksum globset 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4743617a7464bbda3c8aec8558ff2f9429047e025771037df561d383337ff865" "checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" "checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1" -"checksum libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2857ec59fadc0773853c664d2d18e7198e83883e7060b63c924cb077bd5c74" +"checksum libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)" = "023a4cd09b2ff695f9734c1934145a315594b7986398496841c7031a5a1bbdbd" "checksum libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "2eb5e43362e38e2bca2fd5f5134c4d4564a23a5c28e9b95411652021a8675ebe" "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" @@ -692,14 +717,16 @@ dependencies = [ "checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c" "checksum podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "780fb4b6698bbf9cf2444ea5d22411cef2953f0824b98f33cf454ec5615645bd" "checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c" -"checksum rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae9d223d52ae411a33cf7e54ec6034ec165df296ccd23533d671a28252b6f66a" -"checksum rand_chacha 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "771b009e3a508cb67e8823dda454aaa5368c7bc1c16829fb77d3e980440dd34a" +"checksum rand 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3906503e80ac6cbcacb2c2973fa8e473f24d7e2747c8c92bb230c2441cad96b5" +"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" "checksum rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372" "checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db" "checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" "checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" +"checksum rand_os 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f46fbd5550acf75b0c2730f5dd1873751daf9beb8f11b44027778fae50d7feca" "checksum rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "086bd09a33c7044e56bb44d5bdde5a60e7f119a9e95b0775f545de759a32fe05" -"checksum rand_xorshift 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "effa3fcaa47e18db002bdde6060944b6d2f9cfd8db471c30e873448ad9187be3" +"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" "checksum redox_syscall 0.1.50 (registry+https://github.com/rust-lang/crates.io-index)" = "52ee9a534dc1301776eff45b4fa92d2c39b1d8c3d3357e6eb593e0d795506fc2" "checksum regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f" "checksum regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4e47a2ed29da7a9e1960e1639e7a982e6edc6d49be308a3b02daf511504a16d1" @@ -716,7 +743,7 @@ dependencies = [ "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "7e91405c14320e5c79b3d148e1c86f40749a36e490642202a31689cb1a3452b2" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" -"checksum time 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "847da467bf0db05882a9e2375934a8a55cffdc9db0d128af1518200260ba1f6c" +"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" "checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737" diff --git a/Cargo.toml b/Cargo.toml index 3ab0137..86d57a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,8 +10,6 @@ repository="https://github.com/mozilla/grcov" readme = "README.md" keywords=["coverage"] categories=["command-line-utilities", "development-tools", "development-tools::testing"] -links = "llvmgcov" -build = "build.rs" exclude = [ "test/*", "tests/*", @@ -26,9 +24,6 @@ appveyor = { repository = "marco-c/grcov" } travis-ci = { repository = "mozilla/grcov" } codecov = { repository = "mozilla/grcov" } -[build-dependencies] -cc = "^1.0" - [dependencies] crossbeam = "^0.4" serde_json = "^1.0" @@ -39,9 +34,9 @@ md5 = "^0.6" zip = { version = "^0.4", features = ["deflate-zlib"], default-features = false } tempfile = "^3" uuid = { version = "^0.7", features = ["v4"] } -libc = "^0.2" globset = "^0.4" xml-rs = "^0.8" +smallvec = "^0.6.7" [dev-dependencies] regex = "^1.0" diff --git a/appveyor.yml b/appveyor.yml index b6d35d8..0ee9002 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -34,8 +34,6 @@ install: - 7z x clang.tar.bz2 - 7z x clang.tar - set PATH=clang\bin;%PATH% - - llvm-config --version - - llvm-config --host-target - set TARGET=%PLATFORM_TARGET%-pc-windows-msvc - if NOT "%PLATFORM_TARGET%" == "x86_64" ( @@ -64,7 +62,6 @@ test_script: If ($env:CHANNEL -eq "nightly" -And $env:APPVEYOR_REPO_TAG -eq "false") { mkdir ccov_dir Get-ChildItem -Path *\grcov*.gc* -Recurse | Copy-Item -Destination ccov_dir - Get-ChildItem -Path *\llvmgcov.gc* -Recurse | Copy-Item -Destination ccov_dir .\target\debug\grcov ccov_dir -s . -t lcov --llvm --branch --ignore-not-existing --ignore-dir "C:*" | Out-File -Encoding "UTF8" -FilePath lcov.info (Get-Content lcov.info) | Foreach-Object {$_ -replace "\xEF\xBB\xBF", ""} | Set-Content lcov.info ((Get-Content lcov.info) -join "`n") + "`n" | Set-Content -NoNewline lcov.info diff --git a/build.rs b/build.rs deleted file mode 100755 index 99bf48a..0000000 --- a/build.rs +++ /dev/null @@ -1,96 +0,0 @@ -extern crate cc; - -use std::env; -use std::process::Command; - -fn llvm_config(args: &[&str]) -> String { - let llvm_config_path = match env::var("LLVM_CONFIG") { - Ok(v) => v, - Err(_e) => "llvm-config".to_string(), - }; - - Command::new(llvm_config_path) - .args(args) - .arg("--link-static") - .output() - .map(|output| String::from_utf8(output.stdout).expect("llvm-config output is not UTF-8")) - .expect("Error while running llvm-config") -} - -fn get_llvm_includedir() -> Vec { - llvm_config(&["--cflags"]) - .split(&[' ', '\n'][..]) - .filter(|word| word.starts_with("-I")) - .map(|word| &word[2..]) - .map(str::to_owned) - .collect::>() -} - -fn get_llvm_libs() -> Vec { - llvm_config(&["--libnames", "core", "profiledata"]) - .split(&[' ', '\n'][..]) - .filter(|s| !s.is_empty()) - .map(|lib| { - if !cfg!(target_env = "msvc") { - assert!(lib.starts_with("lib")); - assert!(lib.ends_with(".a")); - &lib[3..lib.len() - 2] - } else { - assert!(lib.ends_with(".lib")); - &lib[..lib.len() - 4] - } - }).map(str::to_owned) - .collect::>() -} - -fn get_llvm_system_libs() -> Vec { - llvm_config(&["--system-libs"]) - .split(&[' ', '\n'][..]) - .filter(|s| !s.is_empty()) - .map(|lib| { - if !cfg!(target_env = "msvc") { - assert!(lib.starts_with("-l")); - &lib[2..] - } else { - assert!(lib.ends_with(".lib")); - &lib[..lib.len() - 4] - } - }).map(str::to_owned) - .collect::>() -} - -fn get_llvm_libdir() -> String { - llvm_config(&["--libdir"]) -} - -fn main() { - let mut build = cc::Build::new(); - - if !cfg!(target_env = "msvc") { - build.flag("-Wno-unused-parameter"); - } - - build.file("src/c/llvmgcov.cpp"); - - for include in get_llvm_includedir() { - build.include(include); - } - - println!("cargo:rustc-link-search=native={}", get_llvm_libdir()); - for lib in get_llvm_libs() { - println!("cargo:rustc-link-lib=static={}", lib); - } - for lib in get_llvm_system_libs() { - println!("cargo:rustc-link-lib=dylib={}", lib); - } - - build.cpp(true); - - if !cfg!(target_env = "msvc") { - build.flag("-fno-builtin"); - build.flag("-fno-exceptions"); - build.flag("-std=c++11"); - } - - build.compile("libllvmgcov.a"); -} diff --git a/src/c/GCOVOutput.hxx b/src/c/GCOVOutput.hxx deleted file mode 100644 index d34108c..0000000 --- a/src/c/GCOVOutput.hxx +++ /dev/null @@ -1,77 +0,0 @@ -#ifndef GCOVOUTPUT_HXX -#define GCOVOUTPUT_HXX - -extern "C" void handleFileRust(void*, const char*, size_t); -extern "C" void handleFunctionRust(void*, uint32_t, uint64_t, const char*, size_t); -extern "C" void handleLcountRust(void*, uint32_t, uint64_t); -extern "C" void handleBranchRust(void*, uint32_t, uint8_t, uint8_t); - -namespace llvm { - - class GCOVOutputStream - { - raw_ostream & CovOS; - - public: - GCOVOutputStream(raw_ostream & _CovOS) : CovOS(_CovOS) { } - - void handleFile(StringRef filename) - { - CovOS << "file:" << filename << "\n"; - } - - void handleFunction(uint32_t index, uint64_t entrycount, StringRef funcname) - { - CovOS << "function:" << index << "," << entrycount << "," << funcname << "\n"; - } - - void handleLcount(uint32_t index, uint64_t linecount) - { - CovOS << "lcount:" << index << "," << linecount << "\n"; - } - - void handleBranch(uint32_t index, bool taken, bool exec) - { - CovOS << "branch:" << index << ","; - if (taken && exec) - CovOS << "taken"; - else if (exec) - CovOS << "nottaken"; - else - CovOS << "notexec"; - CovOS << "\n"; - } - }; - - class GCOVOutputRust - { - - void * RustHDL; - - public: - GCOVOutputRust(void * _RustHDL) : RustHDL(_RustHDL) { } - - void handleFile(StringRef filename) - { - handleFileRust(RustHDL, filename.data(), filename.size()); - } - - void handleFunction(uint32_t index, uint64_t entrycount, StringRef funcname) - { - handleFunctionRust(RustHDL, index, entrycount, funcname.data(), funcname.size()); - } - - void handleLcount(uint32_t index, uint64_t linecount) - { - handleLcountRust(RustHDL, index, linecount); - } - - void handleBranch(uint32_t index, bool taken, bool exec) - { - handleBranchRust(RustHDL, index, (uint8_t)taken, (uint8_t)exec); - } - }; - -} - -#endif // GCOVOUTPUT_HXX diff --git a/src/c/llvmgcov.cpp b/src/c/llvmgcov.cpp deleted file mode 100755 index d48d586..0000000 --- a/src/c/llvmgcov.cpp +++ /dev/null @@ -1,165 +0,0 @@ -#include "llvm/Support/Errc.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/Path.h" -#include "llvm/ProfileData/GCOV.h" -#include "GCOVOutput.hxx" - -using namespace llvm; - -class CustomFileInfo : public FileInfo { -public: - CustomFileInfo(const GCOV::Options &Options) : FileInfo(Options) {} - - void printIntermediate(StringRef WorkingDir, StringRef MainFilename); - - template - void printIntermediate(T &Output); -}; - -void CustomFileInfo::printIntermediate(StringRef WorkingDir, StringRef MainFilename) { - std::string CoveragePath = getCoveragePath(MainFilename, MainFilename); - SmallString<128> FullCoveragePath(WorkingDir); - sys::path::append(FullCoveragePath, CoveragePath); - std::unique_ptr CovOs = openCoveragePath(FullCoveragePath); - GCOVOutputStream Output(*CovOs.get()); - printIntermediate(Output); -} - -/// printIntermediate - Print source files with collected line count information in the intermediate gcov format. -template -void CustomFileInfo::printIntermediate(T &Output) { - SmallVector Filenames; - for (const auto &LI : LineInfo) - Filenames.push_back(LI.first()); - std::sort(Filenames.begin(), Filenames.end()); - - for (StringRef Filename : Filenames) { - Output.handleFile(Filename); - - const LineData &Line = LineInfo[Filename]; - for (uint32_t LineIndex = 0; LineIndex < Line.LastLine; ++LineIndex) { - FunctionLines::const_iterator FuncsIt = Line.Functions.find(LineIndex); - if (FuncsIt != Line.Functions.end()) { - for (const GCOVFunction *Func : FuncsIt->second) { - Output.handleFunction(LineIndex + 1, Func->getEntryCount(), Func->getName()); - } - } - - BlockLines::const_iterator BlocksIt = Line.Blocks.find(LineIndex); - if (BlocksIt == Line.Blocks.end()) { - // No basic blocks are on this line. Not an executable line of code. - continue; - } else { - const BlockVector &Blocks = BlocksIt->second; - - // Add up the block counts to form line counts. - DenseMap LineExecs; - uint64_t LineCount = 0; - for (const GCOVBlock *Block : Blocks) { - LineCount += Block->getCount(); - } - - Output.handleLcount(LineIndex + 1, LineCount); - - if (Options.BranchInfo) { - for (const GCOVBlock *Block : Blocks) { - // Only print block and branch information at the end of the block. - if (Block->getLastLine() != LineIndex + 1) - continue; - - size_t NumEdges = Block->getNumDstEdges(); - if (NumEdges > 1) { - uint64_t TotalCounts = 0; - for (const GCOVEdge *Edge : Block->dsts()) { - TotalCounts += Edge->Count; - } - bool exec = TotalCounts > 0; - for (const GCOVEdge *Edge : Block->dsts()) { - bool taken = Edge->Count > 0; - Output.handleBranch(LineIndex + 1, taken, exec); - } - } - } - } - } - } - } -} - -void parse_llvm_gcno_mbuf(void* RustHdl, char* working_dir, char* file_stem, MemoryBuffer* GCNO_Buff, MemoryBuffer* GCDA_Buff, uint8_t branch_enabled) { - GCOV::Options Options( - /* AllBlocks */ false, - /* BranchProb (BranchInfo) */ branch_enabled != 0, - /* BranchCount */ branch_enabled != 0, - /* FuncSummary (FuncCoverage) */ false, - /* PreservePaths */ false, - /* UncondBranch */ false, - /* LongNames */ false, - /* NoOutput */ false - ); - - GCOVFile GF; - std::string GCNO = std::string(file_stem) + ".gcno"; - - GCOVBuffer GCNO_GB(GCNO_Buff); - if (!GF.readGCNO(GCNO_GB)) { - errs() << "Invalid .gcno File!\n"; - return; - } - - if (GCDA_Buff->getBufferSize() != 0) { - GCOVBuffer GCDA_GB(GCDA_Buff); - if (!GF.readGCDA(GCDA_GB)) { - errs() << "Invalid .gcda File!\n"; - return; - } - } - - CustomFileInfo FI(Options); - GF.collectLineCounts(FI); - if (RustHdl) { - GCOVOutputRust Output(RustHdl); - FI.printIntermediate(Output); - } else { - FI.printIntermediate(working_dir, GCNO); - } -} - -extern "C" -void parse_llvm_gcno(void* RustHdl, char* working_dir, char* file_stem, uint8_t branch_enabled) { - std::string GCNO = std::string(file_stem) + ".gcno"; - std::string GCDA = std::string(file_stem) + ".gcda"; - std::unique_ptr gcno_buf; - std::unique_ptr gcda_buf; - - ErrorOr> GCNO_Buff = MemoryBuffer::getFileOrSTDIN(GCNO); - if (std::error_code EC = GCNO_Buff.getError()) { - errs() << GCNO << ": " << EC.message() << "\n"; - return; - } - - gcno_buf = std::move(GCNO_Buff.get()); - - ErrorOr> GCDA_Buff = MemoryBuffer::getFileOrSTDIN(GCDA); - if (std::error_code EC = GCDA_Buff.getError()) { - if (EC != errc::no_such_file_or_directory) { - errs() << GCDA << ": " << EC.message() << "\n"; - return; - } - // Clear the filename to make it clear we didn't read anything. - GCDA = "-"; - gcda_buf = MemoryBuffer::getMemBuffer(StringRef("")); - } else { - gcda_buf = std::move(GCDA_Buff.get()); - } - - parse_llvm_gcno_mbuf(RustHdl, working_dir, file_stem, gcno_buf.get(), gcda_buf.get(), branch_enabled); -} - -extern "C" -void parse_llvm_gcno_buf(void* RustHdl, char* working_dir, char* file_stem, char* gcno_buf, size_t gcno_buf_len, char* gcda_buf, size_t gcda_buf_len, uint8_t branch_enabled) { - std::unique_ptr GCNO_Buff = MemoryBuffer::getMemBuffer(StringRef(gcno_buf, gcno_buf_len)); - std::unique_ptr GCDA_Buff = MemoryBuffer::getMemBuffer(StringRef(gcda_buf, gcda_buf_len)); - - parse_llvm_gcno_mbuf(RustHdl, working_dir, file_stem, GCNO_Buff.get(), GCDA_Buff.get(), branch_enabled); -} diff --git a/src/defs.rs b/src/defs.rs index 549fe34..de8d143 100755 --- a/src/defs.rs +++ b/src/defs.rs @@ -1,8 +1,7 @@ use crossbeam::queue::MsQueue; -use libc; use std::collections::{BTreeMap, HashMap}; use std::path::PathBuf; -use std::sync::{Arc, Mutex}; +use std::sync::Mutex; #[derive(Debug, Clone, PartialEq)] pub struct Function { @@ -13,19 +12,10 @@ pub struct Function { #[derive(Debug, Clone, PartialEq)] pub struct CovResult { pub lines: BTreeMap, - pub branches: BTreeMap<(u32, u32), bool>, + pub branches: BTreeMap>, pub functions: HashMap, } -#[derive(Debug)] -#[repr(C)] -pub struct GCOVResult { - pub ptr: *mut libc::c_void, - pub len: libc::size_t, - pub capacity: libc::size_t, - pub branch_number: libc::uint32_t, -} - #[derive(Debug, PartialEq, Copy, Clone)] #[allow(non_camel_case_types)] pub enum ItemFormat { @@ -37,8 +27,8 @@ pub enum ItemFormat { #[derive(Debug)] pub struct GcnoBuffers { pub stem: String, - pub gcno_buf: Arc>, - pub gcda_buf: Vec, + pub gcno_buf: Vec, + pub gcda_buf: Vec>, } #[derive(Debug)] diff --git a/src/lib.rs b/src/lib.rs index c03b20e..6d37d9e 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,8 +2,8 @@ extern crate serde_json; extern crate crossbeam; extern crate globset; -extern crate libc; extern crate semver; +extern crate smallvec; extern crate tempfile; extern crate uuid; extern crate walkdir; @@ -31,6 +31,9 @@ pub use path_rewriting::*; mod output; pub use output::*; +mod reader; +pub use reader::*; + use std::collections::{btree_map, hash_map}; use std::fs; use std::io::{BufReader, Cursor}; @@ -38,7 +41,7 @@ use std::path::PathBuf; use walkdir::WalkDir; // Merge results, without caring about duplicate lines (they will be removed at the end). -fn merge_results(result: &mut CovResult, result2: &mut CovResult) { +fn merge_results(result: &mut CovResult, result2: CovResult) { for (&line_no, &execution_count) in &result2.lines { match result.lines.entry(line_no) { btree_map::Entry::Occupied(c) => { @@ -50,10 +53,17 @@ fn merge_results(result: &mut CovResult, result2: &mut CovResult) { }; } - for (&(line_no, number), &taken) in &result2.branches { - match result.branches.entry((line_no, number)) { + for (line_no, taken) in result2.branches { + match result.branches.entry(line_no) { btree_map::Entry::Occupied(c) => { - *c.into_mut() |= taken; + let v = c.into_mut(); + for (x, y) in taken.iter().zip(v.iter_mut()) { + *y |= x; + } + let l = v.len(); + if taken.len() > l { + v.extend(&taken[l..]); + } } btree_map::Entry::Vacant(v) => { v.insert(taken); @@ -61,7 +71,7 @@ fn merge_results(result: &mut CovResult, result2: &mut CovResult) { }; } - for (name, function) in result2.functions.drain() { + for (name, function) in result2.functions { match result.functions.entry(name) { hash_map::Entry::Occupied(f) => f.into_mut().executed |= function.executed, hash_map::Entry::Vacant(v) => { @@ -90,7 +100,7 @@ fn add_results( }; match map.entry(path) { hash_map::Entry::Occupied(obj) => { - merge_results(obj.into_mut(), &mut result.1); + merge_results(obj.into_mut(), result.1); } hash_map::Entry::Vacant(v) => { v.insert(result.1); @@ -173,19 +183,19 @@ pub fn consumer( new_results } } - ItemType::Buffers(buffers) => { + ItemType::Buffers(mut buffers) => { // LLVM - let result = call_parse_llvm_gcno_buf( - working_dir.to_str().unwrap(), - &buffers.stem, - &buffers.gcno_buf, - &buffers.gcda_buf, - branch_enabled, - ); - - drop(buffers.gcda_buf); - drop(buffers.gcno_buf); - result + match GCNO::compute(&buffers.stem, + buffers.gcno_buf, + buffers.gcda_buf, + branch_enabled) { + Ok(r) => r, + Err(e) => { + // Just print the error, don't panic and continue + eprintln!("Error in computing counters:\n{}", e); + Vec::new() + } + } } ItemType::Content(_) => { panic!("Invalid content type"); @@ -237,11 +247,9 @@ mod tests { let mut result = CovResult { lines: [(1, 21), (2, 7), (7, 0)].iter().cloned().collect(), branches: [ - ((1, 0), false), - ((1, 1), false), - ((2, 0), false), - ((2, 1), true), - ((4, 0), true), + (1, vec![false, false]), + (2, vec![false, true]), + (4, vec![true]), ] .iter() .cloned() @@ -263,17 +271,15 @@ mod tests { executed: true, }, ); - let mut result2 = CovResult { + let result2 = CovResult { lines: [(1, 21), (3, 42), (4, 7), (2, 0), (8, 0)] .iter() .cloned() .collect(), branches: [ - ((1, 0), false), - ((1, 1), false), - ((2, 0), true), - ((2, 1), false), - ((3, 0), true), + (1, vec![false, false]), + (2, vec![false, true]), + (3, vec![true]), ] .iter() .cloned() @@ -281,7 +287,7 @@ mod tests { functions: functions2, }; - merge_results(&mut result, &mut result2); + merge_results(&mut result, result2); assert_eq!( result.lines, [(1, 42), (2, 7), (3, 42), (4, 7), (7, 0), (8, 0)] @@ -292,12 +298,10 @@ mod tests { assert_eq!( result.branches, [ - ((1, 0), false), - ((1, 1), false), - ((2, 0), true), - ((2, 1), true), - ((3, 0), true), - ((4, 0), true) + (1, vec![false, false]), + (2, vec![false, true]), + (3, vec![true]), + (4, vec![true]), ] .iter() .cloned() diff --git a/src/output.rs b/src/output.rs index 8783421..40f5448 100755 --- a/src/output.rs +++ b/src/output.rs @@ -152,16 +152,18 @@ pub fn output_lcov(results: CovResultIter) { // branch coverage information let mut branch_hit = 0; - for (&(line, number), &taken) in &result.branches { - writeln!( - writer, - "BRDA:{},0,{},{}", - line, - number, - if taken { "1" } else { "-" } - ).unwrap(); - if taken { - branch_hit += 1; + for (line, ref taken) in &result.branches { + for (n, b_t) in taken.iter().enumerate() { + writeln!( + writer, + "BRDA:{},0,{},{}", + line, + n, + if *b_t { "1" } else { "-" } + ).unwrap(); + if *b_t { + branch_hit += 1; + } } } @@ -217,11 +219,13 @@ pub fn output_coveralls( } let mut branches = Vec::new(); - for (&(line, number), &taken) in &result.branches { - branches.push(line); - branches.push(0); - branches.push(number); - branches.push(if taken { 1 } else { 0 }); + for (line, ref taken) in &result.branches { + for (n, b_t) in taken.iter().enumerate() { + branches.push(*line); + branches.push(0); + branches.push(n as u32); + branches.push(if *b_t { 1 } else { 0 }); + } } if !with_function_info { diff --git a/src/parser.rs b/src/parser.rs index 054d572..dd556ee 100755 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,18 +1,14 @@ use std::collections::{btree_map, hash_map, BTreeMap, HashMap}; -use std::ffi::CString; use std::fmt; use std::fs::File; use std::io::{self, BufRead, BufReader, Read}; -use std::mem; use std::num::ParseIntError; use std::path::Path; -use std::{slice, str}; +use std::str; use xml::attribute::OwnedAttribute; use xml::reader::{EventReader, XmlEvent}; -use libc; - use defs::*; #[derive(Debug)] @@ -68,202 +64,6 @@ macro_rules! try_parse_next { }; } -#[link(name = "llvmgcov", kind = "static")] -extern "C" { - fn parse_llvm_gcno( - hdl: *const GCOVResult, - working_dir: *const libc::c_char, - file_stem: *const libc::c_char, - branch_enabled: libc::uint8_t, - ); - fn parse_llvm_gcno_buf( - hdl: *const GCOVResult, - working_dir: *const libc::c_char, - file_stem: *const libc::c_char, - gcno_buf: *const libc::c_char, - gcno_buf_len: libc::size_t, - gcda_buf: *const libc::c_char, - gcda_buf_len: libc::size_t, - branch_enabled: libc::uint8_t, - ); -} - -pub fn call_parse_llvm_gcno( - working_dir: &str, - file_stem: &str, - branch_enabled: bool, -) -> Vec<(String, CovResult)> { - let working_dir_c = CString::new(working_dir).unwrap(); - let file_stem_c = CString::new(file_stem).unwrap(); - let branch_enabled = if branch_enabled { 1 as u8 } else { 0 as u8 }; - - let mut result: Vec<(String, CovResult)> = Vec::new(); - let hdl = GCOVResult { - ptr: result.as_mut_ptr() as *mut libc::c_void, - len: result.len(), - capacity: result.capacity(), - branch_number: 0, - }; - - let res = unsafe { - parse_llvm_gcno( - &hdl, - working_dir_c.as_ptr(), - file_stem_c.as_ptr(), - branch_enabled, - ); - Vec::from_raw_parts(hdl.ptr as *mut (String, CovResult), hdl.len, hdl.capacity) - }; - - if hdl.ptr != result.as_mut_ptr() as *mut libc::c_void { - drop(result); - } - res -} - -pub fn call_parse_llvm_gcno_buf( - working_dir: &str, - file_stem: &str, - gcno: &[u8], - gcda: &[u8], - branch_enabled: bool, -) -> Vec<(String, CovResult)> { - let working_dir_c = CString::new(working_dir).unwrap(); - let file_stem_c = CString::new(file_stem).unwrap(); - let gcno_buf_len = gcno.len(); - let gcda_buf_len = gcda.len(); - let branch_enabled = if branch_enabled { 1 as u8 } else { 0 as u8 }; - - let mut result: Vec<(String, CovResult)> = Vec::new(); - let hdl = GCOVResult { - ptr: result.as_mut_ptr() as *mut libc::c_void, - len: result.len(), - capacity: result.capacity(), - branch_number: 0, - }; - - let res = unsafe { - let gcno_buf = gcno.as_ptr() as *const libc::c_char; - let gcda_buf = gcda.as_ptr() as *const libc::c_char; - - parse_llvm_gcno_buf( - &hdl, - working_dir_c.as_ptr(), - file_stem_c.as_ptr(), - gcno_buf, - gcno_buf_len, - gcda_buf, - gcda_buf_len, - branch_enabled, - ); - Vec::from_raw_parts(hdl.ptr as *mut (String, CovResult), hdl.len, hdl.capacity) - }; - - if hdl.ptr != result.as_mut_ptr() as *mut libc::c_void { - drop(result); - } - res -} - -#[no_mangle] -pub unsafe extern "C" fn handleFileRust( - hdl: *mut GCOVResult, - filename: *const libc::c_char, - len: libc::size_t, -) { - let res = &mut *hdl; - let filename = str::from_utf8_unchecked(slice::from_raw_parts(filename as *const u8, len)); - - let mut results = - Vec::from_raw_parts(res.ptr as *mut (String, CovResult), res.len, res.capacity); - results.push(( - filename.to_string(), - CovResult { - lines: BTreeMap::new(), - branches: BTreeMap::new(), - functions: HashMap::new(), - }, - )); - - res.ptr = results.as_mut_ptr() as *mut libc::c_void; - res.len = results.len(); - res.capacity = results.capacity(); - - mem::forget(results); -} - -#[no_mangle] -pub unsafe extern "C" fn handleFunctionRust( - hdl: *mut GCOVResult, - index: libc::uint32_t, - entrycount: libc::uint64_t, - funcname: *const libc::c_char, - len: libc::size_t, -) { - let res = &mut *hdl; - let funcname = str::from_utf8_unchecked(slice::from_raw_parts(funcname as *const u8, len)); - let mut results = - Vec::from_raw_parts(res.ptr as *mut (String, CovResult), res.len, res.capacity); - match results.last_mut() { - Some(r) => { - r.1.functions.insert( - funcname.to_string(), - Function { - start: index, - executed: entrycount != 0, - }, - ); - } - None => { - panic!("Results should not be emtpy"); - } - } - mem::forget(results); -} - -#[no_mangle] -pub unsafe extern "C" fn handleLcountRust( - hdl: *mut GCOVResult, - index: libc::uint32_t, - linecount: libc::uint64_t, -) { - let res = &mut *hdl; - let mut results = - Vec::from_raw_parts(res.ptr as *mut (String, CovResult), res.len, res.capacity); - match results.last_mut() { - Some(r) => { - res.branch_number = 0; - r.1.lines.insert(index, linecount); - } - None => { - panic!("Results should not be emtpy"); - } - } - mem::forget(results); -} - -#[no_mangle] -pub unsafe extern "C" fn handleBranchRust( - hdl: *mut GCOVResult, - index: libc::uint32_t, - taken: libc::uint8_t, - _exec: libc::uint8_t, -) { - let res = &mut *hdl; - let mut results = - Vec::from_raw_parts(res.ptr as *mut (String, CovResult), res.len, res.capacity); - match results.last_mut() { - Some(r) => { - r.1.branches.insert((index, res.branch_number), taken != 0); - res.branch_number += 1; - } - None => { - panic!("Results should not be emtpy"); - } - } - mem::forget(results); -} - fn remove_newline(l: &mut Vec) { loop { let last = { @@ -282,6 +82,27 @@ fn remove_newline(l: &mut Vec) { } } +pub fn add_branch(branches: &mut BTreeMap>, line_no: u32, no: u32, taken: bool) { + match branches.entry(line_no) { + btree_map::Entry::Occupied(c) => { + let mut v = c.into_mut(); + let l = v.len(); + let no = no as usize; + if no == l { + v.push(taken); + } else if no > l { + v.extend(vec![false; no - l]); + v.push(taken) + } else { + v[no] |= taken; + } + } + btree_map::Entry::Vacant(v) => { + v.insert(vec![taken; 1]); + } + }; +} + pub fn parse_lcov( mut lcov_reader: BufReader, branch_enabled: bool, @@ -384,14 +205,7 @@ pub fn parse_lcov( values.next(); let branch_number = try_parse_next!(values, l); let taken = try_next!(values, l) != "-"; - match cur_branches.entry((line_no, branch_number)) { - btree_map::Entry::Occupied(c) => { - *c.into_mut() |= taken; - } - btree_map::Entry::Vacant(v) => { - v.insert(taken); - } - }; + add_branch(&mut cur_branches, line_no, branch_number, taken); } } _ => {} @@ -407,8 +221,6 @@ pub fn parse_gcov(gcov_path: &Path) -> Result, ParserEr let mut cur_lines = BTreeMap::new(); let mut cur_branches = BTreeMap::new(); let mut cur_functions = HashMap::new(); - let mut branch_number = 0; - let mut results = Vec::new(); let f = File::open(&gcov_path) @@ -464,8 +276,6 @@ pub fn parse_gcov(gcov_path: &Path) -> Result, ParserEr ); } "lcount" => { - branch_number = 0; - let mut values = value.splitn(2, ','); let line_no = try_parse_next!(values, l); let execution_count = try_next!(values, l); @@ -479,8 +289,15 @@ pub fn parse_gcov(gcov_path: &Path) -> Result, ParserEr let mut values = value.splitn(2, ','); let line_no = try_parse_next!(values, l); let taken = try_next!(values, l) == "taken"; - cur_branches.insert((line_no, branch_number), taken); - branch_number += 1; + match cur_branches.entry(line_no) { + btree_map::Entry::Occupied(c) => { + let mut v = c.into_mut(); + v.push(taken); + } + btree_map::Entry::Vacant(p) => { + p.insert(vec![taken; 1]); + } + } } _ => {} } @@ -514,9 +331,9 @@ fn get_xml_attribute(attributes: &[OwnedAttribute], name: &str) -> Result( parser: &mut EventReader, -) -> Result<(BTreeMap, BTreeMap<(u32, u32), bool>), ParserError> { +) -> Result<(BTreeMap, BTreeMap>), ParserError> { let mut lines: BTreeMap = BTreeMap::new(); - let mut branches: BTreeMap<(u32, u32), bool> = BTreeMap::new(); + let mut branches: BTreeMap> = BTreeMap::new(); loop { match parser.next() { @@ -534,13 +351,9 @@ fn parse_jacoco_report_sourcefile( if mb > 0 || cb > 0 { // This line is a branch. - for branch in 0..cb { - branches.insert((nr, branch as u32), true); - } - - for branch in cb..cb + mb { - branches.insert((nr, branch as u32), false); - } + let mut v = vec![true; cb as usize]; + v.extend(vec![false; mb as usize]); + branches.insert(nr, v); } else { // This line is a statement. // JaCoCo does not feature execution counts, so we set the @@ -932,18 +745,12 @@ mod tests { assert_eq!( result.branches, [ - ((34, 0), false), - ((34, 1), false), - ((41, 0), false), - ((41, 1), false), - ((44, 0), false), - ((44, 1), false), - ((60, 0), false), - ((60, 1), false), - ((63, 0), false), - ((63, 1), false), - ((68, 0), true), - ((68, 1), true) + (34, vec![false, false]), + (41, vec![false, false]), + (44, vec![false, false]), + (60, vec![false, false]), + (63, vec![false, false]), + (68, vec![true, true]) ] .iter() .cloned() @@ -1653,10 +1460,8 @@ mod tests { assert_eq!( result.branches, [ - ((399, 0), false), - ((399, 1), false), - ((401, 0), true), - ((401, 1), false) + (399, vec![false, false]), + (401, vec![true, false]) ] .iter() .cloned() @@ -1712,31 +1517,6 @@ mod tests { assert_eq!(func.executed, true); } - #[test] - fn test_parser_gcov_with_no_tmp() { - let mut lines: BTreeMap = BTreeMap::new(); - lines.insert(2, 1); - let mut functions: HashMap = HashMap::new(); - functions.insert( - String::from("main"), - Function { - start: 1, - executed: true, - }, - ); - let branches: BTreeMap<(u32, u32), bool> = BTreeMap::new(); - let expected = vec![( - String::from("file.c"), - CovResult { - lines: lines, - branches: branches, - functions: functions, - }, - )]; - let result = call_parse_llvm_gcno("test/llvm", "test/llvm/file", true); - assert_eq!(result, expected); - } - #[test] fn test_parser_jacoco_xml_basic() { let mut lines: BTreeMap = BTreeMap::new(); @@ -1758,9 +1538,8 @@ mod tests { start: 3, }, ); - let mut branches: BTreeMap<(u32, u32), bool> = BTreeMap::new(); - branches.insert((3, 0), true); - branches.insert((3, 1), true); + let mut branches: BTreeMap> = BTreeMap::new(); + branches.insert(3, vec![true, true]); let expected = vec![( String::from("hello.java"), CovResult { @@ -1807,7 +1586,7 @@ mod tests { ] { functions.insert(String::from(name), Function { executed, start }); } - let branches: BTreeMap<(u32, u32), bool> = BTreeMap::new(); + let branches: BTreeMap> = BTreeMap::new(); let expected = vec![( String::from("org/gradle/Person.java"), CovResult { diff --git a/src/producer.rs b/src/producer.rs index ad594f6..58249f2 100755 --- a/src/producer.rs +++ b/src/producer.rs @@ -4,10 +4,9 @@ use std::cell::RefCell; use std::collections::HashMap; use std::env; use std::fs::{self, File}; -use std::io::{self, Read}; +use std::io::{self, BufReader, Read}; use std::os; use std::path::{Path, PathBuf}; -use std::sync::Arc; use walkdir::WalkDir; use zip::ZipArchive; @@ -15,7 +14,7 @@ use defs::*; #[derive(Debug)] pub enum ArchiveType { - Zip(RefCell>), + Zip(RefCell>>), Dir(PathBuf), Plain(Vec), } @@ -314,60 +313,48 @@ fn gcno_gcda_producer( let gcno_archive = *gcno_archive; let gcno = format!("{}.gcno", stem).to_string(); let physical_gcno_path = tmp_dir.join(format!("{}_{}.gcno", stem, 1)); - let mut gcno_buf_opt: Option>> = if gcno_stem.llvm { - let mut buffer: Vec = Vec::new(); - gcno_archive.read_in_buffer(&gcno, &mut buffer); - Some(Arc::new(buffer)) + if gcno_stem.llvm { + let mut gcno_buffer: Vec = Vec::new(); + let mut gcda_buffers: Vec> = Vec::with_capacity(gcda_archives.len()); + gcno_archive.read_in_buffer(&gcno, &mut gcno_buffer); + for gcda_archive in gcda_archives { + let mut gcda_buf: Vec = Vec::new(); + let gcda = format!("{}.gcda", stem).to_string(); + if gcda_archive.read_in_buffer(&gcda, &mut gcda_buf) { + gcda_buffers.push(gcda_buf); + } + } + push_to_queue( + ItemType::Buffers(GcnoBuffers { + stem: stem.clone(), + gcno_buf: gcno_buffer, + gcda_buf: gcda_buffers, + }), + "".to_string(), + ); } else { gcno_archive.extract(&gcno, &physical_gcno_path); - None - }; + for (num, &gcda_archive) in gcda_archives.iter().enumerate() { + let gcno_path = tmp_dir.join(format!("{}_{}.gcno", stem, num + 1)); + let gcda = format!("{}.gcda", stem).to_string(); - for (num, &gcda_archive) in gcda_archives.iter().enumerate() { - let gcno_path = tmp_dir.join(format!("{}_{}.gcno", stem, num + 1)); - let gcda = format!("{}.gcda", stem).to_string(); - - match gcno_buf_opt { - Some(ref gcno_buf) => { - let mut gcda_buf: Vec = Vec::new(); - let gcno_stem = tmp_dir.join(format!("{}_{}", stem, num + 1)); - let gcno_stem = gcno_stem - .to_str() - .expect("Failed to create stem file string"); - - if gcda_archive.read_in_buffer(&gcda, &mut gcda_buf) - || (num == 0 && !ignore_orphan_gcno) - { - push_to_queue( - ItemType::Buffers(GcnoBuffers { - stem: gcno_stem.to_string(), - gcno_buf: Arc::clone(gcno_buf), - gcda_buf: gcda_buf, - }), - gcda_archive.get_name().to_string(), - ); - } + // Create symlinks. + if num != 0 { + fs::hard_link(&physical_gcno_path, &gcno_path).unwrap_or_else( + |_| panic!("Failed to create hardlink {:?}", gcno_path), + ); } - None => { - // Create symlinks. - if num != 0 { - fs::hard_link(&physical_gcno_path, &gcno_path).unwrap_or_else( - |_| panic!("Failed to create hardlink {:?}", gcno_path), - ); - } - let gcda_path = tmp_dir.join(format!("{}_{}.gcda", stem, num + 1)); - - if gcda_archive.extract(&gcda, &gcda_path) - || (num == 0 && !ignore_orphan_gcno) - { - push_to_queue( - ItemType::Path(gcno_path), - gcda_archive.get_name().to_string(), - ); - } + let gcda_path = tmp_dir.join(format!("{}_{}.gcda", stem, num + 1)); + if gcda_archive.extract(&gcda, &gcda_path) + || (num == 0 && !ignore_orphan_gcno) + { + push_to_queue( + ItemType::Path(gcno_path), + gcda_archive.get_name().to_string(), + ); } - }; + } } } None => { @@ -380,8 +367,8 @@ fn gcno_gcda_producer( push_to_queue( ItemType::Buffers(GcnoBuffers { - stem: gcno, - gcno_buf: Arc::new(buffer), + stem: stem.clone(), + gcno_buf: buffer, gcda_buf: Vec::new(), }), gcno_archive.get_name().to_string(), @@ -430,9 +417,10 @@ pub fn get_mapping(linked_files_maps: &HashMap) -> Option ZipArchive { +fn open_archive(path: &str) -> ZipArchive> { let file = File::open(&path).unwrap_or_else(|_| panic!("Failed to open ZIP file '{}'.", path)); - ZipArchive::new(file).unwrap_or_else(|_| panic!("Failed to parse ZIP file: {}", path)) + let reader = BufReader::new(file); + ZipArchive::new(reader).unwrap_or_else(|_| panic!("Failed to parse ZIP file: {}", path)) } pub fn producer( @@ -663,7 +651,7 @@ mod tests { ( ItemFormat::GCNO, false, - "rust/generics_with_two_parameters_1", + "rust/generics_with_two_parameters", true, ), (ItemFormat::INFO, false, "1494603973-2977-7.info", false), @@ -677,7 +665,9 @@ mod tests { "relative_path/relative_path.info", false, ), - (ItemFormat::GCNO, false, "llvm/file_1", true), + (ItemFormat::GCNO, false, "llvm/file", true), + (ItemFormat::GCNO, false, "llvm/file_branch", true), + (ItemFormat::GCNO, false, "llvm/reader", true), ( ItemFormat::JACOCO_XML, false, @@ -1410,7 +1400,7 @@ mod tests { true, true, ); - let gcno_buf: Arc> = Arc::new(vec![ + let gcno_buf: Vec = vec![ 111, 110, 99, 103, 42, 50, 48, 52, 74, 200, 254, 66, 0, 0, 0, 1, 9, 0, 0, 0, 0, 0, 0, 0, 236, 217, 93, 255, 2, 0, 0, 0, 109, 97, 105, 110, 0, 0, 0, 0, 2, 0, 0, 0, 102, 105, 108, 101, 46, 99, 0, 0, 1, 0, 0, 0, 0, 0, 65, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -1419,7 +1409,7 @@ mod tests { 0, 0, 0, 0, 0, 0, 0, 0, 69, 1, 8, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 102, 105, 108, 101, 46, 99, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ]); + ]; let gcda1_buf: Vec = vec![ 97, 100, 99, 103, 42, 50, 48, 52, 74, 200, 254, 66, 0, 0, 0, 1, 5, 0, 0, 0, 0, 0, 0, 0, 236, 217, 93, 255, 2, 0, 0, 0, 109, 97, 105, 110, 0, 0, 0, 0, 0, 0, 161, 1, 4, 0, 0, 0, @@ -1444,12 +1434,9 @@ mod tests { if let ItemType::Buffers(buffers) = elem.item { let stem = PathBuf::from(buffers.stem); let stem = stem.file_stem().expect("Unable to get file_stem"); - if stem == "file_1" { + if stem == "file" { assert_eq!(buffers.gcno_buf, gcno_buf); - assert_eq!(buffers.gcda_buf, gcda1_buf); - } else if stem == "file_2" { - assert_eq!(buffers.gcno_buf, gcno_buf); - assert_eq!(buffers.gcda_buf, gcda2_buf); + assert_eq!(buffers.gcda_buf, vec![gcda1_buf.clone(), gcda2_buf.clone()]); } else { assert!(false, "Unexpected file: {:?}", stem); } diff --git a/src/reader.rs b/src/reader.rs new file mode 100644 index 0000000..f673853 --- /dev/null +++ b/src/reader.rs @@ -0,0 +1,1191 @@ +use smallvec::SmallVec; +use std::cmp; +use std::collections::{btree_map, hash_map, BTreeMap, HashMap}; +use std::convert::From; +use std::fmt::{Debug, Display, Formatter}; +use std::fs::File; +use std::io::{BufReader, Error, Read, Write}; +use std::path::PathBuf; +use std::result::Result; + +use defs::{CovResult, Function}; + +#[derive(Default)] +pub struct GCNO { + version: u32, + checksum: u32, + runcounts: u32, + functions: Vec, +} + +#[derive(Debug)] +struct GcovFunction { + identifier: u32, + line_number: u32, + line_checksum: u32, + file_name: String, + name: String, + blocks: SmallVec<[GcovBlock; 16]>, + edges: SmallVec<[GcovEdge; 16]>, + lines: HashMap, + executed: bool, +} + +#[derive(Debug)] +struct GcovBlock { + no: usize, + source: SmallVec<[usize; 4]>, + destination: SmallVec<[usize; 4]>, + lines: SmallVec<[u32; 16]>, + line_max: u32, + counter: u64, +} + +#[derive(Debug)] +struct GcovEdge { + source: usize, + destination: usize, + counter: u64, + cycles: u64, +} + +#[derive(Debug)] +pub enum GcovError { + Io(std::io::Error), + Str(String), +} + +impl From for GcovError { + fn from(err: Error) -> GcovError { + GcovError::Str(format!("Reader error: {}", err)) + } +} + +pub trait GcovReader { + fn read_string(&mut self) -> Result; + fn read_u32(&mut self) -> Result; + fn read_u64(&mut self) -> Result; + fn read_counter(&mut self) -> Result; + fn read_version(&mut self) -> Result; + fn check_type(&mut self, typ: [u8; 4]) -> Result<(), GcovError>; + fn get_pos(&self) -> usize; + fn get_stem(&self) -> &str; + fn skip_u32(&mut self) -> Result<(), GcovError>; + fn skip_u64(&mut self) -> Result<(), GcovError>; +} + +pub struct GcovReaderBuf { + stem: String, + buffer: Vec, + pos: usize, +} + +impl From<&str> for GcovReaderBuf { + fn from(path: &str) -> GcovReaderBuf { + let path = PathBuf::from(path); + let mut f = File::open(&path).unwrap(); + let mut buf = Vec::new(); + f.read_to_end(&mut buf).unwrap(); + GcovReaderBuf::new(path.to_str().unwrap(), buf) + } +} + +macro_rules! read_u_le { + ($ty: ty, $buf: expr) => {{ + let size = std::mem::size_of::<$ty>(); + let start = $buf.pos; + $buf.pos += size; + if $buf.pos <= $buf.buffer.len() { + Ok(unsafe { + *std::mem::transmute::<*const u8, *const $ty>($buf.buffer[start..].as_ptr()) + } + .to_le()) + } else { + Err(GcovError::Str(format!( + "Not enough data in buffer: cannot read integer in {}", + $buf.get_stem() + ))) + } + }}; +} + +macro_rules! skip { + ($ty: ty, $buf: expr) => {{ + let size = std::mem::size_of::<$ty>(); + $buf.pos += size; + if $buf.pos < $buf.buffer.len() { + Ok(()) + } else { + Err(GcovError::Str(format!( + "Not enough data in buffer: cannot skip {} bytes in {}", + size, + $buf.get_stem() + ))) + } + }}; +} + +impl GcovReaderBuf { + pub fn new(stem: &str, buffer: Vec) -> GcovReaderBuf { + GcovReaderBuf { + stem: stem.to_string(), + buffer, + pos: 0, + } + } +} + +impl GcovReader for GcovReaderBuf { + fn get_stem(&self) -> &str { + &self.stem + } + + #[inline(always)] + fn skip_u32(&mut self) -> Result<(), GcovError> { + skip!(u32, self) + } + + #[inline(always)] + fn skip_u64(&mut self) -> Result<(), GcovError> { + skip!(u64, self) + } + + fn read_string(&mut self) -> Result { + let mut len = 0; + while len == 0 { + len = read_u_le!(u32, self)?; + } + let len = len as usize * 4; + let start = self.pos; + self.pos += len; + if self.pos <= self.buffer.len() { + let bytes = &self.buffer[start..self.pos]; + let i = len - bytes.iter().rev().position(|&x| x != 0).unwrap(); + Ok(unsafe { std::str::from_utf8_unchecked(&bytes[..i]).to_string() }) + } else { + Err(GcovError::Str(format!( + "Not enough data in buffer: cannot read string in {}", + self.get_stem() + ))) + } + } + + #[inline(always)] + fn read_u32(&mut self) -> Result { + read_u_le!(u32, self) + } + + #[inline(always)] + fn read_u64(&mut self) -> Result { + read_u_le!(u64, self) + } + + #[inline(always)] + fn read_counter(&mut self) -> Result { + let lo = read_u_le!(u32, self)?; + let hi = read_u_le!(u32, self)?; + + Ok(u64::from(hi) << 32 | u64::from(lo)) + } + + fn read_version(&mut self) -> Result { + let i = self.pos; + if i + 4 <= self.buffer.len() { + self.pos += 4; + if self.buffer[i] == b'*' { + let zero = u32::from('0'); + let zero = zero + 10 * (zero + 10 * zero); + Ok(u32::from(self.buffer[i + 1]) + + 10 * (u32::from(self.buffer[i + 2]) + 10 * u32::from(self.buffer[i + 3])) + - zero) + } else { + let bytes = &self.buffer[i..i + 4]; + Err(GcovError::Str(format!( + "Unexpected version: {} in {}", + std::str::from_utf8(&bytes).unwrap(), + self.get_stem() + ))) + } + } else { + Err(GcovError::Str(format!( + "Not enough data in buffer: Cannot read version in {}", + self.get_stem() + ))) + } + } + + fn check_type(&mut self, typ: [u8; 4]) -> Result<(), GcovError> { + let i = self.pos; + if i + 4 <= self.buffer.len() { + self.pos += 4; + if self.buffer[i] == typ[0] + && self.buffer[i + 1] == typ[1] + && self.buffer[i + 2] == typ[2] + && self.buffer[i + 3] == typ[3] + { + Ok(()) + } else { + let bytes = &self.buffer[i..i + 4]; + Err(GcovError::Str(format!( + "Unexpected file type: {} in {}.", + std::str::from_utf8(&bytes).unwrap(), + self.get_stem() + ))) + } + } else { + Err(GcovError::Str(format!( + "Not enough data in buffer: Cannot compare types in {}", + self.get_stem() + ))) + } + } + + #[inline(always)] + fn get_pos(&self) -> usize { + self.pos + } +} + +impl Display for GcovError { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + match self { + GcovError::Io(e) => write!(f, "{}", e), + GcovError::Str(e) => write!(f, "{}", e), + } + } +} + +impl Debug for GCNO { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + for fun in &self.functions { + writeln!( + f, + "===== {} ({}) @ {}:{}", + fun.name, fun.identifier, fun.file_name, fun.line_number + )?; + for block in &fun.blocks { + writeln!(f, "Block : {} Counter : {}", block.no, block.counter)?; + if let Some((last, elmts)) = block.source.split_last() { + write!(f, "\tSource Edges : ")?; + for edge in elmts.iter().map(|i| &fun.edges[*i]) { + write!(f, "{} ({}), ", edge.source, edge.counter)?; + } + let edge = &fun.edges[*last]; + writeln!(f, "{} ({}), ", edge.source, edge.counter)?; + } + if let Some((last, elmts)) = block.destination.split_last() { + write!(f, "\tDestination Edges : ")?; + for edge in elmts.iter().map(|i| &fun.edges[*i]) { + write!(f, "{} ({}), ", edge.destination, edge.counter)?; + } + let edge = &fun.edges[*last]; + writeln!(f, "{} ({}), ", edge.destination, edge.counter)?; + } + if let Some((last, elmts)) = block.lines.split_last() { + write!(f, "\tLines : ")?; + for i in elmts { + write!(f, "{},", i)?; + } + writeln!(f, "{},", last)?; + } + } + } + + Ok(()) + } +} + +impl GCNO { + pub fn new() -> Self { + GCNO { + version: 0, + checksum: 0, + runcounts: 0, + functions: Vec::new(), + } + } + + #[inline] + pub fn compute( + stem: &str, + gcno_buf: Vec, + mut gcda_bufs: Vec>, + branch_enabled: bool, + ) -> Result, GcovError> { + let mut gcno = GCNO::new(); + gcno.read(GcovReaderBuf::new(stem, gcno_buf))?; + for gcda_buf in gcda_bufs.drain(..) { + gcno.read_gcda(GcovReaderBuf::new(stem, gcda_buf))?; + } + Ok(gcno.finalize(branch_enabled)) + } + + pub fn read(&mut self, mut reader: T) -> Result<(), GcovError> { + reader.check_type([b'o', b'n', b'c', b'g'])?; + self.version = reader.read_version()?; + self.checksum = reader.read_u32()?; + self.read_functions(&mut reader) + } + + fn read_edges(fun: &mut GcovFunction, reader: &mut GcovReader) -> Result { + let mut tag = reader.read_u32()?; + let edges = &mut fun.edges; + let blocks = &mut fun.blocks; + let mut edges_count = 0; + while tag == 0x0143_0000 { + let count = reader.read_u32()?; + let count = ((count - 1) / 2) as usize; + let block_no = reader.read_u32()? as usize; + if block_no <= blocks.len() { + edges.reserve(count); + blocks[block_no].destination.reserve(count); + for _ in 0..count { + let dst_block_no = reader.read_u32()? as usize; + let _flags = reader.read_u32()?; + edges.push(GcovEdge { + source: block_no, + destination: dst_block_no, + counter: 0, + cycles: 0, + }); + let i = match blocks[block_no] + .destination + .binary_search_by(|x| edges[*x].destination.cmp(&dst_block_no)) + { + Ok(i) => i, + Err(i) => i, + }; + blocks[block_no].destination.insert(i, edges_count); + blocks[dst_block_no].source.push(edges_count); + edges_count += 1; + } + tag = reader.read_u32()?; + } else { + return Err(GcovError::Str(format!( + "Unexpected block number: {} (in {}) in {}", + block_no, + fun.name, + reader.get_stem() + ))); + } + } + Ok(tag) + } + + fn read_lines( + fun: &mut GcovFunction, + reader: &mut GcovReader, + tag: u32, + ) -> Result { + let mut tag = tag; + while tag == 0x0145_0000 { + let len = reader.read_u32()? - 3; + let block_no = reader.read_u32()? as usize; + if block_no <= fun.blocks.len() { + if len > 0 { + // Read the word that pads the beginning of the line table. This may be a + // flag of some sort, but seems to always be zero. + let _dummy = reader.skip_u32()?; + + let file_name = reader.read_string()?; + let len = len - 2 - ((file_name.len() as u32 / 4) + 1); + + if file_name != fun.file_name { + return Err(GcovError::Str(format!( + "Multiple sources for a single basic block: {} != {} (in {}) in {}", + fun.file_name, + file_name, + fun.name, + reader.get_stem() + ))); + } + let block = &mut fun.blocks[block_no]; + let lines = &mut block.lines; + lines.reserve(len as usize); + for _ in 0..len { + let line = reader.read_u32()?; + if line != 0 { + lines.push(line); + if line > block.line_max { + block.line_max = line; + } + } + } + } + // Just read 2 zeros + let _dummy = reader.skip_u64()?; + tag = reader.read_u32()?; + } else { + return Err(GcovError::Str(format!( + "Unexpected block number: {} (in {}).", + block_no, fun.name + ))); + } + } + Ok(tag) + } + + fn read_functions(&mut self, reader: &mut GcovReader) -> Result<(), GcovError> { + let mut tag = reader.read_u32()?; + while tag == 0x0100_0000 { + let _dummy = reader.skip_u32()?; + let identifier = reader.read_u32()?; + let line_checksum = reader.read_u32()?; + if self.version != 402 { + let cfg_sum = reader.read_u32()?; + if cfg_sum != self.checksum { + let fn_name = reader.read_string()?; + return Err(GcovError::Str(format!( + "File checksums do not match: {} != {} (in {}) in {}", + self.checksum, + cfg_sum, + fn_name, + reader.get_stem() + ))); + } + } + + let name = reader.read_string()?; + let file_name = reader.read_string()?; + let line_number = reader.read_u32()?; + let block_tag = reader.read_u32()?; + + if block_tag == 0x0141_0000 { + let count = reader.read_u32()? as usize; + let mut blocks: SmallVec<[GcovBlock; 16]> = SmallVec::with_capacity(count); + for no in 0..count { + let _flags = reader.skip_u32()?; + blocks.push(GcovBlock { + no, + source: SmallVec::new(), + destination: SmallVec::new(), + lines: SmallVec::new(), + line_max: 0, + counter: 0, + }); + } + let mut fun = GcovFunction { + identifier, + line_number, + line_checksum, + file_name, + name, + blocks, + edges: SmallVec::new(), + lines: HashMap::new(), + executed: false, + }; + tag = GCNO::read_edges(&mut fun, reader)?; + tag = GCNO::read_lines(&mut fun, reader, tag)?; + self.functions.push(fun); + } else { + return Err(GcovError::Str(format!( + "Invalid function tag: {} in {}", + tag, + reader.get_stem() + ))); + } + } + Ok(()) + } + + pub fn read_gcda(&mut self, mut reader: T) -> Result<(), GcovError> { + reader.check_type([b'a', b'd', b'c', b'g'])?; + let version = reader.read_version()?; + if version != self.version { + Err(GcovError::Str(format!( + "GCOV versions do not match in {}", + reader.get_stem() + ))) + } else { + let checksum = reader.read_u32()?; + if checksum != self.checksum { + Err(GcovError::Str(format!( + "File checksums do not match: {} != {} in {}", + self.checksum, + checksum, + reader.get_stem() + ))) + } else { + for mut fun in &mut self.functions { + GCNO::read_gcda_function(self.version, self.checksum, &mut fun, &mut reader)?; + } + let object_tag = reader.read_u32()?; + if object_tag == 0xa100_0000 { + reader.skip_u32()?; + reader.skip_u64()?; + self.runcounts += reader.read_u32()?; + } + Ok(()) + } + } + } + + fn read_gcda_function( + version: u32, + checksum: u32, + fun: &mut GcovFunction, + reader: &mut GcovReader, + ) -> Result<(), GcovError> { + let tag = reader.read_u32()?; + if tag != 0x0100_0000 { + return Err(GcovError::Str(format!( + "Unexpected number of functions in {}", + reader.get_stem() + ))); + } + + let header_len = reader.read_u32()?; + let end_pos = reader.get_pos() + (header_len as usize) * 4; + let id = reader.read_u32()?; + if id != fun.identifier { + return Err(GcovError::Str(format!( + "Function identifiers do not match: {} != {} (in {}) in {}", + fun.identifier, + id, + fun.name, + reader.get_stem() + ))); + } + + let _chk_sum = reader.skip_u32()?; + if version != 402 { + let cfg_sum = reader.read_u32()?; + if cfg_sum != checksum { + return Err(GcovError::Str(format!( + "File checksums do not match: {} != {} (in {}) in {}", + checksum, + cfg_sum, + fun.name, + reader.get_stem() + ))); + } + } + + if reader.get_pos() < end_pos { + let fun_name = reader.read_string()?; + if fun.name != fun_name { + return Err(GcovError::Str(format!( + "Function names do not match: {} != {} in {}", + fun.name, + fun_name, + reader.get_stem() + ))); + } + } + + let arc_tag = reader.read_u32()?; + if arc_tag != 0x01a1_0000 { + return Err(GcovError::Str(format!( + "Arc tag not found (in {}) in {}", + fun.name, + reader.get_stem() + ))); + } + + let count = reader.read_u32()?; + let edges = &mut fun.edges; + if edges.len() as u32 != count / 2 { + return Err(GcovError::Str(format!( + "Unexpected number of edges (in {}) in {}", + fun.name, + reader.get_stem() + ))); + } + + if let Some((first, elmts)) = edges.split_first_mut() { + // The first edge is between entry block and a block + // so the entry block as the same counter as the + // edge counter. + let counter = reader.read_counter()?; + first.counter += counter; + fun.blocks[first.destination].counter += counter; + fun.blocks[first.source].counter += counter; + + for edge in elmts { + let counter = reader.read_counter()?; + edge.counter += counter; + fun.blocks[edge.destination].counter += counter; + } + } + Ok(()) + } + + fn collect_lines(&self) -> HashMap<&str, HashMap> { + let mut results: HashMap<&str, HashMap> = HashMap::new(); + for function in &self.functions { + let mut lines = match results.entry(&function.file_name) { + hash_map::Entry::Occupied(l) => l.into_mut(), + hash_map::Entry::Vacant(p) => p.insert(HashMap::new()), + }; + + for (line, counter) in &function.lines { + match lines.entry(*line) { + hash_map::Entry::Occupied(c) => { + *c.into_mut() += *counter; + } + hash_map::Entry::Vacant(p) => { + p.insert(*counter); + } + } + } + } + results + } + + fn dump( + &mut self, + path: &PathBuf, + file_name: &str, + writer: &mut Write, + ) -> Result<(), GcovError> { + let file = File::open(path)?; + let mut reader = BufReader::new(file); + let mut source = String::new(); + + for fun in &mut self.functions { + fun.add_line_count(); + } + + let counters = self.collect_lines(); + let counters = &counters[file_name]; + reader.read_to_string(&mut source)?; + let stem = PathBuf::from(file_name); + let stem = stem.file_stem().unwrap().to_str().unwrap(); + let mut n: u32 = 0; + let has_runs = self.runcounts != 0; + + writeln!(writer, "{:>9}:{:>5}:Source:{}", "-", 0, file_name)?; + writeln!(writer, "{:>9}:{:>5}:Graph:{}.gcno", "-", 0, stem)?; + if has_runs { + writeln!(writer, "{:>9}:{:>5}:Data:{}.gcda", "-", 0, stem)?; + } else { + writeln!(writer, "{:>9}:{:>5}:Data:-", "-", 0)?; + } + writeln!(writer, "{:>9}:{:>5}:Runs:{}", "-", 0, self.runcounts)?; + writeln!( + writer, + "{:>9}:{:>5}:Programs:{}", + "-", + 0, + if has_runs { 1 } else { 0 } + )?; + let mut iter = source.split('\n').peekable(); + while let Some(line) = iter.next() { + if iter.peek().is_none() && line.is_empty() { + // We're on the last line and it's empty + break; + } + n += 1; + if let Some(counter) = counters.get(&n) { + if *counter == 0 { + writeln!(writer, "{:>9}:{:>5}:{}", "#####", n, line)?; + } else { + writeln!(writer, "{:>9}:{:>5}:{}", *counter, n, line)?; + } + } else { + writeln!(writer, "{:>9}:{:>5}:{}", "-", n, line)?; + } + } + + Ok(()) + } + + pub fn finalize(&mut self, branch_enabled: bool) -> Vec<(String, CovResult)> { + let mut results: HashMap<&str, CovResult> = HashMap::new(); + for fun in &mut self.functions { + fun.add_line_count(); + let mut res = match results.entry(&fun.file_name) { + hash_map::Entry::Occupied(r) => r.into_mut(), + hash_map::Entry::Vacant(p) => p.insert(CovResult { + lines: BTreeMap::new(), + branches: BTreeMap::new(), + functions: HashMap::new(), + }), + }; + res.functions.insert( + fun.name.clone(), + Function { + start: fun.line_number, + executed: fun.executed, + }, + ); + if fun.executed { + for (line, counter) in fun.lines.iter() { + match res.lines.entry(*line) { + btree_map::Entry::Occupied(c) => { + *c.into_mut() += *counter; + } + btree_map::Entry::Vacant(p) => { + p.insert(*counter); + } + } + } + } else { + for line in fun.lines.keys() { + res.lines.entry(*line).or_insert(0); + } + } + if branch_enabled { + if fun.executed { + for block in &fun.blocks { + if block.destination.len() > 1 { + let line = block.line_max; + let taken = block + .destination + .iter() + .map(|no| fun.edges[*no].counter > 0); + match res.branches.entry(line) { + btree_map::Entry::Occupied(c) => { + let mut v = c.into_mut(); + v.extend(taken); + } + btree_map::Entry::Vacant(p) => { + p.insert(taken.collect()); + } + } + } + } + } else { + for block in &fun.blocks { + let n_dest = block.destination.len(); + if n_dest > 1 { + let taken = vec![false; n_dest]; + match res.branches.entry(block.line_max) { + btree_map::Entry::Occupied(c) => { + let mut v = c.into_mut(); + v.extend(taken); + } + btree_map::Entry::Vacant(p) => { + p.insert(taken); + } + } + } + } + } + } + } + let mut r = Vec::with_capacity(results.len()); + for (k, v) in results.drain() { + r.push((k.to_string(), v)); + } + r + } +} + +impl GcovFunction { + fn get_cycle_count(edges: &mut [GcovEdge], path: &[usize]) -> u64 { + let mut count = std::u64::MAX; + for e in path.iter() { + count = cmp::min(count, edges[*e].cycles); + } + for e in path { + edges[*e].cycles -= count; + } + count + } + + fn unblock( + block: usize, + blocked: &mut SmallVec<[usize; 4]>, + block_lists: &mut SmallVec<[SmallVec<[usize; 2]>; 2]>, + ) { + if let Some(i) = blocked.iter().position(|x| *x == block) { + blocked.remove(i); + for b in block_lists.remove(i) { + GcovFunction::unblock(b, blocked, block_lists); + } + } + } + + fn look_for_circuit( + fun_edges: &mut [GcovEdge], + fun_blocks: &[GcovBlock], + v: usize, + start: usize, + path: &mut SmallVec<[usize; 4]>, + blocked: &mut SmallVec<[usize; 4]>, + block_lists: &mut SmallVec<[SmallVec<[usize; 2]>; 2]>, + blocks: &[usize], + ) -> (bool, u64) { + let mut count = 0; + blocked.push(v); + block_lists.push(SmallVec::new()); + let mut found = false; + let dsts = &fun_blocks[v].destination; + + for e in dsts { + let w = fun_edges[*e].destination; + if w >= start && blocks.iter().any(|x| *x == w) { + path.push(*e); + if w == start { + count += GcovFunction::get_cycle_count(fun_edges, path); + found = true; + } else if blocked.iter().all(|x| *x != w) { + let (f, c) = GcovFunction::look_for_circuit( + fun_edges, + fun_blocks, + w, + start, + path, + blocked, + block_lists, + blocks, + ); + count += c; + if f { + found = true; + } + } + path.pop(); + } + } + + if found { + GcovFunction::unblock(v, blocked, block_lists); + } else { + for e in dsts { + let w = fun_edges[*e].destination; + if w >= start || blocks.iter().any(|x| *x == w) { + if let Some(i) = blocked.iter().position(|x| *x == w) { + let list = &mut block_lists[i]; + if list.iter().all(|x| *x != v) { + list.push(v); + } + } + } + } + } + + (found, count) + } + + fn get_cycles_count( + fun_edges: &mut [GcovEdge], + fun_blocks: &[GcovBlock], + blocks: &[usize], + ) -> u64 { + let mut count: u64 = 0; + let mut path: SmallVec<[usize; 4]> = SmallVec::new(); + let mut blocked: SmallVec<[usize; 4]> = SmallVec::new(); + let mut block_lists: SmallVec<[SmallVec<[usize; 2]>; 2]> = SmallVec::new(); + for b in blocks { + path.clear(); + blocked.clear(); + block_lists.clear(); + let (_, c) = GcovFunction::look_for_circuit( + fun_edges, + fun_blocks, + *b, + *b, + &mut path, + &mut blocked, + &mut block_lists, + blocks, + ); + count += c; + } + count + } + + fn get_line_count( + fun_edges: &mut [GcovEdge], + fun_blocks: &[GcovBlock], + blocks: &[usize], + ) -> u64 { + let mut count: u64 = 0; + for b in blocks { + let block = &fun_blocks[*b]; + if block.source.is_empty() { + count += block.counter; + } else { + for e in &block.source { + let e = &fun_edges[*e]; + let w = e.source; + if !blocks.iter().any(|x| *x == w) { + count += e.counter; + } + } + } + for e in &block.destination { + let e = &mut fun_edges[*e]; + e.cycles = e.counter; + } + } + + count + GcovFunction::get_cycles_count(fun_edges, fun_blocks, blocks) + } + + fn add_line_count(&mut self) { + self.executed = self.edges.first().unwrap().counter > 0; + if self.executed { + let mut lines_to_block: HashMap> = HashMap::new(); + for block in &self.blocks { + let n = block.no; + for line in &block.lines { + match lines_to_block.entry(*line) { + hash_map::Entry::Occupied(vec) => { + vec.into_mut().push(n); + } + hash_map::Entry::Vacant(v) => { + v.insert(vec![n]); + } + } + } + } + self.lines.reserve(lines_to_block.len()); + for (line, blocks) in lines_to_block { + let count = if blocks.len() == 1 { + self.blocks[blocks[0]].counter + } else { + GcovFunction::get_line_count(&mut self.edges, &self.blocks, &blocks) + }; + self.lines.insert(line, count); + } + } else { + for block in &self.blocks { + for line in &block.lines { + self.lines.entry(*line).or_insert(0); + } + } + } + } +} + +#[cfg(test)] +mod tests { + + use super::*; + + fn get_input_string(path: &str) -> String { + let path = PathBuf::from(path); + let mut f = File::open(path).unwrap(); + let mut input = String::new(); + f.read_to_string(&mut input).unwrap(); + input + } + + fn get_input_vec(path: &str) -> Vec { + let path = PathBuf::from(path); + let mut f = File::open(path).unwrap(); + let mut input = Vec::new(); + f.read_to_end(&mut input).unwrap(); + input + } + + #[test] + fn test_reader_gcno() { + let mut gcno = GCNO::new(); + gcno.read(GcovReaderBuf::from("test/llvm/reader.gcno")) + .unwrap(); + let output = format!("{:?}", gcno); + let input = get_input_string("test/llvm/reader.gcno.0.dump"); + + assert!(input == output); + } + + #[test] + fn test_reader_gcno_gcda() { + let mut gcno = GCNO::new(); + gcno.read(GcovReaderBuf::from("test/llvm/reader.gcno")) + .unwrap(); + gcno.read_gcda(GcovReaderBuf::from("test/llvm/reader.gcda")) + .unwrap(); + let output = format!("{:?}", gcno); + let input = get_input_string("test/llvm/reader.gcno.1.dump"); + + assert!(input == output); + } + + #[test] + fn test_reader_gcno_gcda_gcda() { + let mut gcno = GCNO::new(); + gcno.read(GcovReaderBuf::from("test/llvm/reader.gcno")) + .unwrap(); + for _ in 0..2 { + gcno.read_gcda(GcovReaderBuf::from("test/llvm/reader.gcda")) + .unwrap(); + } + let output = format!("{:?}", gcno); + let input = get_input_string("test/llvm/reader.gcno.2.dump"); + + assert!(input == output); + } + + #[test] + fn test_reader_gcno_counter() { + let mut gcno = GCNO::new(); + gcno.read(GcovReaderBuf::from("test/llvm/reader.gcno")) + .unwrap(); + let mut output = Vec::new(); + gcno.dump( + &PathBuf::from("test/llvm/reader.c"), + "reader.c", + &mut output, + ) + .unwrap(); + let input = get_input_vec("test/llvm/reader.c.0.gcov"); + + assert!(input == output); + } + + #[test] + fn test_reader_gcno_gcda_counter() { + let mut gcno = GCNO::new(); + gcno.read(GcovReaderBuf::from("test/llvm/reader.gcno")) + .unwrap(); + gcno.read_gcda(GcovReaderBuf::from("test/llvm/reader.gcda")) + .unwrap(); + let mut output = Vec::new(); + gcno.dump( + &PathBuf::from("test/llvm/reader.c"), + "reader.c", + &mut output, + ) + .unwrap(); + let input = get_input_vec("test/llvm/reader.c.1.gcov"); + + assert!(input == output); + } + + #[test] + fn test_reader_gcno_gcda_gcda_counter() { + let mut gcno = GCNO::new(); + gcno.read(GcovReaderBuf::from("test/llvm/reader.gcno")) + .unwrap(); + for _ in 0..2 { + gcno.read_gcda(GcovReaderBuf::from("test/llvm/reader.gcda")) + .unwrap(); + } + let mut output = Vec::new(); + gcno.dump( + &PathBuf::from("test/llvm/reader.c"), + "reader.c", + &mut output, + ) + .unwrap(); + let input = get_input_vec("test/llvm/reader.c.2.gcov"); + + assert!(input == output); + } + + #[test] + fn test_reader_finalize_file() { + let mut gcno = GCNO::new(); + gcno.read(GcovReaderBuf::from("test/llvm/file.gcno")) + .unwrap(); + gcno.read_gcda(GcovReaderBuf::from("test/llvm/file.gcda")) + .unwrap(); + let result = gcno.finalize(true); + + let mut lines: BTreeMap = BTreeMap::new(); + lines.insert(2, 1); + let mut functions: HashMap = HashMap::new(); + functions.insert( + String::from("main"), + Function { + start: 1, + executed: true, + }, + ); + let branches: BTreeMap> = BTreeMap::new(); + let expected = vec![( + String::from("file.c"), + CovResult { + lines: lines, + branches: branches, + functions: functions, + }, + )]; + + assert_eq!(result, expected); + } + + #[test] + fn test_reader_finalize_file_branch() { + let mut gcno = GCNO::new(); + gcno.read(GcovReaderBuf::from("test/llvm/file_branch.gcno")) + .unwrap(); + gcno.read_gcda(GcovReaderBuf::from("test/llvm/file_branch.gcda")) + .unwrap(); + let result = gcno.finalize(true); + + let mut lines: BTreeMap = BTreeMap::new(); + [ + (2, 2), + (3, 1), + (4, 1), + (5, 1), + (6, 1), + (8, 1), + (10, 2), + (13, 1), + (14, 0), + (16, 1), + (18, 1), + (21, 0), + (22, 0), + (24, 0), + (25, 0), + (26, 0), + (28, 0), + (32, 1), + ] + .iter() + .for_each(|x| { + lines.insert(x.0, x.1); + }); + + let mut functions: HashMap = HashMap::new(); + functions.insert( + String::from("foo"), + Function { + start: 1, + executed: true, + }, + ); + functions.insert( + String::from("bar"), + Function { + start: 12, + executed: true, + }, + ); + functions.insert( + String::from("oof"), + Function { + start: 20, + executed: false, + }, + ); + functions.insert( + String::from("main"), + Function { + start: 31, + executed: true, + }, + ); + let mut branches: BTreeMap> = BTreeMap::new(); + [ + (2, vec![true, true]), + (3, vec![true, false]), + (13, vec![false, true]), + (21, vec![false, false, false, false]), + ] + .iter() + .for_each(|x| { + branches.insert(x.0, x.1.clone()); + }); + + let expected = vec![( + String::from("file_branch.c"), + CovResult { + lines: lines, + branches: branches, + functions: functions, + }, + )]; + + assert_eq!(result, expected); + } +} diff --git a/test/llvm/file_branch.c b/test/llvm/file_branch.c new file mode 100644 index 0000000..aa08083 --- /dev/null +++ b/test/llvm/file_branch.c @@ -0,0 +1,33 @@ +int foo(int x) { + if (x > 0 && + x <= 3) { + int y = x + 1; + y += 1; + return y; + } else { + return 1; + } +} + +int bar(int x) { + if (x > 0) { + return 0; + } else { + return 1; + } +} + +int oof(int x) { + if (x > 0 && x <= 3) { + return 0; + } else { + int y = x + 1; + y += 1; + return y; + } +} + + +int main() { + return foo(-1) + foo(2) + bar(-1); +} diff --git a/test/llvm/file_branch.gcda b/test/llvm/file_branch.gcda new file mode 100644 index 0000000000000000000000000000000000000000..f148e02c15c553c3aa1e3414ae7324898da48f41 GIT binary patch literal 396 zcmYdHNlw=?GBA1Zw4;oHfq{_)NPs}*?=DV8Af1+<4 literal 0 HcmV?d00001 diff --git a/test/llvm/file_branch.gcno b/test/llvm/file_branch.gcno new file mode 100644 index 0000000000000000000000000000000000000000..73f526d9e6f32dae8ec9b172f418b5af05274eb4 GIT binary patch literal 1580 zcmds1OG?B*5bgH=M?^(M(2W}x;&0viR2 zlBNhUOB+(1s(O`vRn?WU$Zif#kI%n8pC6oaenA9Q{{1rVWgnEKo5G&oU0?NwX_4I? zWun99+@+rldzVQ3WaZWvD2bmI34H>pES#ZBeXujG>4Oaq;&tiwfr)L<`UPE+(%pysodbhvd)@QS0xqC8d!MtwhE|o z|9QN4MGgAt&^!nH(d#z9U~GuL^_g|R9bm1PBW*s{Ltl~u1515f@I ze$~J;4-3E6!t3mT&&SExJNi+U1G69NA@2Xr-bSlFdcV8+W#PN}wHbXM_q?lLTjJlA zXD5ooT=8yNeco;1UHFH9*TLIUf_(1Nya2?C-Gf7Bk9h<&2z%auYyMZqot6F3LG{}& D+z@}` literal 0 HcmV?d00001 diff --git a/test/llvm/reader.c b/test/llvm/reader.c new file mode 100644 index 0000000..891c0db --- /dev/null +++ b/test/llvm/reader.c @@ -0,0 +1,30 @@ +int foo(int x, int n) +{ + int i; + for (i = 0; i < n; ++i) { + if (x > n) + { + x += i; + } + else + { + int j; + for (j = 0; j < i; ++j) { + x += j; + } + } + } + return x; +} + +int main() +{ + int x = foo(10, 10); + int y = 0; + int i; + for (i = 0; i < x; ++i) + { + y += foo(x, 10); + } + return y; +} diff --git a/test/llvm/reader.c.0.gcov b/test/llvm/reader.c.0.gcov new file mode 100644 index 0000000..4808859 --- /dev/null +++ b/test/llvm/reader.c.0.gcov @@ -0,0 +1,35 @@ + -: 0:Source:reader.c + -: 0:Graph:reader.gcno + -: 0:Data:- + -: 0:Runs:0 + -: 0:Programs:0 + -: 1:int foo(int x, int n) + -: 2:{ + -: 3: int i; + #####: 4: for (i = 0; i < n; ++i) { + #####: 5: if (x > n) + -: 6: { + #####: 7: x += i; + #####: 8: } + -: 9: else + -: 10: { + -: 11: int j; + #####: 12: for (j = 0; j < i; ++j) { + #####: 13: x += j; + #####: 14: } + -: 15: } + #####: 16: } + #####: 17: return x; + -: 18:} + -: 19: + -: 20:int main() + -: 21:{ + #####: 22: int x = foo(10, 10); + #####: 23: int y = 0; + -: 24: int i; + #####: 25: for (i = 0; i < x; ++i) + -: 26: { + #####: 27: y += foo(x, 10); + #####: 28: } + #####: 29: return y; + -: 30:} diff --git a/test/llvm/reader.c.1.gcov b/test/llvm/reader.c.1.gcov new file mode 100644 index 0000000..7667e29 --- /dev/null +++ b/test/llvm/reader.c.1.gcov @@ -0,0 +1,35 @@ + -: 0:Source:reader.c + -: 0:Graph:reader.gcno + -: 0:Data:reader.gcda + -: 0:Runs:1 + -: 0:Programs:1 + -: 1:int foo(int x, int n) + -: 2:{ + -: 3: int i; + 594: 4: for (i = 0; i < n; ++i) { + 540: 5: if (x > n) + -: 6: { + 537: 7: x += i; + 537: 8: } + -: 9: else + -: 10: { + -: 11: int j; + 6: 12: for (j = 0; j < i; ++j) { + 3: 13: x += j; + 3: 14: } + -: 15: } + 540: 16: } + 54: 17: return x; + -: 18:} + -: 19: + -: 20:int main() + -: 21:{ + 1: 22: int x = foo(10, 10); + 1: 23: int y = 0; + -: 24: int i; + 54: 25: for (i = 0; i < x; ++i) + -: 26: { + 53: 27: y += foo(x, 10); + 53: 28: } + 1: 29: return y; + -: 30:} diff --git a/test/llvm/reader.c.2.gcov b/test/llvm/reader.c.2.gcov new file mode 100644 index 0000000..b927f24 --- /dev/null +++ b/test/llvm/reader.c.2.gcov @@ -0,0 +1,35 @@ + -: 0:Source:reader.c + -: 0:Graph:reader.gcno + -: 0:Data:reader.gcda + -: 0:Runs:2 + -: 0:Programs:1 + -: 1:int foo(int x, int n) + -: 2:{ + -: 3: int i; + 1188: 4: for (i = 0; i < n; ++i) { + 1080: 5: if (x > n) + -: 6: { + 1074: 7: x += i; + 1074: 8: } + -: 9: else + -: 10: { + -: 11: int j; + 12: 12: for (j = 0; j < i; ++j) { + 6: 13: x += j; + 6: 14: } + -: 15: } + 1080: 16: } + 108: 17: return x; + -: 18:} + -: 19: + -: 20:int main() + -: 21:{ + 2: 22: int x = foo(10, 10); + 2: 23: int y = 0; + -: 24: int i; + 108: 25: for (i = 0; i < x; ++i) + -: 26: { + 106: 27: y += foo(x, 10); + 106: 28: } + 2: 29: return y; + -: 30:} diff --git a/test/llvm/reader.gcda b/test/llvm/reader.gcda new file mode 100644 index 0000000000000000000000000000000000000000..59980c88171eddfb4ae057321067a7d16125b9b1 GIT binary patch literal 324 zcmYdHNlw=?GBBC@$@eY;0|O%qkN|4+k`4Ldd6TKt z7F0{)jGtc|$9Ag8@a5|E=I;IL@iT-FJ4qqqpSMm->vL6w^|Ve$gQwB-dWa7^gok*T z*Hu+75*M*}{27M5hMA=oH`9tO{=PYqHXry#jlngsSm3*Y>iBTBTA1wZ8dD9F5BN@t z&w1GG7+~!4XJPN9&%|=VZr_i+-08qbN%x!DG*H5buUfIE-4os4_{P;4|KY+xJh#*n zZ$EM$o#nZ+buvCVD~9KnJ{GQO`WKfx)GyDaj