diff --git a/llvm/test/tools/llvm-cov/Inputs/binary-formats.canonical.json b/llvm/test/tools/llvm-cov/Inputs/binary-formats.canonical.json index 5f9122d01da9a..f219ca6c7e179 100644 --- a/llvm/test/tools/llvm-cov/Inputs/binary-formats.canonical.json +++ b/llvm/test/tools/llvm-cov/Inputs/binary-formats.canonical.json @@ -33,4 +33,4 @@ CHECK-SAME: "mcdc":{"count":0,"covered":0,"notcovered":0,"percent":0}, CHECK-SAME: "regions":{"count":1,"covered":1,"notcovered":0,"percent":100}}} CHECK-SAME: ], CHECK-SAME: "type":"llvm.coverage.json.export" -CHECK-SAME: "version":"3.0.1" +CHECK-SAME: "version":"3.1.0" diff --git a/llvm/test/tools/llvm-cov/mcdc-export-json.test b/llvm/test/tools/llvm-cov/mcdc-export-json.test index e6dbd17bee5b2..4b6f3b011451a 100644 --- a/llvm/test/tools/llvm-cov/mcdc-export-json.test +++ b/llvm/test/tools/llvm-cov/mcdc-export-json.test @@ -1,10 +1,10 @@ // RUN: llvm-profdata merge %S/Inputs/mcdc-general.proftext -o %t.profdata // RUN: llvm-cov export --format=text %S/Inputs/mcdc-general.o -instr-profile %t.profdata | FileCheck %s -// CHECK: 12,7,12,27,2,4,0,0,5,[true,true,true,true] -// CHECK: 15,7,15,13,1,2,0,0,5,[true,true] -// CHECK: 15,19,15,25,1,1,0,0,5,[true,false] -// CHECK: 18,7,19,15,1,3,0,0,5,[true,true,false,true] +// CHECK: 12,7,12,27,2,4,0,0,5,[true,true,true,true],[{"conditions":[false,null,false,null],"executed":true,"result":false},{"conditions":[false,null,true,false],"executed":true,"result":false},{"conditions":[true,false,false,null],"executed":true,"result":false},{"conditions":[true,false,true,false],"executed":true,"result":false},{"conditions":[true,false,true,true],"executed":true,"result":true},{"conditions":[true,true,null,null],"executed":true,"result":true}] +// CHECK: 15,7,15,13,1,2,0,0,5,[true,true],[{"conditions":[false,null],"executed":true,"result":false},{"conditions":[true,false],"executed":true,"result":false},{"conditions":[true,true],"executed":true,"result":true}] +// CHECK: 15,19,15,25,1,1,0,0,5,[true,false],[{"conditions":[false,null],"executed":true,"result":false},{"conditions":[true,true],"executed":true,"result":true}] +// CHECK: 18,7,19,15,1,3,0,0,5,[true,true,false,true],[{"conditions":[false,null,null,null],"executed":true,"result":false},{"conditions":[true,false,null,null],"executed":true,"result":false},{"conditions":[true,true,true,false],"executed":true,"result":false},{"conditions":[true,true,true,true],"executed":true,"result":true}] // CHECK: "mcdc":{"count":12,"covered":10,"notcovered":2,"percent":83.333333333333343} Instructions for regenerating the test: diff --git a/llvm/tools/llvm-cov/CoverageExporterJson.cpp b/llvm/tools/llvm-cov/CoverageExporterJson.cpp index 06de33dc070e0..ff86c8dfe951b 100644 --- a/llvm/tools/llvm-cov/CoverageExporterJson.cpp +++ b/llvm/tools/llvm-cov/CoverageExporterJson.cpp @@ -21,7 +21,8 @@ // -- Branches: array => List of Branches in the file // -- Branch: dict => Describes a branch of the file with counters // -- MCDC Records: array => List of MCDC records in the file -// -- MCDC Values: array => List of T/F covered condition values +// -- MCDC Values: array => List of T/F covered condition values and +// list of executed test vectors // -- Segments: array => List of Segments contained in the file // -- Segment: dict => Describes a segment of the file with a counter // -- Expansions: array => List of expansion records @@ -62,7 +63,7 @@ #include /// The semantic version combined as a string. -#define LLVM_COVERAGE_EXPORT_JSON_STR "3.0.1" +#define LLVM_COVERAGE_EXPORT_JSON_STR "3.1.0" /// Unique type identifier for JSON coverage export. #define LLVM_COVERAGE_EXPORT_JSON_TYPE_STR "llvm.coverage.json.export" @@ -108,13 +109,42 @@ json::Array gatherConditions(const coverage::MCDCRecord &Record) { return Conditions; } +json::Value renderCondState(const coverage::MCDCRecord::CondState CondState) { + switch (CondState) { + case coverage::MCDCRecord::MCDC_DontCare: + return json::Value(nullptr); + case coverage::MCDCRecord::MCDC_True: + return json::Value(true); + case coverage::MCDCRecord::MCDC_False: + return json::Value(false); + } +} + +json::Array gatherTestVectors(coverage::MCDCRecord &Record) { + json::Array TestVectors; + unsigned NumConditions = Record.getNumConditions(); + for (unsigned tv = 0; tv < Record.getNumTestVectors(); tv++) { + + json::Array TVConditions; + for (unsigned c = 0; c < NumConditions; c++) + TVConditions.push_back(renderCondState(Record.getTVCondition(tv, c))); + + TestVectors.push_back( + json::Object({{"executed", json::Value(true)}, + {"result", renderCondState(Record.getTVResult(tv))}, + {"conditions", std::move(TVConditions)}})); + } + return TestVectors; +} + json::Array renderMCDCRecord(const coverage::MCDCRecord &Record) { const llvm::coverage::CounterMappingRegion &CMR = Record.getDecisionRegion(); const auto [TrueDecisions, FalseDecisions] = Record.getDecisions(); - return json::Array({CMR.LineStart, CMR.ColumnStart, CMR.LineEnd, - CMR.ColumnEnd, TrueDecisions, FalseDecisions, - CMR.FileID, CMR.ExpandedFileID, int64_t(CMR.Kind), - gatherConditions(Record)}); + return json::Array( + {CMR.LineStart, CMR.ColumnStart, CMR.LineEnd, CMR.ColumnEnd, + TrueDecisions, FalseDecisions, CMR.FileID, CMR.ExpandedFileID, + int64_t(CMR.Kind), gatherConditions(Record), + gatherTestVectors(const_cast(Record))}); } json::Array renderRegions(ArrayRef Regions) { @@ -216,32 +246,28 @@ json::Object renderSummary(const FileCoverageSummary &Summary) { } json::Array renderFileExpansions(const coverage::CoverageMapping &Coverage, - const coverage::CoverageData &FileCoverage, - const FileCoverageSummary &FileReport) { + const coverage::CoverageData &FileCoverage) { json::Array ExpansionArray; for (const auto &Expansion : FileCoverage.getExpansions()) ExpansionArray.push_back(renderExpansion(Coverage, Expansion)); return ExpansionArray; } -json::Array renderFileSegments(const coverage::CoverageData &FileCoverage, - const FileCoverageSummary &FileReport) { +json::Array renderFileSegments(const coverage::CoverageData &FileCoverage) { json::Array SegmentArray; for (const auto &Segment : FileCoverage) SegmentArray.push_back(renderSegment(Segment)); return SegmentArray; } -json::Array renderFileBranches(const coverage::CoverageData &FileCoverage, - const FileCoverageSummary &FileReport) { +json::Array renderFileBranches(const coverage::CoverageData &FileCoverage) { json::Array BranchArray; for (const auto &Branch : FileCoverage.getBranches()) BranchArray.push_back(renderBranch(Branch)); return BranchArray; } -json::Array renderFileMCDC(const coverage::CoverageData &FileCoverage, - const FileCoverageSummary &FileReport) { +json::Array renderFileMCDC(const coverage::CoverageData &FileCoverage) { json::Array MCDCRecordArray; for (const auto &Record : FileCoverage.getMCDCRecords()) MCDCRecordArray.push_back(renderMCDCRecord(Record)); @@ -256,12 +282,11 @@ json::Object renderFile(const coverage::CoverageMapping &Coverage, if (!Options.ExportSummaryOnly) { // Calculate and render detailed coverage information for given file. auto FileCoverage = Coverage.getCoverageForFile(Filename); - File["segments"] = renderFileSegments(FileCoverage, FileReport); - File["branches"] = renderFileBranches(FileCoverage, FileReport); - File["mcdc_records"] = renderFileMCDC(FileCoverage, FileReport); + File["segments"] = renderFileSegments(FileCoverage); + File["branches"] = renderFileBranches(FileCoverage); + File["mcdc_records"] = renderFileMCDC(FileCoverage); if (!Options.SkipExpansions) { - File["expansions"] = - renderFileExpansions(Coverage, FileCoverage, FileReport); + File["expansions"] = renderFileExpansions(Coverage, FileCoverage); } } File["summary"] = renderSummary(FileReport);