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