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