Mercurial > hg > CbC > CbC_llvm
comparison tools/llvm-cov/CodeCoverage.cpp @ 121:803732b1fca8
LLVM 5.0
author | kono |
---|---|
date | Fri, 27 Oct 2017 17:07:41 +0900 |
parents | 1172e4bd9c6f |
children | 3a76565eade5 |
comparison
equal
deleted
inserted
replaced
120:1172e4bd9c6f | 121:803732b1fca8 |
---|---|
13 // | 13 // |
14 //===----------------------------------------------------------------------===// | 14 //===----------------------------------------------------------------------===// |
15 | 15 |
16 #include "CoverageFilters.h" | 16 #include "CoverageFilters.h" |
17 #include "CoverageReport.h" | 17 #include "CoverageReport.h" |
18 #include "CoverageSummaryInfo.h" | |
18 #include "CoverageViewOptions.h" | 19 #include "CoverageViewOptions.h" |
19 #include "RenderingSupport.h" | 20 #include "RenderingSupport.h" |
20 #include "SourceCoverageView.h" | 21 #include "SourceCoverageView.h" |
21 #include "llvm/ADT/SmallString.h" | 22 #include "llvm/ADT/SmallString.h" |
22 #include "llvm/ADT/StringRef.h" | 23 #include "llvm/ADT/StringRef.h" |
29 #include "llvm/Support/MemoryBuffer.h" | 30 #include "llvm/Support/MemoryBuffer.h" |
30 #include "llvm/Support/Path.h" | 31 #include "llvm/Support/Path.h" |
31 #include "llvm/Support/Process.h" | 32 #include "llvm/Support/Process.h" |
32 #include "llvm/Support/Program.h" | 33 #include "llvm/Support/Program.h" |
33 #include "llvm/Support/ScopedPrinter.h" | 34 #include "llvm/Support/ScopedPrinter.h" |
35 #include "llvm/Support/Threading.h" | |
34 #include "llvm/Support/ThreadPool.h" | 36 #include "llvm/Support/ThreadPool.h" |
35 #include "llvm/Support/ToolOutputFile.h" | 37 #include "llvm/Support/ToolOutputFile.h" |
38 | |
36 #include <functional> | 39 #include <functional> |
40 #include <map> | |
37 #include <system_error> | 41 #include <system_error> |
38 | 42 |
39 using namespace llvm; | 43 using namespace llvm; |
40 using namespace coverage; | 44 using namespace coverage; |
41 | 45 |
42 void exportCoverageDataToJson(const coverage::CoverageMapping &CoverageMapping, | 46 void exportCoverageDataToJson(const coverage::CoverageMapping &CoverageMapping, |
47 const CoverageViewOptions &Options, | |
43 raw_ostream &OS); | 48 raw_ostream &OS); |
44 | 49 |
45 namespace { | 50 namespace { |
46 /// \brief The implementation of the coverage tool. | 51 /// \brief The implementation of the coverage tool. |
47 class CodeCoverageTool { | 52 class CodeCoverageTool { |
90 createSourceFileView(StringRef SourceFile, const CoverageMapping &Coverage); | 95 createSourceFileView(StringRef SourceFile, const CoverageMapping &Coverage); |
91 | 96 |
92 /// \brief Load the coverage mapping data. Return nullptr if an error occurred. | 97 /// \brief Load the coverage mapping data. Return nullptr if an error occurred. |
93 std::unique_ptr<CoverageMapping> load(); | 98 std::unique_ptr<CoverageMapping> load(); |
94 | 99 |
100 /// \brief Create a mapping from files in the Coverage data to local copies | |
101 /// (path-equivalence). | |
102 void remapPathNames(const CoverageMapping &Coverage); | |
103 | |
95 /// \brief Remove input source files which aren't mapped by \p Coverage. | 104 /// \brief Remove input source files which aren't mapped by \p Coverage. |
96 void removeUnmappedInputs(const CoverageMapping &Coverage); | 105 void removeUnmappedInputs(const CoverageMapping &Coverage); |
97 | 106 |
98 /// \brief If a demangler is available, demangle all symbol names. | 107 /// \brief If a demangler is available, demangle all symbol names. |
99 void demangleSymbols(const CoverageMapping &Coverage); | 108 void demangleSymbols(const CoverageMapping &Coverage); |
100 | |
101 /// \brief Demangle \p Sym if possible. Otherwise, just return \p Sym. | |
102 StringRef getSymbolForHumans(StringRef Sym) const; | |
103 | 109 |
104 /// \brief Write out a source file view to the filesystem. | 110 /// \brief Write out a source file view to the filesystem. |
105 void writeSourceFileView(StringRef SourceFile, CoverageMapping *Coverage, | 111 void writeSourceFileView(StringRef SourceFile, CoverageMapping *Coverage, |
106 CoveragePrinter *Printer, bool ShowFilenames); | 112 CoveragePrinter *Printer, bool ShowFilenames); |
107 | 113 |
124 std::string PGOFilename; | 130 std::string PGOFilename; |
125 | 131 |
126 /// A list of input source files. | 132 /// A list of input source files. |
127 std::vector<std::string> SourceFiles; | 133 std::vector<std::string> SourceFiles; |
128 | 134 |
129 /// Whether or not we're in -filename-equivalence mode. | 135 /// In -path-equivalence mode, this maps the absolute paths from the coverage |
130 bool CompareFilenamesOnly; | 136 /// mapping data to the input source files. |
131 | |
132 /// In -filename-equivalence mode, this maps absolute paths from the | |
133 /// coverage mapping data to input source files. | |
134 StringMap<std::string> RemappedFilenames; | 137 StringMap<std::string> RemappedFilenames; |
135 | 138 |
139 /// The coverage data path to be remapped from, and the source path to be | |
140 /// remapped to, when using -path-equivalence. | |
141 Optional<std::pair<std::string, std::string>> PathRemapping; | |
142 | |
136 /// The architecture the coverage mapping data targets. | 143 /// The architecture the coverage mapping data targets. |
137 std::string CoverageArch; | 144 std::vector<StringRef> CoverageArches; |
138 | 145 |
139 /// A cache for demangled symbol names. | 146 /// A cache for demangled symbols. |
140 StringMap<std::string> DemangledNames; | 147 DemangleCache DC; |
141 | 148 |
142 /// Errors and warnings which have not been printed. | 149 /// A lock which guards printing to stderr. |
143 std::mutex ErrsLock; | 150 std::mutex ErrsLock; |
144 | 151 |
145 /// A container for input source file buffers. | 152 /// A container for input source file buffers. |
146 std::mutex LoadedSourceFilesLock; | 153 std::mutex LoadedSourceFilesLock; |
147 std::vector<std::pair<std::string, std::unique_ptr<MemoryBuffer>>> | 154 std::vector<std::pair<std::string, std::unique_ptr<MemoryBuffer>>> |
148 LoadedSourceFiles; | 155 LoadedSourceFiles; |
156 | |
157 /// Whitelist from -name-whitelist to be used for filtering. | |
158 std::unique_ptr<SpecialCaseList> NameWhitelist; | |
149 }; | 159 }; |
150 } | 160 } |
151 | 161 |
152 static std::string getErrorString(const Twine &Message, StringRef Whence, | 162 static std::string getErrorString(const Twine &Message, StringRef Whence, |
153 bool Warning) { | 163 bool Warning) { |
170 ViewOpts.colored_ostream(errs(), raw_ostream::RED) | 180 ViewOpts.colored_ostream(errs(), raw_ostream::RED) |
171 << getErrorString(Message, Whence, true); | 181 << getErrorString(Message, Whence, true); |
172 } | 182 } |
173 | 183 |
174 void CodeCoverageTool::addCollectedPath(const std::string &Path) { | 184 void CodeCoverageTool::addCollectedPath(const std::string &Path) { |
175 if (CompareFilenamesOnly) { | 185 SmallString<128> EffectivePath(Path); |
176 SourceFiles.emplace_back(Path); | 186 if (std::error_code EC = sys::fs::make_absolute(EffectivePath)) { |
177 } else { | 187 error(EC.message(), Path); |
178 SmallString<128> EffectivePath(Path); | 188 return; |
179 if (std::error_code EC = sys::fs::make_absolute(EffectivePath)) { | 189 } |
180 error(EC.message(), Path); | 190 sys::path::remove_dots(EffectivePath, /*remove_dot_dots=*/true); |
181 return; | 191 SourceFiles.emplace_back(EffectivePath.str()); |
182 } | |
183 sys::path::remove_dots(EffectivePath, /*remove_dot_dots=*/true); | |
184 SourceFiles.emplace_back(EffectivePath.str()); | |
185 } | |
186 } | 192 } |
187 | 193 |
188 void CodeCoverageTool::collectPaths(const std::string &Path) { | 194 void CodeCoverageTool::collectPaths(const std::string &Path) { |
189 llvm::sys::fs::file_status Status; | 195 llvm::sys::fs::file_status Status; |
190 llvm::sys::fs::status(Path, Status); | 196 llvm::sys::fs::status(Path, Status); |
191 if (!llvm::sys::fs::exists(Status)) { | 197 if (!llvm::sys::fs::exists(Status)) { |
192 if (CompareFilenamesOnly) | 198 if (PathRemapping) |
193 addCollectedPath(Path); | 199 addCollectedPath(Path); |
194 else | 200 else |
195 error("Missing source file", Path); | 201 error("Missing source file", Path); |
196 return; | 202 return; |
197 } | 203 } |
265 auto SourceBuffer = getSourceFile(FunctionCoverage.getFilename()); | 271 auto SourceBuffer = getSourceFile(FunctionCoverage.getFilename()); |
266 if (!SourceBuffer) | 272 if (!SourceBuffer) |
267 return nullptr; | 273 return nullptr; |
268 | 274 |
269 auto Expansions = FunctionCoverage.getExpansions(); | 275 auto Expansions = FunctionCoverage.getExpansions(); |
270 auto View = SourceCoverageView::create(getSymbolForHumans(Function.Name), | 276 auto View = SourceCoverageView::create(DC.demangle(Function.Name), |
271 SourceBuffer.get(), ViewOpts, | 277 SourceBuffer.get(), ViewOpts, |
272 std::move(FunctionCoverage)); | 278 std::move(FunctionCoverage)); |
273 attachExpansionSubViews(*View, Expansions, Coverage); | 279 attachExpansionSubViews(*View, Expansions, Coverage); |
274 | 280 |
275 return View; | 281 return View; |
287 | 293 |
288 auto Expansions = FileCoverage.getExpansions(); | 294 auto Expansions = FileCoverage.getExpansions(); |
289 auto View = SourceCoverageView::create(SourceFile, SourceBuffer.get(), | 295 auto View = SourceCoverageView::create(SourceFile, SourceBuffer.get(), |
290 ViewOpts, std::move(FileCoverage)); | 296 ViewOpts, std::move(FileCoverage)); |
291 attachExpansionSubViews(*View, Expansions, Coverage); | 297 attachExpansionSubViews(*View, Expansions, Coverage); |
292 | 298 if (!ViewOpts.ShowFunctionInstantiations) |
293 for (const auto *Function : Coverage.getInstantiations(SourceFile)) { | 299 return View; |
294 std::unique_ptr<SourceCoverageView> SubView{nullptr}; | 300 |
295 | 301 for (const auto &Group : Coverage.getInstantiationGroups(SourceFile)) { |
296 StringRef Funcname = getSymbolForHumans(Function->Name); | 302 // Skip functions which have a single instantiation. |
297 | 303 if (Group.size() < 2) |
298 if (Function->ExecutionCount > 0) { | 304 continue; |
299 auto SubViewCoverage = Coverage.getCoverageForFunction(*Function); | 305 |
300 auto SubViewExpansions = SubViewCoverage.getExpansions(); | 306 for (const FunctionRecord *Function : Group.getInstantiations()) { |
301 SubView = SourceCoverageView::create( | 307 std::unique_ptr<SourceCoverageView> SubView{nullptr}; |
302 Funcname, SourceBuffer.get(), ViewOpts, std::move(SubViewCoverage)); | 308 |
303 attachExpansionSubViews(*SubView, SubViewExpansions, Coverage); | 309 StringRef Funcname = DC.demangle(Function->Name); |
304 } | 310 |
305 | 311 if (Function->ExecutionCount > 0) { |
306 unsigned FileID = Function->CountedRegions.front().FileID; | 312 auto SubViewCoverage = Coverage.getCoverageForFunction(*Function); |
307 unsigned Line = 0; | 313 auto SubViewExpansions = SubViewCoverage.getExpansions(); |
308 for (const auto &CR : Function->CountedRegions) | 314 SubView = SourceCoverageView::create( |
309 if (CR.FileID == FileID) | 315 Funcname, SourceBuffer.get(), ViewOpts, std::move(SubViewCoverage)); |
310 Line = std::max(CR.LineEnd, Line); | 316 attachExpansionSubViews(*SubView, SubViewExpansions, Coverage); |
311 View->addInstantiation(Funcname, Line, std::move(SubView)); | 317 } |
318 | |
319 unsigned FileID = Function->CountedRegions.front().FileID; | |
320 unsigned Line = 0; | |
321 for (const auto &CR : Function->CountedRegions) | |
322 if (CR.FileID == FileID) | |
323 Line = std::max(CR.LineEnd, Line); | |
324 View->addInstantiation(Funcname, Line, std::move(SubView)); | |
325 } | |
312 } | 326 } |
313 return View; | 327 return View; |
314 } | 328 } |
315 | 329 |
316 static bool modifiedTimeGT(StringRef LHS, StringRef RHS) { | 330 static bool modifiedTimeGT(StringRef LHS, StringRef RHS) { |
328 for (StringRef ObjectFilename : ObjectFilenames) | 342 for (StringRef ObjectFilename : ObjectFilenames) |
329 if (modifiedTimeGT(ObjectFilename, PGOFilename)) | 343 if (modifiedTimeGT(ObjectFilename, PGOFilename)) |
330 warning("profile data may be out of date - object is newer", | 344 warning("profile data may be out of date - object is newer", |
331 ObjectFilename); | 345 ObjectFilename); |
332 auto CoverageOrErr = | 346 auto CoverageOrErr = |
333 CoverageMapping::load(ObjectFilenames, PGOFilename, CoverageArch); | 347 CoverageMapping::load(ObjectFilenames, PGOFilename, CoverageArches); |
334 if (Error E = CoverageOrErr.takeError()) { | 348 if (Error E = CoverageOrErr.takeError()) { |
335 error("Failed to load coverage: " + toString(std::move(E)), | 349 error("Failed to load coverage: " + toString(std::move(E)), |
336 join(ObjectFilenames.begin(), ObjectFilenames.end(), ", ")); | 350 join(ObjectFilenames.begin(), ObjectFilenames.end(), ", ")); |
337 return nullptr; | 351 return nullptr; |
338 } | 352 } |
339 auto Coverage = std::move(CoverageOrErr.get()); | 353 auto Coverage = std::move(CoverageOrErr.get()); |
340 unsigned Mismatched = Coverage->getMismatchedCount(); | 354 unsigned Mismatched = Coverage->getMismatchedCount(); |
341 if (Mismatched) | 355 if (Mismatched) { |
342 warning(utostr(Mismatched) + " functions have mismatched data"); | 356 warning(utostr(Mismatched) + " functions have mismatched data"); |
357 | |
358 if (ViewOpts.Debug) { | |
359 for (const auto &HashMismatch : Coverage->getHashMismatches()) | |
360 errs() << "hash-mismatch: " | |
361 << "No profile record found for '" << HashMismatch.first << "'" | |
362 << " with hash = 0x" << utohexstr(HashMismatch.second) << "\n"; | |
363 | |
364 for (const auto &CounterMismatch : Coverage->getCounterMismatches()) | |
365 errs() << "counter-mismatch: " | |
366 << "Coverage mapping for " << CounterMismatch.first | |
367 << " only has " << CounterMismatch.second | |
368 << " valid counter expressions\n"; | |
369 } | |
370 } | |
371 | |
372 remapPathNames(*Coverage); | |
343 | 373 |
344 if (!SourceFiles.empty()) | 374 if (!SourceFiles.empty()) |
345 removeUnmappedInputs(*Coverage); | 375 removeUnmappedInputs(*Coverage); |
346 | 376 |
347 demangleSymbols(*Coverage); | 377 demangleSymbols(*Coverage); |
348 | 378 |
349 return Coverage; | 379 return Coverage; |
350 } | 380 } |
351 | 381 |
382 void CodeCoverageTool::remapPathNames(const CoverageMapping &Coverage) { | |
383 if (!PathRemapping) | |
384 return; | |
385 | |
386 // Convert remapping paths to native paths with trailing seperators. | |
387 auto nativeWithTrailing = [](StringRef Path) -> std::string { | |
388 if (Path.empty()) | |
389 return ""; | |
390 SmallString<128> NativePath; | |
391 sys::path::native(Path, NativePath); | |
392 if (!sys::path::is_separator(NativePath.back())) | |
393 NativePath += sys::path::get_separator(); | |
394 return NativePath.c_str(); | |
395 }; | |
396 std::string RemapFrom = nativeWithTrailing(PathRemapping->first); | |
397 std::string RemapTo = nativeWithTrailing(PathRemapping->second); | |
398 | |
399 // Create a mapping from coverage data file paths to local paths. | |
400 for (StringRef Filename : Coverage.getUniqueSourceFiles()) { | |
401 SmallString<128> NativeFilename; | |
402 sys::path::native(Filename, NativeFilename); | |
403 if (NativeFilename.startswith(RemapFrom)) { | |
404 RemappedFilenames[Filename] = | |
405 RemapTo + NativeFilename.substr(RemapFrom.size()).str(); | |
406 } | |
407 } | |
408 | |
409 // Convert input files from local paths to coverage data file paths. | |
410 StringMap<std::string> InvRemappedFilenames; | |
411 for (const auto &RemappedFilename : RemappedFilenames) | |
412 InvRemappedFilenames[RemappedFilename.getValue()] = RemappedFilename.getKey(); | |
413 | |
414 for (std::string &Filename : SourceFiles) { | |
415 SmallString<128> NativeFilename; | |
416 sys::path::native(Filename, NativeFilename); | |
417 auto CovFileName = InvRemappedFilenames.find(NativeFilename); | |
418 if (CovFileName != InvRemappedFilenames.end()) | |
419 Filename = CovFileName->second; | |
420 } | |
421 } | |
422 | |
352 void CodeCoverageTool::removeUnmappedInputs(const CoverageMapping &Coverage) { | 423 void CodeCoverageTool::removeUnmappedInputs(const CoverageMapping &Coverage) { |
353 std::vector<StringRef> CoveredFiles = Coverage.getUniqueSourceFiles(); | 424 std::vector<StringRef> CoveredFiles = Coverage.getUniqueSourceFiles(); |
354 | 425 |
355 auto UncoveredFilesIt = SourceFiles.end(); | 426 auto UncoveredFilesIt = SourceFiles.end(); |
356 if (!CompareFilenamesOnly) { | 427 // The user may have specified source files which aren't in the coverage |
357 // The user may have specified source files which aren't in the coverage | 428 // mapping. Filter these files away. |
358 // mapping. Filter these files away. | 429 UncoveredFilesIt = std::remove_if( |
359 UncoveredFilesIt = std::remove_if( | 430 SourceFiles.begin(), SourceFiles.end(), [&](const std::string &SF) { |
360 SourceFiles.begin(), SourceFiles.end(), [&](const std::string &SF) { | 431 return !std::binary_search(CoveredFiles.begin(), CoveredFiles.end(), |
361 return !std::binary_search(CoveredFiles.begin(), CoveredFiles.end(), | 432 SF); |
362 SF); | 433 }); |
363 }); | |
364 } else { | |
365 for (auto &SF : SourceFiles) { | |
366 StringRef SFBase = sys::path::filename(SF); | |
367 for (const auto &CF : CoveredFiles) { | |
368 if (SFBase == sys::path::filename(CF)) { | |
369 RemappedFilenames[CF] = SF; | |
370 SF = CF; | |
371 break; | |
372 } | |
373 } | |
374 } | |
375 UncoveredFilesIt = std::remove_if( | |
376 SourceFiles.begin(), SourceFiles.end(), | |
377 [&](const std::string &SF) { return !RemappedFilenames.count(SF); }); | |
378 } | |
379 | 434 |
380 SourceFiles.erase(UncoveredFilesIt, SourceFiles.end()); | 435 SourceFiles.erase(UncoveredFilesIt, SourceFiles.end()); |
381 } | 436 } |
382 | 437 |
383 void CodeCoverageTool::demangleSymbols(const CoverageMapping &Coverage) { | 438 void CodeCoverageTool::demangleSymbols(const CoverageMapping &Coverage) { |
391 sys::fs::createTemporaryFile("demangle-in", "list", InputFD, InputPath); | 446 sys::fs::createTemporaryFile("demangle-in", "list", InputFD, InputPath); |
392 if (EC) { | 447 if (EC) { |
393 error(InputPath, EC.message()); | 448 error(InputPath, EC.message()); |
394 return; | 449 return; |
395 } | 450 } |
396 tool_output_file InputTOF{InputPath, InputFD}; | 451 ToolOutputFile InputTOF{InputPath, InputFD}; |
397 | 452 |
398 unsigned NumSymbols = 0; | 453 unsigned NumSymbols = 0; |
399 for (const auto &Function : Coverage.getCoveredFunctions()) { | 454 for (const auto &Function : Coverage.getCoveredFunctions()) { |
400 InputTOF.os() << Function.Name << '\n'; | 455 InputTOF.os() << Function.Name << '\n'; |
401 ++NumSymbols; | 456 ++NumSymbols; |
409 OutputPath); | 464 OutputPath); |
410 if (EC) { | 465 if (EC) { |
411 error(OutputPath, EC.message()); | 466 error(OutputPath, EC.message()); |
412 return; | 467 return; |
413 } | 468 } |
414 tool_output_file OutputTOF{OutputPath, OutputFD}; | 469 ToolOutputFile OutputTOF{OutputPath, OutputFD}; |
415 OutputTOF.os().close(); | 470 OutputTOF.os().close(); |
416 | 471 |
417 // Invoke the demangler. | 472 // Invoke the demangler. |
418 std::vector<const char *> ArgsV; | 473 std::vector<const char *> ArgsV; |
419 for (const std::string &Arg : ViewOpts.DemanglerOpts) | 474 for (const std::string &Arg : ViewOpts.DemanglerOpts) |
420 ArgsV.push_back(Arg.c_str()); | 475 ArgsV.push_back(Arg.c_str()); |
421 ArgsV.push_back(nullptr); | 476 ArgsV.push_back(nullptr); |
422 StringRef InputPathRef = InputPath.str(); | 477 Optional<StringRef> Redirects[] = {InputPath.str(), OutputPath.str(), {""}}; |
423 StringRef OutputPathRef = OutputPath.str(); | |
424 StringRef StderrRef; | |
425 const StringRef *Redirects[] = {&InputPathRef, &OutputPathRef, &StderrRef}; | |
426 std::string ErrMsg; | 478 std::string ErrMsg; |
427 int RC = sys::ExecuteAndWait(ViewOpts.DemanglerOpts[0], ArgsV.data(), | 479 int RC = sys::ExecuteAndWait(ViewOpts.DemanglerOpts[0], ArgsV.data(), |
428 /*env=*/nullptr, Redirects, /*secondsToWait=*/0, | 480 /*env=*/nullptr, Redirects, /*secondsToWait=*/0, |
429 /*memoryLimit=*/0, &ErrMsg); | 481 /*memoryLimit=*/0, &ErrMsg); |
430 if (RC) { | 482 if (RC) { |
451 } | 503 } |
452 | 504 |
453 // Cache the demangled names. | 505 // Cache the demangled names. |
454 unsigned I = 0; | 506 unsigned I = 0; |
455 for (const auto &Function : Coverage.getCoveredFunctions()) | 507 for (const auto &Function : Coverage.getCoveredFunctions()) |
456 DemangledNames[Function.Name] = Symbols[I++]; | 508 // On Windows, lines in the demangler's output file end with "\r\n". |
457 } | 509 // Splitting by '\n' keeps '\r's, so cut them now. |
458 | 510 DC.DemangledNames[Function.Name] = Symbols[I++].rtrim(); |
459 StringRef CodeCoverageTool::getSymbolForHumans(StringRef Sym) const { | |
460 const auto DemangledName = DemangledNames.find(Sym); | |
461 if (DemangledName == DemangledNames.end()) | |
462 return Sym; | |
463 return DemangledName->getValue(); | |
464 } | 511 } |
465 | 512 |
466 void CodeCoverageTool::writeSourceFileView(StringRef SourceFile, | 513 void CodeCoverageTool::writeSourceFileView(StringRef SourceFile, |
467 CoverageMapping *Coverage, | 514 CoverageMapping *Coverage, |
468 CoveragePrinter *Printer, | 515 CoveragePrinter *Printer, |
479 return; | 526 return; |
480 } | 527 } |
481 auto OS = std::move(OSOrErr.get()); | 528 auto OS = std::move(OSOrErr.get()); |
482 | 529 |
483 View->print(*OS.get(), /*Wholefile=*/true, | 530 View->print(*OS.get(), /*Wholefile=*/true, |
484 /*ShowSourceName=*/ShowFilenames); | 531 /*ShowSourceName=*/ShowFilenames, |
532 /*ShowTitle=*/ViewOpts.hasOutputDirectory()); | |
485 Printer->closeViewFile(std::move(OS)); | 533 Printer->closeViewFile(std::move(OS)); |
486 } | 534 } |
487 | 535 |
488 int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { | 536 int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { |
489 cl::opt<std::string> CovFilename( | 537 cl::opt<std::string> CovFilename( |
503 cl::opt<std::string, true> PGOFilename( | 551 cl::opt<std::string, true> PGOFilename( |
504 "instr-profile", cl::Required, cl::location(this->PGOFilename), | 552 "instr-profile", cl::Required, cl::location(this->PGOFilename), |
505 cl::desc( | 553 cl::desc( |
506 "File with the profile data obtained after an instrumented run")); | 554 "File with the profile data obtained after an instrumented run")); |
507 | 555 |
508 cl::opt<std::string> Arch( | 556 cl::list<std::string> Arches( |
509 "arch", cl::desc("architecture of the coverage mapping binary")); | 557 "arch", cl::desc("architectures of the coverage mapping binaries")); |
510 | 558 |
511 cl::opt<bool> DebugDump("dump", cl::Optional, | 559 cl::opt<bool> DebugDump("dump", cl::Optional, |
512 cl::desc("Show internal debug dump")); | 560 cl::desc("Show internal debug dump")); |
513 | 561 |
514 cl::opt<CoverageViewOptions::OutputFormat> Format( | 562 cl::opt<CoverageViewOptions::OutputFormat> Format( |
517 "Text output"), | 565 "Text output"), |
518 clEnumValN(CoverageViewOptions::OutputFormat::HTML, "html", | 566 clEnumValN(CoverageViewOptions::OutputFormat::HTML, "html", |
519 "HTML output")), | 567 "HTML output")), |
520 cl::init(CoverageViewOptions::OutputFormat::Text)); | 568 cl::init(CoverageViewOptions::OutputFormat::Text)); |
521 | 569 |
522 cl::opt<bool> FilenameEquivalence( | 570 cl::opt<std::string> PathRemap( |
523 "filename-equivalence", cl::Optional, | 571 "path-equivalence", cl::Optional, |
524 cl::desc("Treat source files as equivalent to paths in the coverage data " | 572 cl::desc("<from>,<to> Map coverage data paths to local source file " |
525 "when the file names match, even if the full paths do not")); | 573 "paths")); |
526 | 574 |
527 cl::OptionCategory FilteringCategory("Function filtering options"); | 575 cl::OptionCategory FilteringCategory("Function filtering options"); |
528 | 576 |
529 cl::list<std::string> NameFilters( | 577 cl::list<std::string> NameFilters( |
530 "name", cl::Optional, | 578 "name", cl::Optional, |
531 cl::desc("Show code coverage only for functions with the given name"), | 579 cl::desc("Show code coverage only for functions with the given name"), |
580 cl::ZeroOrMore, cl::cat(FilteringCategory)); | |
581 | |
582 cl::list<std::string> NameFilterFiles( | |
583 "name-whitelist", cl::Optional, | |
584 cl::desc("Show code coverage only for functions listed in the given " | |
585 "file"), | |
532 cl::ZeroOrMore, cl::cat(FilteringCategory)); | 586 cl::ZeroOrMore, cl::cat(FilteringCategory)); |
533 | 587 |
534 cl::list<std::string> NameRegexFilters( | 588 cl::list<std::string> NameRegexFilters( |
535 "name-regex", cl::Optional, | 589 "name-regex", cl::Optional, |
536 cl::desc("Show code coverage only for functions that match the given " | 590 cl::desc("Show code coverage only for functions that match the given " |
566 cl::init(cl::BOU_UNSET)); | 620 cl::init(cl::BOU_UNSET)); |
567 | 621 |
568 cl::list<std::string> DemanglerOpts( | 622 cl::list<std::string> DemanglerOpts( |
569 "Xdemangler", cl::desc("<demangler-path>|<demangler-option>")); | 623 "Xdemangler", cl::desc("<demangler-path>|<demangler-option>")); |
570 | 624 |
625 cl::opt<bool> RegionSummary( | |
626 "show-region-summary", cl::Optional, | |
627 cl::desc("Show region statistics in summary table"), | |
628 cl::init(true)); | |
629 | |
630 cl::opt<bool> InstantiationSummary( | |
631 "show-instantiation-summary", cl::Optional, | |
632 cl::desc("Show instantiation statistics in summary table")); | |
633 | |
571 auto commandLineParser = [&, this](int argc, const char **argv) -> int { | 634 auto commandLineParser = [&, this](int argc, const char **argv) -> int { |
572 cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n"); | 635 cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n"); |
573 ViewOpts.Debug = DebugDump; | 636 ViewOpts.Debug = DebugDump; |
574 CompareFilenamesOnly = FilenameEquivalence; | |
575 | 637 |
576 if (!CovFilename.empty()) | 638 if (!CovFilename.empty()) |
577 ObjectFilenames.emplace_back(CovFilename); | 639 ObjectFilenames.emplace_back(CovFilename); |
578 for (const std::string &Filename : CovFilenames) | 640 for (const std::string &Filename : CovFilenames) |
579 ObjectFilenames.emplace_back(Filename); | 641 ObjectFilenames.emplace_back(Filename); |
594 errs() << "Color output cannot be disabled when generating html.\n"; | 656 errs() << "Color output cannot be disabled when generating html.\n"; |
595 ViewOpts.Colors = true; | 657 ViewOpts.Colors = true; |
596 break; | 658 break; |
597 } | 659 } |
598 | 660 |
661 // If path-equivalence was given and is a comma seperated pair then set | |
662 // PathRemapping. | |
663 auto EquivPair = StringRef(PathRemap).split(','); | |
664 if (!(EquivPair.first.empty() && EquivPair.second.empty())) | |
665 PathRemapping = EquivPair; | |
666 | |
599 // If a demangler is supplied, check if it exists and register it. | 667 // If a demangler is supplied, check if it exists and register it. |
600 if (DemanglerOpts.size()) { | 668 if (DemanglerOpts.size()) { |
601 auto DemanglerPathOrErr = sys::findProgramByName(DemanglerOpts[0]); | 669 auto DemanglerPathOrErr = sys::findProgramByName(DemanglerOpts[0]); |
602 if (!DemanglerPathOrErr) { | 670 if (!DemanglerPathOrErr) { |
603 error("Could not find the demangler!", | 671 error("Could not find the demangler!", |
606 } | 674 } |
607 DemanglerOpts[0] = *DemanglerPathOrErr; | 675 DemanglerOpts[0] = *DemanglerPathOrErr; |
608 ViewOpts.DemanglerOpts.swap(DemanglerOpts); | 676 ViewOpts.DemanglerOpts.swap(DemanglerOpts); |
609 } | 677 } |
610 | 678 |
679 // Read in -name-whitelist files. | |
680 if (!NameFilterFiles.empty()) { | |
681 std::string SpecialCaseListErr; | |
682 NameWhitelist = | |
683 SpecialCaseList::create(NameFilterFiles, SpecialCaseListErr); | |
684 if (!NameWhitelist) | |
685 error(SpecialCaseListErr); | |
686 } | |
687 | |
611 // Create the function filters | 688 // Create the function filters |
612 if (!NameFilters.empty() || !NameRegexFilters.empty()) { | 689 if (!NameFilters.empty() || NameWhitelist || !NameRegexFilters.empty()) { |
613 auto NameFilterer = new CoverageFilters; | 690 auto NameFilterer = llvm::make_unique<CoverageFilters>(); |
614 for (const auto &Name : NameFilters) | 691 for (const auto &Name : NameFilters) |
615 NameFilterer->push_back(llvm::make_unique<NameCoverageFilter>(Name)); | 692 NameFilterer->push_back(llvm::make_unique<NameCoverageFilter>(Name)); |
693 if (NameWhitelist) | |
694 NameFilterer->push_back( | |
695 llvm::make_unique<NameWhitelistCoverageFilter>(*NameWhitelist)); | |
616 for (const auto &Regex : NameRegexFilters) | 696 for (const auto &Regex : NameRegexFilters) |
617 NameFilterer->push_back( | 697 NameFilterer->push_back( |
618 llvm::make_unique<NameRegexCoverageFilter>(Regex)); | 698 llvm::make_unique<NameRegexCoverageFilter>(Regex)); |
619 Filters.push_back(std::unique_ptr<CoverageFilter>(NameFilterer)); | 699 Filters.push_back(std::move(NameFilterer)); |
620 } | 700 } |
621 if (RegionCoverageLtFilter.getNumOccurrences() || | 701 if (RegionCoverageLtFilter.getNumOccurrences() || |
622 RegionCoverageGtFilter.getNumOccurrences() || | 702 RegionCoverageGtFilter.getNumOccurrences() || |
623 LineCoverageLtFilter.getNumOccurrences() || | 703 LineCoverageLtFilter.getNumOccurrences() || |
624 LineCoverageGtFilter.getNumOccurrences()) { | 704 LineCoverageGtFilter.getNumOccurrences()) { |
625 auto StatFilterer = new CoverageFilters; | 705 auto StatFilterer = llvm::make_unique<CoverageFilters>(); |
626 if (RegionCoverageLtFilter.getNumOccurrences()) | 706 if (RegionCoverageLtFilter.getNumOccurrences()) |
627 StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>( | 707 StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>( |
628 RegionCoverageFilter::LessThan, RegionCoverageLtFilter)); | 708 RegionCoverageFilter::LessThan, RegionCoverageLtFilter)); |
629 if (RegionCoverageGtFilter.getNumOccurrences()) | 709 if (RegionCoverageGtFilter.getNumOccurrences()) |
630 StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>( | 710 StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>( |
633 StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>( | 713 StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>( |
634 LineCoverageFilter::LessThan, LineCoverageLtFilter)); | 714 LineCoverageFilter::LessThan, LineCoverageLtFilter)); |
635 if (LineCoverageGtFilter.getNumOccurrences()) | 715 if (LineCoverageGtFilter.getNumOccurrences()) |
636 StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>( | 716 StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>( |
637 RegionCoverageFilter::GreaterThan, LineCoverageGtFilter)); | 717 RegionCoverageFilter::GreaterThan, LineCoverageGtFilter)); |
638 Filters.push_back(std::unique_ptr<CoverageFilter>(StatFilterer)); | 718 Filters.push_back(std::move(StatFilterer)); |
639 } | 719 } |
640 | 720 |
641 if (!Arch.empty() && | 721 if (!Arches.empty()) { |
642 Triple(Arch).getArch() == llvm::Triple::ArchType::UnknownArch) { | 722 for (const std::string &Arch : Arches) { |
643 error("Unknown architecture: " + Arch); | 723 if (Triple(Arch).getArch() == llvm::Triple::ArchType::UnknownArch) { |
644 return 1; | 724 error("Unknown architecture: " + Arch); |
645 } | 725 return 1; |
646 CoverageArch = Arch; | 726 } |
727 CoverageArches.emplace_back(Arch); | |
728 } | |
729 if (CoverageArches.size() != ObjectFilenames.size()) { | |
730 error("Number of architectures doesn't match the number of objects"); | |
731 return 1; | |
732 } | |
733 } | |
647 | 734 |
648 for (const std::string &File : InputSourceFiles) | 735 for (const std::string &File : InputSourceFiles) |
649 collectPaths(File); | 736 collectPaths(File); |
650 | 737 |
651 if (DebugDumpCollectedPaths) { | 738 if (DebugDumpCollectedPaths) { |
652 for (const std::string &SF : SourceFiles) | 739 for (const std::string &SF : SourceFiles) |
653 outs() << SF << '\n'; | 740 outs() << SF << '\n'; |
654 ::exit(0); | 741 ::exit(0); |
655 } | 742 } |
743 | |
744 ViewOpts.ShowRegionSummary = RegionSummary; | |
745 ViewOpts.ShowInstantiationSummary = InstantiationSummary; | |
656 | 746 |
657 return 0; | 747 return 0; |
658 }; | 748 }; |
659 | 749 |
660 switch (Cmd) { | 750 switch (Cmd) { |
693 cl::desc("Show expanded source regions"), | 783 cl::desc("Show expanded source regions"), |
694 cl::cat(ViewCategory)); | 784 cl::cat(ViewCategory)); |
695 | 785 |
696 cl::opt<bool> ShowInstantiations("show-instantiations", cl::Optional, | 786 cl::opt<bool> ShowInstantiations("show-instantiations", cl::Optional, |
697 cl::desc("Show function instantiations"), | 787 cl::desc("Show function instantiations"), |
698 cl::cat(ViewCategory)); | 788 cl::init(true), cl::cat(ViewCategory)); |
699 | 789 |
700 cl::opt<std::string> ShowOutputDirectory( | 790 cl::opt<std::string> ShowOutputDirectory( |
701 "output-dir", cl::init(""), | 791 "output-dir", cl::init(""), |
702 cl::desc("Directory in which coverage information is written out")); | 792 cl::desc("Directory in which coverage information is written out")); |
703 cl::alias ShowOutputDirectoryA("o", cl::desc("Alias for --output-dir"), | 793 cl::alias ShowOutputDirectoryA("o", cl::desc("Alias for --output-dir"), |
710 | 800 |
711 cl::opt<std::string> ProjectTitle( | 801 cl::opt<std::string> ProjectTitle( |
712 "project-title", cl::Optional, | 802 "project-title", cl::Optional, |
713 cl::desc("Set project title for the coverage report")); | 803 cl::desc("Set project title for the coverage report")); |
714 | 804 |
805 cl::opt<unsigned> NumThreads( | |
806 "num-threads", cl::init(0), | |
807 cl::desc("Number of merge threads to use (default: autodetect)")); | |
808 cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"), | |
809 cl::aliasopt(NumThreads)); | |
810 | |
715 auto Err = commandLineParser(argc, argv); | 811 auto Err = commandLineParser(argc, argv); |
716 if (Err) | 812 if (Err) |
717 return Err; | 813 return Err; |
718 | 814 |
719 ViewOpts.ShowLineNumbers = true; | 815 ViewOpts.ShowLineNumbers = true; |
720 ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 || | 816 ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 || |
721 !ShowRegions || ShowBestLineRegionsCounts; | 817 !ShowRegions || ShowBestLineRegionsCounts; |
722 ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts; | 818 ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts; |
723 ViewOpts.ShowLineStatsOrRegionMarkers = ShowBestLineRegionsCounts; | |
724 ViewOpts.ShowExpandedRegions = ShowExpansions; | 819 ViewOpts.ShowExpandedRegions = ShowExpansions; |
725 ViewOpts.ShowFunctionInstantiations = ShowInstantiations; | 820 ViewOpts.ShowFunctionInstantiations = ShowInstantiations; |
726 ViewOpts.ShowOutputDirectory = ShowOutputDirectory; | 821 ViewOpts.ShowOutputDirectory = ShowOutputDirectory; |
727 ViewOpts.TabSize = TabSize; | 822 ViewOpts.TabSize = TabSize; |
728 ViewOpts.ProjectTitle = ProjectTitle; | 823 ViewOpts.ProjectTitle = ProjectTitle; |
740 return 1; | 835 return 1; |
741 } | 836 } |
742 | 837 |
743 auto ModifiedTime = Status.getLastModificationTime(); | 838 auto ModifiedTime = Status.getLastModificationTime(); |
744 std::string ModifiedTimeStr = to_string(ModifiedTime); | 839 std::string ModifiedTimeStr = to_string(ModifiedTime); |
745 size_t found = ModifiedTimeStr.rfind(":"); | 840 size_t found = ModifiedTimeStr.rfind(':'); |
746 ViewOpts.CreatedTimeStr = (found != std::string::npos) | 841 ViewOpts.CreatedTimeStr = (found != std::string::npos) |
747 ? "Created: " + ModifiedTimeStr.substr(0, found) | 842 ? "Created: " + ModifiedTimeStr.substr(0, found) |
748 : "Created: " + ModifiedTimeStr; | 843 : "Created: " + ModifiedTimeStr; |
749 | 844 |
750 auto Coverage = load(); | 845 auto Coverage = load(); |
751 if (!Coverage) | 846 if (!Coverage) |
752 return 1; | 847 return 1; |
753 | 848 |
754 auto Printer = CoveragePrinter::create(ViewOpts); | 849 auto Printer = CoveragePrinter::create(ViewOpts); |
755 | 850 |
851 if (SourceFiles.empty()) | |
852 // Get the source files from the function coverage mapping. | |
853 for (StringRef Filename : Coverage->getUniqueSourceFiles()) | |
854 SourceFiles.push_back(Filename); | |
855 | |
856 // Create an index out of the source files. | |
857 if (ViewOpts.hasOutputDirectory()) { | |
858 if (Error E = Printer->createIndexFile(SourceFiles, *Coverage, Filters)) { | |
859 error("Could not create index file!", toString(std::move(E))); | |
860 return 1; | |
861 } | |
862 } | |
863 | |
756 if (!Filters.empty()) { | 864 if (!Filters.empty()) { |
757 auto OSOrErr = Printer->createViewFile("functions", /*InToplevel=*/true); | 865 // Build the map of filenames to functions. |
758 if (Error E = OSOrErr.takeError()) { | 866 std::map<llvm::StringRef, std::vector<const FunctionRecord *>> |
759 error("Could not create view file!", toString(std::move(E))); | 867 FilenameFunctionMap; |
760 return 1; | 868 for (const auto &SourceFile : SourceFiles) |
761 } | 869 for (const auto &Function : Coverage->getCoveredFunctions(SourceFile)) |
762 auto OS = std::move(OSOrErr.get()); | 870 if (Filters.matches(*Coverage.get(), Function)) |
763 | 871 FilenameFunctionMap[SourceFile].push_back(&Function); |
764 // Show functions. | 872 |
765 for (const auto &Function : Coverage->getCoveredFunctions()) { | 873 // Only print filter matching functions for each file. |
766 if (!Filters.matches(Function)) | 874 for (const auto &FileFunc : FilenameFunctionMap) { |
767 continue; | 875 StringRef File = FileFunc.first; |
768 | 876 const auto &Functions = FileFunc.second; |
769 auto mainView = createFunctionView(Function, *Coverage); | 877 |
770 if (!mainView) { | 878 auto OSOrErr = Printer->createViewFile(File, /*InToplevel=*/false); |
771 warning("Could not read coverage for '" + Function.Name + "'."); | 879 if (Error E = OSOrErr.takeError()) { |
772 continue; | 880 error("Could not create view file!", toString(std::move(E))); |
881 return 1; | |
773 } | 882 } |
774 | 883 auto OS = std::move(OSOrErr.get()); |
775 mainView->print(*OS.get(), /*WholeFile=*/false, /*ShowSourceName=*/true); | 884 |
776 } | 885 bool ShowTitle = ViewOpts.hasOutputDirectory(); |
777 | 886 for (const auto *Function : Functions) { |
778 Printer->closeViewFile(std::move(OS)); | 887 auto FunctionView = createFunctionView(*Function, *Coverage); |
888 if (!FunctionView) { | |
889 warning("Could not read coverage for '" + Function->Name + "'."); | |
890 continue; | |
891 } | |
892 FunctionView->print(*OS.get(), /*WholeFile=*/false, | |
893 /*ShowSourceName=*/true, ShowTitle); | |
894 ShowTitle = false; | |
895 } | |
896 | |
897 Printer->closeViewFile(std::move(OS)); | |
898 } | |
779 return 0; | 899 return 0; |
780 } | 900 } |
781 | 901 |
782 // Show files | 902 // Show files |
783 bool ShowFilenames = | 903 bool ShowFilenames = |
784 (SourceFiles.size() != 1) || ViewOpts.hasOutputDirectory() || | 904 (SourceFiles.size() != 1) || ViewOpts.hasOutputDirectory() || |
785 (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML); | 905 (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML); |
786 | 906 |
787 if (SourceFiles.empty()) | 907 // If NumThreads is not specified, auto-detect a good default. |
788 // Get the source files from the function coverage mapping. | 908 if (NumThreads == 0) |
789 for (StringRef Filename : Coverage->getUniqueSourceFiles()) | 909 NumThreads = |
790 SourceFiles.push_back(Filename); | 910 std::max(1U, std::min(llvm::heavyweight_hardware_concurrency(), |
791 | 911 unsigned(SourceFiles.size()))); |
792 // Create an index out of the source files. | 912 |
793 if (ViewOpts.hasOutputDirectory()) { | 913 if (!ViewOpts.hasOutputDirectory() || NumThreads == 1) { |
794 if (Error E = Printer->createIndexFile(SourceFiles, *Coverage)) { | |
795 error("Could not create index file!", toString(std::move(E))); | |
796 return 1; | |
797 } | |
798 } | |
799 | |
800 // FIXME: Sink the hardware_concurrency() == 1 check into ThreadPool. | |
801 if (!ViewOpts.hasOutputDirectory() || | |
802 std::thread::hardware_concurrency() == 1) { | |
803 for (const std::string &SourceFile : SourceFiles) | 914 for (const std::string &SourceFile : SourceFiles) |
804 writeSourceFileView(SourceFile, Coverage.get(), Printer.get(), | 915 writeSourceFileView(SourceFile, Coverage.get(), Printer.get(), |
805 ShowFilenames); | 916 ShowFilenames); |
806 } else { | 917 } else { |
807 // In -output-dir mode, it's safe to use multiple threads to print files. | 918 // In -output-dir mode, it's safe to use multiple threads to print files. |
808 ThreadPool Pool; | 919 ThreadPool Pool(NumThreads); |
809 for (const std::string &SourceFile : SourceFiles) | 920 for (const std::string &SourceFile : SourceFiles) |
810 Pool.async(&CodeCoverageTool::writeSourceFileView, this, SourceFile, | 921 Pool.async(&CodeCoverageTool::writeSourceFileView, this, SourceFile, |
811 Coverage.get(), Printer.get(), ShowFilenames); | 922 Coverage.get(), Printer.get(), ShowFilenames); |
812 Pool.wait(); | 923 Pool.wait(); |
813 } | 924 } |
815 return 0; | 926 return 0; |
816 } | 927 } |
817 | 928 |
818 int CodeCoverageTool::report(int argc, const char **argv, | 929 int CodeCoverageTool::report(int argc, const char **argv, |
819 CommandLineParserType commandLineParser) { | 930 CommandLineParserType commandLineParser) { |
931 cl::opt<bool> ShowFunctionSummaries( | |
932 "show-functions", cl::Optional, cl::init(false), | |
933 cl::desc("Show coverage summaries for each function")); | |
934 | |
820 auto Err = commandLineParser(argc, argv); | 935 auto Err = commandLineParser(argc, argv); |
821 if (Err) | 936 if (Err) |
822 return Err; | 937 return Err; |
823 | 938 |
824 if (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML) | 939 if (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML) { |
825 error("HTML output for summary reports is not yet supported."); | 940 error("HTML output for summary reports is not yet supported."); |
941 return 1; | |
942 } | |
826 | 943 |
827 auto Coverage = load(); | 944 auto Coverage = load(); |
828 if (!Coverage) | 945 if (!Coverage) |
829 return 1; | 946 return 1; |
830 | 947 |
831 CoverageReport Report(ViewOpts, *Coverage.get()); | 948 CoverageReport Report(ViewOpts, *Coverage.get()); |
832 if (SourceFiles.empty()) | 949 if (!ShowFunctionSummaries) { |
833 Report.renderFileReports(llvm::outs()); | 950 if (SourceFiles.empty()) |
834 else | 951 Report.renderFileReports(llvm::outs()); |
835 Report.renderFunctionReports(SourceFiles, llvm::outs()); | 952 else |
953 Report.renderFileReports(llvm::outs(), SourceFiles); | |
954 } else { | |
955 if (SourceFiles.empty()) { | |
956 error("Source files must be specified when -show-functions=true is " | |
957 "specified"); | |
958 return 1; | |
959 } | |
960 | |
961 Report.renderFunctionReports(SourceFiles, DC, llvm::outs()); | |
962 } | |
836 return 0; | 963 return 0; |
837 } | 964 } |
838 | 965 |
839 int CodeCoverageTool::export_(int argc, const char **argv, | 966 int CodeCoverageTool::export_(int argc, const char **argv, |
840 CommandLineParserType commandLineParser) { | 967 CommandLineParserType commandLineParser) { |
841 | 968 |
842 auto Err = commandLineParser(argc, argv); | 969 auto Err = commandLineParser(argc, argv); |
843 if (Err) | 970 if (Err) |
844 return Err; | 971 return Err; |
972 | |
973 if (ViewOpts.Format != CoverageViewOptions::OutputFormat::Text) { | |
974 error("Coverage data can only be exported as textual JSON."); | |
975 return 1; | |
976 } | |
845 | 977 |
846 auto Coverage = load(); | 978 auto Coverage = load(); |
847 if (!Coverage) { | 979 if (!Coverage) { |
848 error("Could not load coverage information"); | 980 error("Could not load coverage information"); |
849 return 1; | 981 return 1; |
850 } | 982 } |
851 | 983 |
852 exportCoverageDataToJson(*Coverage.get(), outs()); | 984 exportCoverageDataToJson(*Coverage.get(), ViewOpts, outs()); |
853 | 985 |
854 return 0; | 986 return 0; |
855 } | 987 } |
856 | 988 |
857 int showMain(int argc, const char *argv[]) { | 989 int showMain(int argc, const char *argv[]) { |