diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2023-02-11 12:38:04 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2023-02-11 12:38:11 +0000 |
commit | e3b557809604d036af6e00c60f012c2025b59a5e (patch) | |
tree | 8a11ba2269a3b669601e2fd41145b174008f4da8 /llvm/tools/llvm-profdata/llvm-profdata.cpp | |
parent | 08e8dd7b9db7bb4a9de26d44c1cbfd24e869c014 (diff) |
Diffstat (limited to 'llvm/tools/llvm-profdata/llvm-profdata.cpp')
-rw-r--r-- | llvm/tools/llvm-profdata/llvm-profdata.cpp | 394 |
1 files changed, 340 insertions, 54 deletions
diff --git a/llvm/tools/llvm-profdata/llvm-profdata.cpp b/llvm/tools/llvm-profdata/llvm-profdata.cpp index 3af8f800adcb..c8e5e6d1ad68 100644 --- a/llvm/tools/llvm-profdata/llvm-profdata.cpp +++ b/llvm/tools/llvm-profdata/llvm-profdata.cpp @@ -13,7 +13,6 @@ #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" -#include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/IR/LLVMContext.h" #include "llvm/Object/Binary.h" #include "llvm/ProfileData/InstrProfCorrelator.h" @@ -31,6 +30,7 @@ #include "llvm/Support/Format.h" #include "llvm/Support/FormattedStream.h" #include "llvm/Support/InitLLVM.h" +#include "llvm/Support/MD5.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/ThreadPool.h" @@ -38,10 +38,16 @@ #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> +#include <cmath> +#include <optional> #include <queue> using namespace llvm; +// We use this string to indicate that there are +// multiple static functions map to the same name. +const std::string DuplicateNameStr = "----"; + enum ProfileFormat { PF_None = 0, PF_Text, @@ -51,6 +57,8 @@ enum ProfileFormat { PF_Binary }; +enum class ShowFormat { Text, Json, Yaml }; + static void warn(Twine Message, std::string Whence = "", std::string Hint = "") { WithColor::warning(); @@ -329,9 +337,16 @@ static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper, FuncName, firstTime); }); } - if (Reader->hasError()) + + if (Reader->hasError()) { if (Error E = Reader->getError()) WC->Errors.emplace_back(std::move(E), Filename); + } + + std::vector<llvm::object::BuildID> BinaryIds; + if (Error E = Reader->readBinaryIds(BinaryIds)) + WC->Errors.emplace_back(std::move(E), Filename); + WC->Writer.addBinaryIds(BinaryIds); } /// Merge the \p Src writer context into \p Dst. @@ -340,6 +355,9 @@ static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) { Dst->Errors.push_back(std::move(ErrorPair)); Src->Errors.clear(); + if (Error E = Dst->Writer.mergeProfileKind(Src->Writer.getProfileKind())) + exitWithError(std::move(E)); + Dst->Writer.mergeRecordsFromWriter(std::move(Src->Writer), [&](Error E) { instrprof_error IPE = InstrProfError::take(std::move(E)); std::unique_lock<std::mutex> ErrGuard{Dst->ErrLock}; @@ -397,9 +415,6 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs, if (NumThreads == 0) NumThreads = std::min(hardware_concurrency().compute_thread_count(), unsigned((Inputs.size() + 1) / 2)); - // FIXME: There's a bug here, where setting NumThreads = Inputs.size() fails - // the merge_empty_profile.test because the InstrProfWriter.ProfileKind isn't - // merged, thus the emitted file ends up with a PF_Unknown kind. // Initialize the writer contexts. SmallVector<std::unique_ptr<WriterContext>, 4> Contexts; @@ -461,6 +476,7 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs, /// The profile entry for a function in instrumentation profile. struct InstrProfileEntry { uint64_t MaxCount = 0; + uint64_t NumEdgeCounters = 0; float ZeroCounterRatio = 0.0; InstrProfRecord *ProfRecord; InstrProfileEntry(InstrProfRecord *Record); @@ -476,33 +492,49 @@ InstrProfileEntry::InstrProfileEntry(InstrProfRecord *Record) { ZeroCntNum += !Record->Counts[I]; } ZeroCounterRatio = (float)ZeroCntNum / CntNum; + NumEdgeCounters = CntNum; } -/// Either set all the counters in the instr profile entry \p IFE to -1 -/// in order to drop the profile or scale up the counters in \p IFP to -/// be above hot threshold. We use the ratio of zero counters in the -/// profile of a function to decide the profile is helpful or harmful -/// for performance, and to choose whether to scale up or drop it. -static void updateInstrProfileEntry(InstrProfileEntry &IFE, +/// Either set all the counters in the instr profile entry \p IFE to +/// -1 / -2 /in order to drop the profile or scale up the +/// counters in \p IFP to be above hot / cold threshold. We use +/// the ratio of zero counters in the profile of a function to +/// decide the profile is helpful or harmful for performance, +/// and to choose whether to scale up or drop it. +static void updateInstrProfileEntry(InstrProfileEntry &IFE, bool SetToHot, uint64_t HotInstrThreshold, + uint64_t ColdInstrThreshold, float ZeroCounterThreshold) { InstrProfRecord *ProfRecord = IFE.ProfRecord; if (!IFE.MaxCount || IFE.ZeroCounterRatio > ZeroCounterThreshold) { // If all or most of the counters of the function are zero, the - // profile is unaccountable and shuld be dropped. Reset all the - // counters to be -1 and PGO profile-use will drop the profile. + // profile is unaccountable and should be dropped. Reset all the + // counters to be -1 / -2 and PGO profile-use will drop the profile. // All counters being -1 also implies that the function is hot so // PGO profile-use will also set the entry count metadata to be // above hot threshold. - for (size_t I = 0; I < ProfRecord->Counts.size(); ++I) - ProfRecord->Counts[I] = -1; + // All counters being -2 implies that the function is warm so + // PGO profile-use will also set the entry count metadata to be + // above cold threshold. + auto Kind = + (SetToHot ? InstrProfRecord::PseudoHot : InstrProfRecord::PseudoWarm); + ProfRecord->setPseudoCount(Kind); return; } - // Scale up the MaxCount to be multiple times above hot threshold. + // Scale up the MaxCount to be multiple times above hot / cold threshold. const unsigned MultiplyFactor = 3; - uint64_t Numerator = HotInstrThreshold * MultiplyFactor; + uint64_t Threshold = (SetToHot ? HotInstrThreshold : ColdInstrThreshold); + uint64_t Numerator = Threshold * MultiplyFactor; + + // Make sure Threshold for warm counters is below the HotInstrThreshold. + if (!SetToHot && Threshold >= HotInstrThreshold) { + Threshold = (HotInstrThreshold + ColdInstrThreshold) / 2; + } + uint64_t Denominator = IFE.MaxCount; + if (Numerator <= Denominator) + return; ProfRecord->scale(Numerator, Denominator, [&](instrprof_error E) { warn(toString(make_error<InstrProfError>(E))); }); @@ -539,7 +571,167 @@ adjustInstrProfile(std::unique_ptr<WriterContext> &WC, unsigned InstrProfColdThreshold) { // Function to its entry in instr profile. StringMap<InstrProfileEntry> InstrProfileMap; + StringMap<StringRef> StaticFuncMap; InstrProfSummaryBuilder IPBuilder(ProfileSummaryBuilder::DefaultCutoffs); + + auto checkSampleProfileHasFUnique = [&Reader]() { + for (const auto &PD : Reader->getProfiles()) { + auto &FContext = PD.first; + if (FContext.toString().find(FunctionSamples::UniqSuffix) != + std::string::npos) { + return true; + } + } + return false; + }; + + bool SampleProfileHasFUnique = checkSampleProfileHasFUnique(); + + auto buildStaticFuncMap = [&StaticFuncMap, + SampleProfileHasFUnique](const StringRef Name) { + std::string Prefixes[] = {".cpp:", "cc:", ".c:", ".hpp:", ".h:"}; + size_t PrefixPos = StringRef::npos; + for (auto &Prefix : Prefixes) { + PrefixPos = Name.find_insensitive(Prefix); + if (PrefixPos == StringRef::npos) + continue; + PrefixPos += Prefix.size(); + break; + } + + if (PrefixPos == StringRef::npos) { + return; + } + + StringRef NewName = Name.drop_front(PrefixPos); + StringRef FName = Name.substr(0, PrefixPos - 1); + if (NewName.size() == 0) { + return; + } + + // This name should have a static linkage. + size_t PostfixPos = NewName.find(FunctionSamples::UniqSuffix); + bool ProfileHasFUnique = (PostfixPos != StringRef::npos); + + // If sample profile and instrumented profile do not agree on symbol + // uniqification. + if (SampleProfileHasFUnique != ProfileHasFUnique) { + // If instrumented profile uses -funique-internal-linakge-symbols, + // we need to trim the name. + if (ProfileHasFUnique) { + NewName = NewName.substr(0, PostfixPos); + } else { + // If sample profile uses -funique-internal-linakge-symbols, + // we build the map. + std::string NStr = + NewName.str() + getUniqueInternalLinkagePostfix(FName); + NewName = StringRef(NStr); + StaticFuncMap[NewName] = Name; + return; + } + } + + if (StaticFuncMap.find(NewName) == StaticFuncMap.end()) { + StaticFuncMap[NewName] = Name; + } else { + StaticFuncMap[NewName] = DuplicateNameStr; + } + }; + + // We need to flatten the SampleFDO profile as the InstrFDO + // profile does not have inlined callsite profiles. + // One caveat is the pre-inlined function -- their samples + // should be collapsed into the caller function. + // Here we do a DFS traversal to get the flatten profile + // info: the sum of entrycount and the max of maxcount. + // Here is the algorithm: + // recursive (FS, root_name) { + // name = FS->getName(); + // get samples for FS; + // if (InstrProf.find(name) { + // root_name = name; + // } else { + // if (name is in static_func map) { + // root_name = static_name; + // } + // } + // update the Map entry for root_name; + // for (subfs: FS) { + // recursive(subfs, root_name); + // } + // } + // + // Here is an example. + // + // SampleProfile: + // foo:12345:1000 + // 1: 1000 + // 2.1: 1000 + // 15: 5000 + // 4: bar:1000 + // 1: 1000 + // 2: goo:3000 + // 1: 3000 + // 8: bar:40000 + // 1: 10000 + // 2: goo:30000 + // 1: 30000 + // + // InstrProfile has two entries: + // foo + // bar.cc:bar + // + // After BuildMaxSampleMap, we should have the following in FlattenSampleMap: + // {"foo", {1000, 5000}} + // {"bar.cc:bar", {11000, 30000}} + // + // foo's has an entry count of 1000, and max body count of 5000. + // bar.cc:bar has an entry count of 11000 (sum two callsites of 1000 and + // 10000), and max count of 30000 (from the callsite in line 8). + // + // Note that goo's count will remain in bar.cc:bar() as it does not have an + // entry in InstrProfile. + DenseMap<StringRef, std::pair<uint64_t, uint64_t>> FlattenSampleMap; + auto BuildMaxSampleMap = [&FlattenSampleMap, &StaticFuncMap, + &InstrProfileMap](const FunctionSamples &FS, + const StringRef &RootName) { + auto BuildMaxSampleMapImpl = [&](const FunctionSamples &FS, + const StringRef &RootName, + auto &BuildImpl) -> void { + const StringRef &Name = FS.getName(); + const StringRef *NewRootName = &RootName; + uint64_t EntrySample = FS.getHeadSamplesEstimate(); + uint64_t MaxBodySample = FS.getMaxCountInside(/* SkipCallSite*/ true); + + auto It = InstrProfileMap.find(Name); + if (It != InstrProfileMap.end()) { + NewRootName = &Name; + } else { + auto NewName = StaticFuncMap.find(Name); + if (NewName != StaticFuncMap.end()) { + It = InstrProfileMap.find(NewName->second.str()); + if (NewName->second != DuplicateNameStr) { + NewRootName = &NewName->second; + } + } else { + // Here the EntrySample is of an inlined function, so we should not + // update the EntrySample in the map. + EntrySample = 0; + } + } + EntrySample += FlattenSampleMap[*NewRootName].first; + MaxBodySample = + std::max(FlattenSampleMap[*NewRootName].second, MaxBodySample); + FlattenSampleMap[*NewRootName] = + std::make_pair(EntrySample, MaxBodySample); + + for (const auto &C : FS.getCallsiteSamples()) + for (const auto &F : C.second) + BuildImpl(F.second, *NewRootName, BuildImpl); + }; + BuildMaxSampleMapImpl(FS, RootName, BuildMaxSampleMapImpl); + }; + for (auto &PD : WC->Writer.getProfileData()) { // Populate IPBuilder. for (const auto &PDV : PD.getValue()) { @@ -553,13 +745,25 @@ adjustInstrProfile(std::unique_ptr<WriterContext> &WC, // Initialize InstrProfileMap. InstrProfRecord *R = &PD.getValue().begin()->second; - InstrProfileMap[PD.getKey()] = InstrProfileEntry(R); + StringRef FullName = PD.getKey(); + InstrProfileMap[FullName] = InstrProfileEntry(R); + buildStaticFuncMap(FullName); + } + + for (auto &PD : Reader->getProfiles()) { + sampleprof::FunctionSamples &FS = PD.second; + BuildMaxSampleMap(FS, FS.getName()); } ProfileSummary InstrPS = *IPBuilder.getSummary(); ProfileSummary SamplePS = Reader->getSummary(); // Compute cold thresholds for instr profile and sample profile. + uint64_t HotSampleThreshold = + ProfileSummaryBuilder::getEntryForPercentile( + SamplePS.getDetailedSummary(), + ProfileSummaryBuilder::DefaultCutoffs[HotPercentileIdx]) + .MinCount; uint64_t ColdSampleThreshold = ProfileSummaryBuilder::getEntryForPercentile( SamplePS.getDetailedSummary(), @@ -580,17 +784,30 @@ adjustInstrProfile(std::unique_ptr<WriterContext> &WC, // Find hot/warm functions in sample profile which is cold in instr profile // and adjust the profiles of those functions in the instr profile. - for (const auto &PD : Reader->getProfiles()) { - auto &FContext = PD.first; - const sampleprof::FunctionSamples &FS = PD.second; - auto It = InstrProfileMap.find(FContext.toString()); - if (FS.getHeadSamples() > ColdSampleThreshold && - It != InstrProfileMap.end() && - It->second.MaxCount <= ColdInstrThreshold && - FS.getBodySamples().size() >= SupplMinSizeThreshold) { - updateInstrProfileEntry(It->second, HotInstrThreshold, - ZeroCounterThreshold); + for (const auto &E : FlattenSampleMap) { + uint64_t SampleMaxCount = std::max(E.second.first, E.second.second); + if (SampleMaxCount < ColdSampleThreshold) + continue; + const StringRef &Name = E.first; + auto It = InstrProfileMap.find(Name); + if (It == InstrProfileMap.end()) { + auto NewName = StaticFuncMap.find(Name); + if (NewName != StaticFuncMap.end()) { + It = InstrProfileMap.find(NewName->second.str()); + if (NewName->second == DuplicateNameStr) { + WithColor::warning() + << "Static function " << Name + << " has multiple promoted names, cannot adjust profile.\n"; + } + } } + if (It == InstrProfileMap.end() || + It->second.MaxCount > ColdInstrThreshold || + It->second.NumEdgeCounters < SupplMinSizeThreshold) + continue; + bool SetToHot = SampleMaxCount >= HotSampleThreshold; + updateInstrProfileEntry(It->second, SetToHot, HotInstrThreshold, + ColdInstrThreshold, ZeroCounterThreshold); } } @@ -748,14 +965,15 @@ mergeSampleProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper, StringRef ProfileSymbolListFile, bool CompressAllSections, bool UseMD5, bool GenPartialProfile, bool GenCSNestedProfile, bool SampleMergeColdContext, bool SampleTrimColdContext, - bool SampleColdContextFrameDepth, FailureMode FailMode) { + bool SampleColdContextFrameDepth, FailureMode FailMode, + bool DropProfileSymbolList) { using namespace sampleprof; SampleProfileMap ProfileMap; SmallVector<std::unique_ptr<sampleprof::SampleProfileReader>, 5> Readers; LLVMContext Context; sampleprof::ProfileSymbolList WriterList; - Optional<bool> ProfileIsProbeBased; - Optional<bool> ProfileIsCS; + std::optional<bool> ProfileIsProbeBased; + std::optional<bool> ProfileIsCS; for (const auto &Input : Inputs) { auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context, FSDiscriminatorPassOption); @@ -801,10 +1019,12 @@ mergeSampleProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper, } } - std::unique_ptr<sampleprof::ProfileSymbolList> ReaderList = - Reader->getProfileSymbolList(); - if (ReaderList) - WriterList.merge(*ReaderList); + if (!DropProfileSymbolList) { + std::unique_ptr<sampleprof::ProfileSymbolList> ReaderList = + Reader->getProfileSymbolList(); + if (ReaderList) + WriterList.merge(*ReaderList); + } } if (ProfileIsCS && (SampleMergeColdContext || SampleTrimColdContext)) { @@ -1016,6 +1236,10 @@ static int merge_main(int argc, const char *argv[]) { cl::opt<std::string> ProfiledBinary( "profiled-binary", cl::init(""), cl::desc("Path to binary from which the profile was collected.")); + cl::opt<bool> DropProfileSymbolList( + "drop-profile-symbol-list", cl::init(false), cl::Hidden, + cl::desc("Drop the profile symbol list when merging AutoFDO profiles " + "(only meaningful for -sample)")); cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n"); @@ -1060,11 +1284,11 @@ static int merge_main(int argc, const char *argv[]) { OutputFilename, OutputFormat, OutputSparse, NumThreads, FailureMode, ProfiledBinary); else - mergeSampleProfile(WeightedInputs, Remapper.get(), OutputFilename, - OutputFormat, ProfileSymbolListFile, CompressAllSections, - UseMD5, GenPartialProfile, GenCSNestedProfile, - SampleMergeColdContext, SampleTrimColdContext, - SampleColdContextFrameDepth, FailureMode); + mergeSampleProfile( + WeightedInputs, Remapper.get(), OutputFilename, OutputFormat, + ProfileSymbolListFile, CompressAllSections, UseMD5, GenPartialProfile, + GenCSNestedProfile, SampleMergeColdContext, SampleTrimColdContext, + SampleColdContextFrameDepth, FailureMode, DropProfileSymbolList); return 0; } @@ -2142,7 +2366,12 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts, uint64_t ValueCutoff, bool OnlyListBelow, const std::string &ShowFunction, bool TextFormat, bool ShowBinaryIds, bool ShowCovered, + bool ShowProfileVersion, ShowFormat SFormat, raw_fd_ostream &OS) { + if (SFormat == ShowFormat::Json) + exitWithError("JSON output is not supported for instr profiles"); + if (SFormat == ShowFormat::Yaml) + exitWithError("YAML output is not supported for instr profiles"); auto ReaderOrErr = InstrProfReader::create(Filename); std::vector<uint32_t> Cutoffs = std::move(DetailedSummaryCutoffs); if (ShowDetailedSummary && Cutoffs.empty()) { @@ -2207,9 +2436,27 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts, uint64_t FuncMax = 0; uint64_t FuncSum = 0; + + auto PseudoKind = Func.getCountPseudoKind(); + if (PseudoKind != InstrProfRecord::NotPseudo) { + if (Show) { + if (!ShownFunctions) + OS << "Counters:\n"; + ++ShownFunctions; + OS << " " << Func.Name << ":\n" + << " Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n" + << " Counters: " << Func.Counts.size(); + if (PseudoKind == InstrProfRecord::PseudoHot) + OS << " <PseudoHot>\n"; + else if (PseudoKind == InstrProfRecord::PseudoWarm) + OS << " <PseudoWarm>\n"; + else + llvm_unreachable("Unknown PseudoKind"); + } + continue; + } + for (size_t I = 0, E = Func.Counts.size(); I < E; ++I) { - if (Func.Counts[I] == (uint64_t)-1) - continue; FuncMax = std::max(FuncMax, Func.Counts[I]); FuncSum += Func.Counts[I]; } @@ -2334,6 +2581,8 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts, if (Error E = Reader->printBinaryIds(OS)) exitWithError(std::move(E), Filename); + if (ShowProfileVersion) + OS << "Profile version: " << Reader->getVersion() << "\n"; return 0; } @@ -2488,7 +2737,9 @@ static int showSampleProfile(const std::string &Filename, bool ShowCounts, const std::string &ShowFunction, bool ShowProfileSymbolList, bool ShowSectionInfoOnly, bool ShowHotFuncList, - raw_fd_ostream &OS) { + ShowFormat SFormat, raw_fd_ostream &OS) { + if (SFormat == ShowFormat::Yaml) + exitWithError("YAML output is not supported for sample profiles"); using namespace sampleprof; LLVMContext Context; auto ReaderOrErr = @@ -2505,11 +2756,20 @@ static int showSampleProfile(const std::string &Filename, bool ShowCounts, if (std::error_code EC = Reader->read()) exitWithErrorCode(EC, Filename); - if (ShowAllFunctions || ShowFunction.empty()) - Reader->dump(OS); - else + if (ShowAllFunctions || ShowFunction.empty()) { + if (SFormat == ShowFormat::Json) + Reader->dumpJson(OS); + else + Reader->dump(OS); + } else { + if (SFormat == ShowFormat::Json) + exitWithError( + "the JSON format is supported only when all functions are to " + "be printed"); + // TODO: parse context string to support filtering by contexts. Reader->dumpFunctionProfile(StringRef(ShowFunction), OS); + } if (ShowProfileSymbolList) { std::unique_ptr<sampleprof::ProfileSymbolList> ReaderList = @@ -2531,7 +2791,9 @@ static int showSampleProfile(const std::string &Filename, bool ShowCounts, static int showMemProfProfile(const std::string &Filename, const std::string &ProfiledBinary, - raw_fd_ostream &OS) { + ShowFormat SFormat, raw_fd_ostream &OS) { + if (SFormat == ShowFormat::Json) + exitWithError("JSON output is not supported for MemProf"); auto ReaderOr = llvm::memprof::RawMemProfReader::create( Filename, ProfiledBinary, /*KeepNames=*/true); if (Error E = ReaderOr.takeError()) @@ -2550,10 +2812,18 @@ static int showMemProfProfile(const std::string &Filename, static int showDebugInfoCorrelation(const std::string &Filename, bool ShowDetailedSummary, bool ShowProfileSymbolList, - raw_fd_ostream &OS) { + ShowFormat SFormat, raw_fd_ostream &OS) { + if (SFormat == ShowFormat::Json) + exitWithError("JSON output is not supported for debug info correlation"); std::unique_ptr<InstrProfCorrelator> Correlator; if (auto Err = InstrProfCorrelator::get(Filename).moveInto(Correlator)) exitWithError(std::move(Err), Filename); + if (SFormat == ShowFormat::Yaml) { + if (auto Err = Correlator->dumpYaml(OS)) + exitWithError(std::move(Err), Filename); + return 0; + } + if (auto Err = Correlator->correlateProfileData()) exitWithError(std::move(Err), Filename); @@ -2579,9 +2849,20 @@ static int show_main(int argc, const char *argv[]) { cl::opt<bool> ShowCounts("counts", cl::init(false), cl::desc("Show counter values for shown functions")); + cl::opt<ShowFormat> SFormat( + "show-format", cl::init(ShowFormat::Text), + cl::desc("Emit output in the selected format if supported"), + cl::values(clEnumValN(ShowFormat::Text, "text", + "emit normal text output (default)"), + clEnumValN(ShowFormat::Json, "json", "emit JSON"), + clEnumValN(ShowFormat::Yaml, "yaml", "emit YAML"))); + // TODO: Consider replacing this with `--show-format=text-encoding`. cl::opt<bool> TextFormat( "text", cl::init(false), cl::desc("Show instr profile data in text dump format")); + cl::opt<bool> JsonFormat( + "json", cl::desc("Show sample profile data in the JSON format " + "(deprecated, please use --show-format=json)")); cl::opt<bool> ShowIndirectCallTargets( "ic-targets", cl::init(false), cl::desc("Show indirect call site target values for shown functions")); @@ -2646,7 +2927,8 @@ static int show_main(int argc, const char *argv[]) { cl::opt<std::string> ProfiledBinary( "profiled-binary", cl::init(""), cl::desc("Path to binary from which the profile was collected.")); - + cl::opt<bool> ShowProfileVersion("profile-version", cl::init(false), + cl::desc("Show profile version. ")); cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n"); if (Filename.empty() && DebugInfoFilename.empty()) @@ -2659,6 +2941,8 @@ static int show_main(int argc, const char *argv[]) { << ": Input file name cannot be the same as the output file name!\n"; return 1; } + if (JsonFormat) + SFormat = ShowFormat::Json; std::error_code EC; raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_TextWithCRLF); @@ -2670,23 +2954,25 @@ static int show_main(int argc, const char *argv[]) { if (!DebugInfoFilename.empty()) return showDebugInfoCorrelation(DebugInfoFilename, ShowDetailedSummary, - ShowProfileSymbolList, OS); + ShowProfileSymbolList, SFormat, OS); if (ProfileKind == instr) return showInstrProfile( Filename, ShowCounts, TopNFunctions, ShowIndirectCallTargets, ShowMemOPSizes, ShowDetailedSummary, DetailedSummaryCutoffs, ShowAllFunctions, ShowCS, ValueCutoff, OnlyListBelow, ShowFunction, - TextFormat, ShowBinaryIds, ShowCovered, OS); + TextFormat, ShowBinaryIds, ShowCovered, ShowProfileVersion, SFormat, + OS); if (ProfileKind == sample) return showSampleProfile(Filename, ShowCounts, TopNFunctions, ShowAllFunctions, ShowDetailedSummary, ShowFunction, ShowProfileSymbolList, - ShowSectionInfoOnly, ShowHotFuncList, OS); - return showMemProfProfile(Filename, ProfiledBinary, OS); + ShowSectionInfoOnly, ShowHotFuncList, SFormat, OS); + return showMemProfProfile(Filename, ProfiledBinary, SFormat, OS); } -int main(int argc, const char *argv[]) { +int llvm_profdata_main(int argc, char **argvNonConst) { + const char **argv = const_cast<const char **>(argvNonConst); InitLLVM X(argc, argv); StringRef ProgName(sys::path::filename(argv[0])); |