xaizek / uncov (License: AGPLv3+) (since 2018-12-07)
Uncov(er) is a tool that collects and processes code coverage reports.
Commit a9dbe113fe6e25fb3a0eafa9eb1933e900ee95ff

Implement name-binning of gcov input files
This makes new-gcovi work for GCC 8, see the bug:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89961
Author: xaizek
Author date (UTC): 2021-06-05 13:46
Committer name: xaizek
Committer date (UTC): 2021-06-05 13:49
Parent(s): a142f8fac1cf394ca8fd8392ae0bb7dc305a2cca
Signing key: 99DC5E4DB05F6BE2
Tree: 5ed84bd471f5543507f814799c369083f75002b5
File Lines added Lines deleted
docs/uncov.1 3 4
docs/uncov/06-subcommand-list.md 2 3
src/GcovImporter.cpp 121 13
src/GcovImporter.hpp 24 0
File docs/uncov.1 changed (mode: 100644) (index facc095..b283304)
1 1 .\" Automatically generated by Pandoc 1.17.0.3 .\" Automatically generated by Pandoc 1.17.0.3
2 2 .\" .\"
3 .TH "uncov" "1" "May 26, 2021" "uncov v0.3" ""
3 .TH "uncov" "1" "June 05, 2021" "uncov v0.3" ""
4 4 .hy .hy
5 5 .SH NAME .SH NAME
6 6 .PP .PP
 
... ... when greater than zero \-\- line is covered and was hit that many times.
375 375 .SS new\-gcovi .SS new\-gcovi
376 376 .PP .PP
377 377 Generates coverage via \f[C]gcov\f[] and imports it. Generates coverage via \f[C]gcov\f[] and imports it.
378 Doesn\[aq]t understand json version of intermediate format and is thus
379 unusable for GCC older than GCC 7 and GCC 8 has a bug which prevents
380 this command from working correctly.
378 Doesn\[aq]t understand JSON version of intermediate format and is thus
379 unusable for GCC newer than GCC 8.
381 380 .PP .PP
382 381 \f[B]Usage: new\-gcovi [options...] [covoutroot]\f[] \f[B]Usage: new\-gcovi [options...] [covoutroot]\f[]
383 382 .PP .PP
File docs/uncov/06-subcommand-list.md changed (mode: 100644) (index 34b3318..5613653)
... ... Integers have the following meaning:
197 197 new-gcovi new-gcovi
198 198 --------- ---------
199 199
200 Generates coverage via `gcov` and imports it. Doesn't understand json version
201 of intermediate format and is thus unusable for GCC older than GCC 7 and GCC 8
202 has a bug which prevents this command from working correctly.
200 Generates coverage via `gcov` and imports it. Doesn't understand JSON version
201 of intermediate format and is thus unusable for GCC newer than GCC 8.
203 202
204 203 **Usage: new-gcovi [options...] [covoutroot]** **Usage: new-gcovi [options...] [covoutroot]**
205 204
File src/GcovImporter.cpp changed (mode: 100644) (index 24ac2ac..1581526)
19 19 #include <boost/algorithm/string/trim.hpp> #include <boost/algorithm/string/trim.hpp>
20 20 #include <boost/filesystem/operations.hpp> #include <boost/filesystem/operations.hpp>
21 21
22 #include <cassert>
23
22 24 #include <fstream> #include <fstream>
25 #include <regex>
23 26 #include <set> #include <set>
24 27 #include <stdexcept> #include <stdexcept>
25 28 #include <string> #include <string>
 
