1//===- unittest/ProfileData/CoverageMappingTest.cpp -------------------------=//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "llvm/ProfileData/CoverageMapping.h"
11#include "llvm/ProfileData/CoverageMappingReader.h"
12#include "llvm/ProfileData/CoverageMappingWriter.h"
13#include "llvm/ProfileData/InstrProfReader.h"
14#include "llvm/ProfileData/InstrProfWriter.h"
15#include "llvm/Support/raw_ostream.h"
16#include "gtest/gtest.h"
17
18#include <sstream>
19
20using namespace llvm;
21using namespace coverage;
22
23static ::testing::AssertionResult NoError(std::error_code EC) {
24  if (!EC)
25    return ::testing::AssertionSuccess();
26  return ::testing::AssertionFailure() << "error " << EC.value()
27                                       << ": " << EC.message();
28}
29
30namespace llvm {
31namespace coverage {
32void PrintTo(const Counter &C, ::std::ostream *os) {
33  if (C.isZero())
34    *os << "Zero";
35  else if (C.isExpression())
36    *os << "Expression " << C.getExpressionID();
37  else
38    *os << "Counter " << C.getCounterID();
39}
40
41void PrintTo(const CoverageSegment &S, ::std::ostream *os) {
42  *os << "CoverageSegment(" << S.Line << ", " << S.Col << ", ";
43  if (S.HasCount)
44    *os << S.Count << ", ";
45  *os << (S.IsRegionEntry ? "true" : "false") << ")";
46}
47}
48}
49
50namespace {
51
52struct OneFunctionCoverageReader : CoverageMappingReader {
53  StringRef Name;
54  uint64_t Hash;
55  std::vector<StringRef> Filenames;
56  ArrayRef<CounterMappingRegion> Regions;
57  bool Done;
58
59  OneFunctionCoverageReader(StringRef Name, uint64_t Hash,
60                            ArrayRef<StringRef> Filenames,
61                            ArrayRef<CounterMappingRegion> Regions)
62      : Name(Name), Hash(Hash), Filenames(Filenames), Regions(Regions),
63        Done(false) {}
64
65  std::error_code readNextRecord(CoverageMappingRecord &Record) override {
66    if (Done)
67      return instrprof_error::eof;
68    Done = true;
69
70    Record.FunctionName = Name;
71    Record.FunctionHash = Hash;
72    Record.Filenames = Filenames;
73    Record.Expressions = {};
74    Record.MappingRegions = Regions;
75    return instrprof_error::success;
76  }
77};
78
79struct CoverageMappingTest : ::testing::Test {
80  StringMap<unsigned> Files;
81  unsigned NextFile;
82  std::vector<CounterMappingRegion> InputCMRs;
83
84  std::vector<StringRef> OutputFiles;
85  std::vector<CounterExpression> OutputExpressions;
86  std::vector<CounterMappingRegion> OutputCMRs;
87
88  InstrProfWriter ProfileWriter;
89  std::unique_ptr<IndexedInstrProfReader> ProfileReader;
90
91  std::unique_ptr<CoverageMapping> LoadedCoverage;
92
93  void SetUp() override {
94    NextFile = 0;
95  }
96
97  unsigned getFile(StringRef Name) {
98    auto R = Files.find(Name);
99    if (R != Files.end())
100      return R->second;
101    Files[Name] = NextFile;
102    return NextFile++;
103  }
104
105  void addCMR(Counter C, StringRef File, unsigned LS, unsigned CS, unsigned LE,
106              unsigned CE) {
107    InputCMRs.push_back(
108        CounterMappingRegion::makeRegion(C, getFile(File), LS, CS, LE, CE));
109  }
110
111  void addExpansionCMR(StringRef File, StringRef ExpandedFile, unsigned LS,
112                       unsigned CS, unsigned LE, unsigned CE) {
113    InputCMRs.push_back(CounterMappingRegion::makeExpansion(
114        getFile(File), getFile(ExpandedFile), LS, CS, LE, CE));
115  }
116
117  std::string writeCoverageRegions() {
118    SmallVector<unsigned, 8> FileIDs;
119    for (const auto &E : Files)
120      FileIDs.push_back(E.getValue());
121    std::string Coverage;
122    llvm::raw_string_ostream OS(Coverage);
123    CoverageMappingWriter(FileIDs, None, InputCMRs).write(OS);
124    return OS.str();
125  }
126
127  void readCoverageRegions(std::string Coverage) {
128    SmallVector<StringRef, 8> Filenames;
129    for (const auto &E : Files)
130      Filenames.push_back(E.getKey());
131    RawCoverageMappingReader Reader(Coverage, Filenames, OutputFiles,
132                                    OutputExpressions, OutputCMRs);
133    ASSERT_TRUE(NoError(Reader.read()));
134  }
135
136  void readProfCounts() {
137    auto Profile = ProfileWriter.writeBuffer();
138    auto ReaderOrErr = IndexedInstrProfReader::create(std::move(Profile));
139    ASSERT_TRUE(NoError(ReaderOrErr.getError()));
140    ProfileReader = std::move(ReaderOrErr.get());
141  }
142
143  void loadCoverageMapping(StringRef FuncName, uint64_t Hash) {
144    std::string Regions = writeCoverageRegions();
145    readCoverageRegions(Regions);
146
147    SmallVector<StringRef, 8> Filenames;
148    for (const auto &E : Files)
149      Filenames.push_back(E.getKey());
150    OneFunctionCoverageReader CovReader(FuncName, Hash, Filenames, OutputCMRs);
151    auto CoverageOrErr = CoverageMapping::load(CovReader, *ProfileReader);
152    ASSERT_TRUE(NoError(CoverageOrErr.getError()));
153    LoadedCoverage = std::move(CoverageOrErr.get());
154  }
155};
156
157TEST_F(CoverageMappingTest, basic_write_read) {
158  addCMR(Counter::getCounter(0), "foo", 1, 1, 1, 1);
159  addCMR(Counter::getCounter(1), "foo", 2, 1, 2, 2);
160  addCMR(Counter::getZero(),     "foo", 3, 1, 3, 4);
161  addCMR(Counter::getCounter(2), "foo", 4, 1, 4, 8);
162  addCMR(Counter::getCounter(3), "bar", 1, 2, 3, 4);
163  std::string Coverage = writeCoverageRegions();
164  readCoverageRegions(Coverage);
165
166  size_t N = makeArrayRef(InputCMRs).size();
167  ASSERT_EQ(N, OutputCMRs.size());
168  for (size_t I = 0; I < N; ++I) {
169    ASSERT_EQ(InputCMRs[I].Count,      OutputCMRs[I].Count);
170    ASSERT_EQ(InputCMRs[I].FileID,     OutputCMRs[I].FileID);
171    ASSERT_EQ(InputCMRs[I].startLoc(), OutputCMRs[I].startLoc());
172    ASSERT_EQ(InputCMRs[I].endLoc(),   OutputCMRs[I].endLoc());
173    ASSERT_EQ(InputCMRs[I].Kind,       OutputCMRs[I].Kind);
174  }
175}
176
177TEST_F(CoverageMappingTest, expansion_gets_first_counter) {
178  addCMR(Counter::getCounter(1), "foo", 10, 1, 10, 2);
179  // This starts earlier in "foo", so the expansion should get its counter.
180  addCMR(Counter::getCounter(2), "foo", 1, 1, 20, 1);
181  addExpansionCMR("bar", "foo", 3, 3, 3, 3);
182  std::string Coverage = writeCoverageRegions();
183  readCoverageRegions(Coverage);
184
185  ASSERT_EQ(CounterMappingRegion::ExpansionRegion, OutputCMRs[2].Kind);
186  ASSERT_EQ(Counter::getCounter(2), OutputCMRs[2].Count);
187  ASSERT_EQ(3U, OutputCMRs[2].LineStart);
188}
189
190TEST_F(CoverageMappingTest, basic_coverage_iteration) {
191  InstrProfRecord Record("func", 0x1234, {30, 20, 10, 0});
192  ProfileWriter.addRecord(std::move(Record));
193  readProfCounts();
194
195  addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9);
196  addCMR(Counter::getCounter(1), "file1", 1, 1, 4, 7);
197  addCMR(Counter::getCounter(2), "file1", 5, 8, 9, 1);
198  addCMR(Counter::getCounter(3), "file1", 10, 10, 11, 11);
199  loadCoverageMapping("func", 0x1234);
200
201  CoverageData Data = LoadedCoverage->getCoverageForFile("file1");
202  std::vector<CoverageSegment> Segments(Data.begin(), Data.end());
203  ASSERT_EQ(7U, Segments.size());
204  ASSERT_EQ(CoverageSegment(1, 1, 20, true),  Segments[0]);
205  ASSERT_EQ(CoverageSegment(4, 7, 30, false), Segments[1]);
206  ASSERT_EQ(CoverageSegment(5, 8, 10, true),  Segments[2]);
207  ASSERT_EQ(CoverageSegment(9, 1, 30, false), Segments[3]);
208  ASSERT_EQ(CoverageSegment(9, 9, false),     Segments[4]);
209  ASSERT_EQ(CoverageSegment(10, 10, 0, true), Segments[5]);
210  ASSERT_EQ(CoverageSegment(11, 11, false),   Segments[6]);
211}
212
213TEST_F(CoverageMappingTest, uncovered_function) {
214  readProfCounts();
215
216  addCMR(Counter::getZero(), "file1", 1, 2, 3, 4);
217  loadCoverageMapping("func", 0x1234);
218
219  CoverageData Data = LoadedCoverage->getCoverageForFile("file1");
220  std::vector<CoverageSegment> Segments(Data.begin(), Data.end());
221  ASSERT_EQ(2U, Segments.size());
222  ASSERT_EQ(CoverageSegment(1, 2, 0, true), Segments[0]);
223  ASSERT_EQ(CoverageSegment(3, 4, false),   Segments[1]);
224}
225
226TEST_F(CoverageMappingTest, uncovered_function_with_mapping) {
227  readProfCounts();
228
229  addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9);
230  addCMR(Counter::getCounter(1), "file1", 1, 1, 4, 7);
231  loadCoverageMapping("func", 0x1234);
232
233  CoverageData Data = LoadedCoverage->getCoverageForFile("file1");
234  std::vector<CoverageSegment> Segments(Data.begin(), Data.end());
235  ASSERT_EQ(3U, Segments.size());
236  ASSERT_EQ(CoverageSegment(1, 1, 0, true),  Segments[0]);
237  ASSERT_EQ(CoverageSegment(4, 7, 0, false), Segments[1]);
238  ASSERT_EQ(CoverageSegment(9, 9, false),    Segments[2]);
239}
240
241TEST_F(CoverageMappingTest, combine_regions) {
242  InstrProfRecord Record("func", 0x1234, {10, 20, 30});
243  ProfileWriter.addRecord(std::move(Record));
244  readProfCounts();
245
246  addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9);
247  addCMR(Counter::getCounter(1), "file1", 3, 3, 4, 4);
248  addCMR(Counter::getCounter(2), "file1", 3, 3, 4, 4);
249  loadCoverageMapping("func", 0x1234);
250
251  CoverageData Data = LoadedCoverage->getCoverageForFile("file1");
252  std::vector<CoverageSegment> Segments(Data.begin(), Data.end());
253  ASSERT_EQ(4U, Segments.size());
254  ASSERT_EQ(CoverageSegment(1, 1, 10, true), Segments[0]);
255  ASSERT_EQ(CoverageSegment(3, 3, 50, true), Segments[1]);
256  ASSERT_EQ(CoverageSegment(4, 4, 10, false), Segments[2]);
257  ASSERT_EQ(CoverageSegment(9, 9, false), Segments[3]);
258}
259
260TEST_F(CoverageMappingTest, dont_combine_expansions) {
261  InstrProfRecord Record("func", 0x1234, {10, 20});
262  ProfileWriter.addRecord(std::move(Record));
263  readProfCounts();
264
265  addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9);
266  addCMR(Counter::getCounter(1), "file1", 3, 3, 4, 4);
267  addCMR(Counter::getCounter(1), "include1", 6, 6, 7, 7);
268  addExpansionCMR("file1", "include1", 3, 3, 4, 4);
269  loadCoverageMapping("func", 0x1234);
270
271  CoverageData Data = LoadedCoverage->getCoverageForFile("file1");
272  std::vector<CoverageSegment> Segments(Data.begin(), Data.end());
273  ASSERT_EQ(4U, Segments.size());
274  ASSERT_EQ(CoverageSegment(1, 1, 10, true), Segments[0]);
275  ASSERT_EQ(CoverageSegment(3, 3, 20, true), Segments[1]);
276  ASSERT_EQ(CoverageSegment(4, 4, 10, false), Segments[2]);
277  ASSERT_EQ(CoverageSegment(9, 9, false), Segments[3]);
278}
279
280TEST_F(CoverageMappingTest, strip_filename_prefix) {
281  InstrProfRecord Record("file1:func", 0x1234, {10});
282  ProfileWriter.addRecord(std::move(Record));
283  readProfCounts();
284
285  addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9);
286  loadCoverageMapping("file1:func", 0x1234);
287
288  std::vector<std::string> Names;
289  for (const auto &Func : LoadedCoverage->getCoveredFunctions())
290    Names.push_back(Func.Name);
291  ASSERT_EQ(1U, Names.size());
292  ASSERT_EQ("func", Names[0]);
293}
294
295} // end anonymous namespace
296