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 "CoverageReport.h"
45 #include "CoverageSummaryInfo.h"
46 #include "CoverageViewOptions.h"
47 #include "llvm/ProfileData/Coverage/CoverageMapping.h"
48 #include <stack>
49 
50 /// \brief The semantic version combined as a string.
51 #define LLVM_COVERAGE_EXPORT_JSON_STR "1.1.0"
52 
53 /// \brief Unique type identifier for JSON coverage export.
54 #define LLVM_COVERAGE_EXPORT_JSON_TYPE_STR "llvm.coverage.json.export"
55 
56 using namespace llvm;
57 using namespace coverage;
58 
59 class CoverageExporterJson {
60   /// \brief A Name of the object file coverage is for.
61   StringRef ObjectFilename;
62 
63   /// \brief Output stream to print JSON to.
64   raw_ostream &OS;
65 
66   /// \brief The full CoverageMapping object to export.
67   const CoverageMapping &Coverage;
68 
69   /// \brief States that the JSON rendering machine can be in.
70   enum JsonState { None, NonEmptyElement, EmptyElement };
71 
72   /// \brief Tracks state of the JSON output.
73   std::stack<JsonState> State;
74 
75   /// \brief Get the object filename.
76   StringRef getObjectFilename() const { return ObjectFilename; }
77 
78   /// \brief Emit a serialized scalar.
79   void emitSerialized(const int64_t Value) { OS << Value; }
80 
81   /// \brief Emit a serialized string.
82   void emitSerialized(const std::string &Value) {
83     OS << "\"";
84     for (char C : Value) {
85       if (C != '\\')
86         OS << C;
87       else
88         OS << "\\\\";
89     }
90     OS << "\"";
91   }
92 
93   /// \brief Emit a comma if there is a previous element to delimit.
94   void emitComma() {
95     if (State.top() == JsonState::NonEmptyElement) {
96       OS << ",";
97     } else if (State.top() == JsonState::EmptyElement) {
98       State.pop();
99       assert((State.size() >= 1) && "Closed too many JSON elements");
100       State.push(JsonState::NonEmptyElement);
101     }
102   }
103 
104   /// \brief Emit a starting dictionary/object character.
105   void emitDictStart() {
106     emitComma();
107     State.push(JsonState::EmptyElement);
108     OS << "{";
109   }
110 
111   /// \brief Emit a dictionary/object key but no value.
112   void emitDictKey(const std::string &Key) {
113     emitComma();
114     emitSerialized(Key);
115     OS << ":";
116     State.pop();
117     assert((State.size() >= 1) && "Closed too many JSON elements");
118 
119     // We do not want to emit a comma after this key.
120     State.push(JsonState::EmptyElement);
121   }
122 
123   /// \brief Emit a dictionary/object key/value pair.
124   template <typename V>
125   void emitDictElement(const std::string &Key, const V &Value) {
126     emitComma();
127     emitSerialized(Key);
128     OS << ":";
129     emitSerialized(Value);
130   }
131 
132   /// \brief Emit a closing dictionary/object character.
133   void emitDictEnd() {
134     State.pop();
135     assert((State.size() >= 1) && "Closed too many JSON elements");
136     OS << "}";
137   }
138 
139   /// \brief Emit a starting array character.
140   void emitArrayStart() {
141     emitComma();
142     State.push(JsonState::EmptyElement);
143     OS << "[";
144   }
145 
146   /// \brief Emit an array element.
147   template <typename V> void emitArrayElement(const V &Value) {
148     emitComma();
149     emitSerialized(Value);
150   }
151 
152   /// \brief emit a closing array character.
153   void emitArrayEnd() {
154     State.pop();
155     assert((State.size() >= 1) && "Closed too many JSON elements");
156     OS << "]";
157   }
158 
159   /// \brief Render the CoverageMapping object.
160   void renderRoot() {
161     // Start Root of JSON object.
162     emitDictStart();
163 
164     emitDictElement("version", LLVM_COVERAGE_EXPORT_JSON_STR);
165     emitDictElement("type", LLVM_COVERAGE_EXPORT_JSON_TYPE_STR);
166     emitDictKey("data");
167 
168     // Start List of Exports.
169     emitArrayStart();
170 
171     // Start Export.
172     emitDictStart();
173     emitDictElement("object", getObjectFilename());
174 
175     emitDictKey("files");
176 
177     FileCoverageSummary Totals = FileCoverageSummary("Totals");
178     std::vector<std::string> SourceFiles;
179     for (StringRef SF : Coverage.getUniqueSourceFiles())
180       SourceFiles.emplace_back(SF);
181     auto FileReports =
182         CoverageReport::prepareFileReports(Coverage, Totals, SourceFiles);
183     renderFiles(SourceFiles, FileReports);
184 
185     emitDictKey("functions");
186     renderFunctions(Coverage.getCoveredFunctions());
187 
188     emitDictKey("totals");
189     renderSummary(Totals);
190 
191     // End Export.
192     emitDictEnd();
193 
194     // End List of Exports.
195     emitArrayEnd();
196 
197     // End Root of JSON Object.
198     emitDictEnd();
199 
200     assert((State.top() == JsonState::None) &&
201            "All Elements In JSON were Closed");
202   }
203 
204   /// \brief Render an array of all the given functions.
205   void
206   renderFunctions(const iterator_range<FunctionRecordIterator> &Functions) {
207     // Start List of Functions.
208     emitArrayStart();
209 
210     for (const auto &Function : Functions) {
211       // Start Function.
212       emitDictStart();
213 
214       emitDictElement("name", Function.Name);
215       emitDictElement("count", Function.ExecutionCount);
216       emitDictKey("regions");
217 
218       renderRegions(Function.CountedRegions);
219 
220       emitDictKey("filenames");
221 
222       // Start Filenames for Function.
223       emitArrayStart();
224 
225       for (const auto &FileName : Function.Filenames)
226         emitArrayElement(FileName);
227 
228       // End Filenames for Function.
229       emitArrayEnd();
230 
231       // End Function.
232       emitDictEnd();
233     }
234 
235     // End List of Functions.
236     emitArrayEnd();
237   }
238 
239   /// \brief Render an array of all the source files, also pass back a Summary.
240   void renderFiles(ArrayRef<std::string> SourceFiles,
241                    ArrayRef<FileCoverageSummary> FileReports) {
242     // Start List of Files.
243     emitArrayStart();
244 
245     for (unsigned I = 0, E = SourceFiles.size(); I < E; ++I) {
246       // Render the file.
247       auto FileCoverage = Coverage.getCoverageForFile(SourceFiles[I]);
248       renderFile(FileCoverage, FileReports[I]);
249     }
250 
251     // End List of Files.
252     emitArrayEnd();
253   }
254 
255   /// \brief Render a single file.
256   void renderFile(const CoverageData &FileCoverage,
257                   const FileCoverageSummary &FileReport) {
258     // Start File.
259     emitDictStart();
260 
261     emitDictElement("filename", FileCoverage.getFilename());
262     emitDictKey("segments");
263 
264     // Start List of Segments.
265     emitArrayStart();
266 
267     for (const auto &Segment : FileCoverage)
268       renderSegment(Segment);
269 
270     // End List of Segments.
271     emitArrayEnd();
272 
273     emitDictKey("expansions");
274 
275     // Start List of Expansions.
276     emitArrayStart();
277 
278     for (const auto &Expansion : FileCoverage.getExpansions())
279       renderExpansion(Expansion);
280 
281     // End List of Expansions.
282     emitArrayEnd();
283 
284     emitDictKey("summary");
285     renderSummary(FileReport);
286 
287     // End File.
288     emitDictEnd();
289   }
290 
291   /// \brief Render a CoverageSegment.
292   void renderSegment(const CoverageSegment &Segment) {
293     // Start Segment.
294     emitArrayStart();
295 
296     emitArrayElement(Segment.Line);
297     emitArrayElement(Segment.Col);
298     emitArrayElement(Segment.Count);
299     emitArrayElement(Segment.HasCount);
300     emitArrayElement(Segment.IsRegionEntry);
301 
302     // End Segment.
303     emitArrayEnd();
304   }
305 
306   /// \brief Render an ExpansionRecord.
307   void renderExpansion(const ExpansionRecord &Expansion) {
308     // Start Expansion.
309     emitDictStart();
310 
311     // Mark the beginning and end of this expansion in the source file.
312     emitDictKey("source_region");
313     renderRegion(Expansion.Region);
314 
315     // Enumerate the coverage information for the expansion.
316     emitDictKey("target_regions");
317     renderRegions(Expansion.Function.CountedRegions);
318 
319     emitDictKey("filenames");
320     // Start List of Filenames to map the fileIDs.
321     emitArrayStart();
322     for (const auto &Filename : Expansion.Function.Filenames)
323       emitArrayElement(Filename);
324     // End List of Filenames.
325     emitArrayEnd();
326 
327     // End Expansion.
328     emitDictEnd();
329   }
330 
331   /// \brief Render a list of CountedRegions.
332   void renderRegions(ArrayRef<CountedRegion> Regions) {
333     // Start List of Regions.
334     emitArrayStart();
335 
336     for (const auto &Region : Regions)
337       renderRegion(Region);
338 
339     // End List of Regions.
340     emitArrayEnd();
341   }
342 
343   /// \brief Render a single CountedRegion.
344   void renderRegion(const CountedRegion &Region) {
345     // Start CountedRegion.
346     emitArrayStart();
347 
348     emitArrayElement(Region.LineStart);
349     emitArrayElement(Region.ColumnStart);
350     emitArrayElement(Region.LineEnd);
351     emitArrayElement(Region.ColumnEnd);
352     emitArrayElement(Region.ExecutionCount);
353     emitArrayElement(Region.FileID);
354     emitArrayElement(Region.ExpandedFileID);
355     emitArrayElement(Region.Kind);
356 
357     // End CountedRegion.
358     emitArrayEnd();
359   }
360 
361   /// \brief Render a FileCoverageSummary.
362   void renderSummary(const FileCoverageSummary &Summary) {
363     // Start Summary for the file.
364     emitDictStart();
365 
366     emitDictKey("lines");
367 
368     // Start Line Coverage Summary.
369     emitDictStart();
370     emitDictElement("count", Summary.LineCoverage.NumLines);
371     emitDictElement("covered", Summary.LineCoverage.Covered);
372     emitDictElement("percent", Summary.LineCoverage.getPercentCovered());
373     // End Line Coverage Summary.
374     emitDictEnd();
375 
376     emitDictKey("functions");
377 
378     // Start Function Coverage Summary.
379     emitDictStart();
380     emitDictElement("count", Summary.FunctionCoverage.NumFunctions);
381     emitDictElement("covered", Summary.FunctionCoverage.Executed);
382     emitDictElement("percent", Summary.FunctionCoverage.getPercentCovered());
383     // End Function Coverage Summary.
384     emitDictEnd();
385 
386     emitDictKey("instantiations");
387 
388     // Start Instantiation Coverage Summary.
389     emitDictStart();
390     emitDictElement("count", Summary.InstantiationCoverage.NumFunctions);
391     emitDictElement("covered", Summary.InstantiationCoverage.Executed);
392     emitDictElement("percent",
393                     Summary.InstantiationCoverage.getPercentCovered());
394     // End Function Coverage Summary.
395     emitDictEnd();
396 
397     emitDictKey("regions");
398 
399     // Start Region Coverage Summary.
400     emitDictStart();
401     emitDictElement("count", Summary.RegionCoverage.NumRegions);
402     emitDictElement("covered", Summary.RegionCoverage.Covered);
403     emitDictElement("notcovered", Summary.RegionCoverage.NotCovered);
404     emitDictElement("percent", Summary.RegionCoverage.getPercentCovered());
405     // End Region Coverage Summary.
406     emitDictEnd();
407 
408     // End Summary for the file.
409     emitDictEnd();
410   }
411 
412 public:
413   CoverageExporterJson(StringRef ObjectFilename,
414                        const CoverageMapping &CoverageMapping, raw_ostream &OS)
415       : ObjectFilename(ObjectFilename), OS(OS), Coverage(CoverageMapping) {
416     State.push(JsonState::None);
417   }
418 
419   /// \brief Print the CoverageMapping.
420   void print() { renderRoot(); }
421 };
422 
423 /// \brief Export the given CoverageMapping to a JSON Format.
424 void exportCoverageDataToJson(StringRef ObjectFilename,
425                               const CoverageMapping &CoverageMapping,
426                               raw_ostream &OS) {
427   auto Exporter = CoverageExporterJson(ObjectFilename, CoverageMapping, OS);
428 
429   Exporter.print();
430 }
431