32 35 #include "utils/md5.hpp" #include "utils/md5.hpp"
33 36 #include "utils/strings.hpp" #include "utils/strings.hpp"
34 37 #include "BuildHistory.hpp" #include "BuildHistory.hpp"
38 #include "integration.hpp"
35 39
36 40 namespace fs = boost::filesystem; namespace fs = boost::filesystem;
37 41
42 // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89961 for information about
43 // what's wrong with some versions of `gcov` and why binning is needed.
44
45 //! First version of `gcov` which has broken `--preserve-paths` option.
46 static int FirstBrokenGcovVersion = 8;
47
48 namespace {
49 /**
50 * @brief A set of files that should be passed to `gcov` at the same time.
51 */
52 class Bin {
53 public:
54 /**
55 * @brief Constructs an empty set.
56 *
57 * @param deduplicateNames Whether to avoid adding name-duplicates.
58 */
59 Bin(bool deduplicateNames = true) : deduplicateNames(deduplicateNames)
60 { }
61
62 public:
63 /**
64 * @brief Tries to add a file to this bin.
65 *
66 * @param path Absolute path to the file.
67 *
68 * @returns `true` if the path was added.
69 */
70 bool add(const fs::path &path)
71 {
72 assert(path.is_absolute() && "Paths should be absolute.");
73
74 if (deduplicateNames &&
75 !names.emplace(path.filename().string()).second) {
76 return false;
77 }
78
79 paths.emplace_back(path.string());
80 return true;
81 }
82
83 /**
84 * @brief Retrieves list of paths of this bin.
85 *
86 * @returns The list.
87 */
88 const std::vector<std::string> & getPaths() const
89 { return paths; }
90
91 private:
92 //! Whether no two files should have the same name.
93 bool deduplicateNames;
94 //! Names of files in this bin if `deduplicateNames` is set.
95 std::unordered_set<std::string> names;
96 //! Files of this bin.
97 std::vector<std::string> paths;
98 };
99 }
100
101 GcovInfo::GcovInfo() : employBinning(true)
102 {
103 const std::regex versionRegex("gcov \\(GCC\\) (.*)");
104
105 std::smatch match;
106
107 const std::string version = readProc({ "gcov", "--version" });
108 if (std::regex_search(version, match, versionRegex)) {
109 const int majorVersion = std::stoi(match[1]);
110 employBinning = (majorVersion >= FirstBrokenGcovVersion);
111 }
112 }
113
38 114 void void
39 115 GcovImporter::setRunner(std::function<runner_f> runner) GcovImporter::setRunner(std::function<runner_f> runner)
40 116 { {
 
... ... GcovImporter::getFiles() &&
137 213 void void
138 214 GcovImporter::importFiles(std::vector<fs::path> gcnoFiles) GcovImporter::importFiles(std::vector<fs::path> gcnoFiles)
139 215 { {
140 std::vector<std::string> cmd = {
141 "gcov", "--preserve-paths", "--intermediate-format", "--"
142 };
143 for (const fs::path &gcnoFile : gcnoFiles) {
144 cmd.emplace_back(gcnoFile.string());
216 std::vector<Bin> bins;
217
218 if (gcovInfo.needsBinning()) {
219 // We want to execute the runner for tests even if there are no input
220 // files.
221 bins.emplace_back();
222
223 for (const fs::path &gcnoFile : gcnoFiles) {
224 bool added = false;
225
226 for (Bin &bin : bins) {
227 if (bin.add(gcnoFile)) {
228 added = true;
229 break;
230 }
231 }
232
233 if (!added) {
234 bins.emplace_back();
235 bins.back().add(gcnoFile);
236 }
237 }
238 } else {
239 bins.emplace_back(/*deduplicateNames=*/false);
240 Bin &bin = bins.back();
241 for (const fs::path &gcnoFile : gcnoFiles) {
242 bin.add(gcnoFile);
243 }
145 244 } }
146 245
147 TempDir tempDir("gcovi");
148 std::string tempDirPath = tempDir;
149 getRunner()(std::move(cmd), tempDirPath);
246 for (const Bin &bin : bins) {
247 const std::vector<std::string> &paths = bin.getPaths();
150 248
151 for (fs::recursive_directory_iterator it(tempDirPath), end;
152 it != end; ++it) {
153 fs::path path = it->path();
154 if (fs::is_regular(path) && path.extension() == ".gcov") {
155 parseGcov(path.string());
249 std::vector<std::string> cmd = {
250 "gcov", "--preserve-paths", "--intermediate-format", "--"
251 };
252 cmd.insert(cmd.cend(), paths.cbegin(), paths.cend());
253
254 TempDir tempDir("gcovi");
255 std::string tempDirPath = tempDir;
256 getRunner()(std::move(cmd), tempDirPath);
257
258 for (fs::recursive_directory_iterator it(tempDirPath), end;
259 it != end; ++it) {
260 fs::path path = it->path();
261 if (fs::is_regular(path) && path.extension() == ".gcov") {
262 parseGcov(path.string());
263 }
156 264 } }
157 265 } }
158 266 } }
File src/GcovImporter.hpp changed (mode: 100644) (index 6aca643..13a22e7)
27 27
28 28 class File; class File;
29 29
30 /**
31 * @brief Determines information about `gcov` command.
32 */
33 class GcovInfo
34 {
35 public:
36 GcovInfo();
37
38 public:
39 /**
40 * @brief Checks whether binning is needed.
41 *
42 * @returns `true` if so.
43 */
44 bool needsBinning() const
45 { return employBinning; }
46
47 private:
48 //! Whether `gcov` command doesn't handle identically-named files properly.
49 bool employBinning;
50 };
51
30 52 /** /**
31 53 * @brief Generates information by calling `gcov` and collects it. * @brief Generates information by calling `gcov` and collects it.
32 54 */ */
 
... ... private:
100 122 static std::function<runner_f> & getRunner(); static std::function<runner_f> & getRunner();
101 123
102 124 private: private:
125 //! Information about `gcov` command.
126 GcovInfo gcovInfo;
103 127 //! Absolute and normalized path of the source repository. //! Absolute and normalized path of the source repository.
104 128 boost::filesystem::path rootDir; boost::filesystem::path rootDir;
105 129 //! List of absolute and normalized path to be excluded. //! List of absolute and normalized path to be excluded.
Hints

Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/uncov

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/uncov

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master