1 //===- CoverageExporterJson.cpp - Code coverage export --------------------===//
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 // This file implements export of code coverage data to JSON.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 //===----------------------------------------------------------------------===//
15 //
16 // The json code coverage export follows the following format
17 // Root: dict => Root Element containing metadata
18 // -- Data: array => Homogeneous array of one or more export objects
19 // ---- Export: dict => Json representation of one CoverageMapping
20 // ------ Files: array => List of objects describing coverage for files
21 // -------- File: dict => Coverage for a single file
22 // ---------- Segments: array => List of Segments contained in the file
23 // ------------ Segment: dict => Describes a segment of the file with a counter
24 // ---------- Expansions: array => List of expansion records
25 // ------------ Expansion: dict => Object that descibes a single expansion
26 // -------------- CountedRegion: dict => The region to be expanded
27 // -------------- TargetRegions: array => List of Regions in the expansion
28 // ---------------- CountedRegion: dict => Single Region in the expansion
29 // ---------- Summary: dict => Object summarizing the coverage for this file
30 // ------------ LineCoverage: dict => Object summarizing line coverage
31 // ------------ FunctionCoverage: dict => Object summarizing function coverage
32 // ------------ RegionCoverage: dict => Object summarizing region coverage
33 // ------ Functions: array => List of objects describing coverage for functions
34 // -------- Function: dict => Coverage info for a single function
35 // ---------- Filenames: array => List of filenames that the function relates to
36 // ---- Summary: dict => Object summarizing the coverage for the entire binary
37 // ------ LineCoverage: dict => Object summarizing line coverage
38 // ------ FunctionCoverage: dict => Object summarizing function coverage
39 // ------ InstantiationCoverage: dict => Object summarizing inst. coverage
40 // ------ RegionCoverage: dict => Object summarizing region coverage
41 //
42 //===----------------------------------------------------------------------===//
43 
44 #include "CoverageExporterJson.h"
45 #include "CoverageReport.h"
46 
47 /// \brief The semantic version combined as a string.
48 #define LLVM_COVERAGE_EXPORT_JSON_STR "2.0.0"
49 
50 /// \brief Unique type identifier for JSON coverage export.
51 #define LLVM_COVERAGE_EXPORT_JSON_TYPE_STR "llvm.coverage.json.export"
52 
53 using namespace llvm;
54 
55 CoverageExporterJson::CoverageExporterJson(
56     const coverage::CoverageMapping &CoverageMapping,
57     const CoverageViewOptions &Options, raw_ostream &OS)
58     : CoverageExporter(CoverageMapping, Options, OS) {
59   State.push(JsonState::None);
60 }
61 
62 void CoverageExporterJson::emitSerialized(const int64_t Value) { OS << Value; }
63 
64 void CoverageExporterJson::emitSerialized(const std::string &Value) {
65   OS << "\"";
66   for (char C : Value) {
67     if (C != '\\')
68       OS << C;
69     else
70       OS << "\\\\";
71   }
72   OS << "\"";
73 }
74 
75 void CoverageExporterJson::emitComma() {
76   if (State.top() == JsonState::NonEmptyElement) {
77     OS << ",";
78   } else if (State.top() == JsonState::EmptyElement) {
79     State.pop();
80     assert((State.size() >= 1) && "Closed too many JSON elements");
81     State.push(JsonState::NonEmptyElement);
82   }
83 }
84 
85 void CoverageExporterJson::emitDictStart() {
86   emitComma();
87   State.push(JsonState::EmptyElement);
88   OS << "{";
89 }
90 
91 void CoverageExporterJson::emitDictKey(const std::string &Key) {
92   emitComma();
93   emitSerialized(Key);
94   OS << ":";
95   State.pop();
96   assert((State.size() >= 1) && "Closed too many JSON elements");
97 
98   // We do not want to emit a comma after this key.
99   State.push(JsonState::EmptyElement);
100 }
101 
102 void CoverageExporterJson::emitDictEnd() {
103   State.pop();
104   assert((State.size() >= 1) && "Closed too many JSON elements");
105   OS << "}";
106 }
107 
108 void CoverageExporterJson::emitArrayStart() {
109   emitComma();
110   State.push(JsonState::EmptyElement);
111   OS << "[";
112 }
113 
114 void CoverageExporterJson::emitArrayEnd() {
115   State.pop();
116   assert((State.size() >= 1) && "Closed too many JSON elements");
117   OS << "]";
118 }
119 
120 void CoverageExporterJson::renderRoot() {
121   std::vector<std::string> SourceFiles;
122   for (StringRef SF : Coverage.getUniqueSourceFiles())
123     SourceFiles.emplace_back(SF);
124   renderRoot(SourceFiles);
125 }
126 
127 void CoverageExporterJson::renderRoot(
128     const std::vector<std::string> &SourceFiles) {
129   // Start Root of JSON object.
130   emitDictStart();
131 
132   emitDictElement("version", LLVM_COVERAGE_EXPORT_JSON_STR);
133   emitDictElement("type", LLVM_COVERAGE_EXPORT_JSON_TYPE_STR);
134   emitDictKey("data");
135 
136   // Start List of Exports.
137   emitArrayStart();
138 
139   // Start Export.
140   emitDictStart();
141 
142   emitDictKey("files");
143 
144   FileCoverageSummary Totals = FileCoverageSummary("Totals");
145   auto FileReports = CoverageReport::prepareFileReports(Coverage, Totals,
146                                                         SourceFiles, Options);
147   renderFiles(SourceFiles, FileReports);
148 
149   // Skip functions-level information for summary-only export mode.
150   if (!Options.ExportSummaryOnly) {
151     emitDictKey("functions");
152     renderFunctions(Coverage.getCoveredFunctions());
153   }
154 
155   emitDictKey("totals");
156   renderSummary(Totals);
157 
158   // End Export.
159   emitDictEnd();
160 
161   // End List of Exports.
162   emitArrayEnd();
163 
164   // End Root of JSON Object.
165   emitDictEnd();
166 
167   assert((State.top() == JsonState::None) &&
168          "All Elements In JSON were Closed");
169 }
170 
171 void CoverageExporterJson::renderFunctions(
172     const iterator_range<coverage::FunctionRecordIterator> &Functions) {
173   // Start List of Functions.
174   emitArrayStart();
175 
176   for (const auto &Function : Functions) {
177     // Start Function.
178     emitDictStart();
179 
180     emitDictElement("name", Function.Name);
181     emitDictElement("count", Function.ExecutionCount);
182     emitDictKey("regions");
183 
184     renderRegions(Function.CountedRegions);
185 
186     emitDictKey("filenames");
187 
188     // Start Filenames for Function.
189     emitArrayStart();
190 
191     for (const auto &FileName : Function.Filenames)
192       emitArrayElement(FileName);
193 
194     // End Filenames for Function.
195     emitArrayEnd();
196 
197     // End Function.
198     emitDictEnd();
199   }
200 
201   // End List of Functions.
202   emitArrayEnd();
203 }
204 
205 void CoverageExporterJson::renderFiles(
206     ArrayRef<std::string> SourceFiles,
207     ArrayRef<FileCoverageSummary> FileReports) {
208   // Start List of Files.
209   emitArrayStart();
210 
211   for (unsigned I = 0, E = SourceFiles.size(); I < E; ++I) {
212     // Render the file.
213     auto FileCoverage = Coverage.getCoverageForFile(SourceFiles[I]);
214     renderFile(FileCoverage, FileReports[I]);
215   }
216 
217   // End List of Files.
218   emitArrayEnd();
219 }
220 
221 void CoverageExporterJson::renderFile(
222     const coverage::CoverageData &FileCoverage,
223     const FileCoverageSummary &FileReport) {
224   // Start File.
225   emitDictStart();
226 
227   emitDictElement("filename", FileCoverage.getFilename());
228 
229   // Skip segments and expansions for summary-only export mode.
230   if (!Options.ExportSummaryOnly) {
231     emitDictKey("segments");
232 
233     // Start List of Segments.
234     emitArrayStart();
235 
236     for (const auto &Segment : FileCoverage)
237       renderSegment(Segment);
238 
239     // End List of Segments.
240     emitArrayEnd();
241 
242     emitDictKey("expansions");
243 
244     // Start List of Expansions.
245     emitArrayStart();
246 
247     for (const auto &Expansion : FileCoverage.getExpansions())
248       renderExpansion(Expansion);
249 
250     // End List of Expansions.
251     emitArrayEnd();
252   }
253 
254   emitDictKey("summary");
255   renderSummary(FileReport);
256 
257   // End File.
258   emitDictEnd();
259 }
260 
261 void CoverageExporterJson::renderSegment(
262     const coverage::CoverageSegment &Segment) {
263   // Start Segment.
264   emitArrayStart();
265 
266   emitArrayElement(Segment.Line);
267   emitArrayElement(Segment.Col);
268   emitArrayElement(Segment.Count);
269   emitArrayElement(Segment.HasCount);
270   emitArrayElement(Segment.IsRegionEntry);
271 
272   // End Segment.
273   emitArrayEnd();
274 }
275 
276 void CoverageExporterJson::renderExpansion(
277     const coverage::ExpansionRecord &Expansion) {
278   // Start Expansion.
279   emitDictStart();
280 
281   // Mark the beginning and end of this expansion in the source file.
282   emitDictKey("source_region");
283   renderRegion(Expansion.Region);
284 
285   // Enumerate the coverage information for the expansion.
286   emitDictKey("target_regions");
287   renderRegions(Expansion.Function.CountedRegions);
288 
289   emitDictKey("filenames");
290   // Start List of Filenames to map the fileIDs.
291   emitArrayStart();
292   for (const auto &Filename : Expansion.Function.Filenames)
293     emitArrayElement(Filename);
294   // End List of Filenames.
295   emitArrayEnd();
296 
297   // End Expansion.
298   emitDictEnd();
299 }
300 
301 void CoverageExporterJson::renderRegions(
302     ArrayRef<coverage::CountedRegion> Regions) {
303   // Start List of Regions.
304   emitArrayStart();
305 
306   for (const auto &Region : Regions)
307     renderRegion(Region);
308 
309   // End List of Regions.
310   emitArrayEnd();
311 }
312 
313 void CoverageExporterJson::renderRegion(const coverage::CountedRegion &Region) {
314   // Start CountedRegion.
315   emitArrayStart();
316 
317   emitArrayElement(Region.LineStart);
318   emitArrayElement(Region.ColumnStart);
319   emitArrayElement(Region.LineEnd);
320   emitArrayElement(Region.ColumnEnd);
321   emitArrayElement(Region.ExecutionCount);
322   emitArrayElement(Region.FileID);
323   emitArrayElement(Region.ExpandedFileID);
324   emitArrayElement(Region.Kind);
325 
326   // End CountedRegion.
327   emitArrayEnd();
328 }
329 
330 void CoverageExporterJson::renderSummary(const FileCoverageSummary &Summary) {
331   // Start Summary for the file.
332   emitDictStart();
333 
334   emitDictKey("lines");
335 
336   // Start Line Coverage Summary.
337   emitDictStart();
338   emitDictElement("count", Summary.LineCoverage.getNumLines());
339   emitDictElement("covered", Summary.LineCoverage.getCovered());
340   emitDictElement("percent", Summary.LineCoverage.getPercentCovered());
341   // End Line Coverage Summary.
342   emitDictEnd();
343 
344   emitDictKey("functions");
345 
346   // Start Function Coverage Summary.
347   emitDictStart();
348   emitDictElement("count", Summary.FunctionCoverage.getNumFunctions());
349   emitDictElement("covered", Summary.FunctionCoverage.getExecuted());
350   emitDictElement("percent", Summary.FunctionCoverage.getPercentCovered());
351   // End Function Coverage Summary.
352   emitDictEnd();
353 
354   emitDictKey("instantiations");
355 
356   // Start Instantiation Coverage Summary.
357   emitDictStart();
358   emitDictElement("count", Summary.InstantiationCoverage.getNumFunctions());
359   emitDictElement("covered", Summary.InstantiationCoverage.getExecuted());
360   emitDictElement("percent", Summary.InstantiationCoverage.getPercentCovered());
361   // End Function Coverage Summary.
362   emitDictEnd();
363 
364   emitDictKey("regions");
365 
366   // Start Region Coverage Summary.
367   emitDictStart();
368   emitDictElement("count", Summary.RegionCoverage.getNumRegions());
369   emitDictElement("covered", Summary.RegionCoverage.getCovered());
370   emitDictElement("notcovered", Summary.RegionCoverage.getNumRegions() -
371                                     Summary.RegionCoverage.getCovered());
372   emitDictElement("percent", Summary.RegionCoverage.getPercentCovered());
373   // End Region Coverage Summary.
374   emitDictEnd();
375 
376   // End Summary for the file.
377   emitDictEnd();
378 }
379