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