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