diff options
Diffstat (limited to 'llvm/tools/llvm-profdata/llvm-profdata.cpp')
-rw-r--r-- | llvm/tools/llvm-profdata/llvm-profdata.cpp | 274 |
1 files changed, 220 insertions, 54 deletions
diff --git a/llvm/tools/llvm-profdata/llvm-profdata.cpp b/llvm/tools/llvm-profdata/llvm-profdata.cpp index 41e9abb82b1fc..843f072a61c32 100644 --- a/llvm/tools/llvm-profdata/llvm-profdata.cpp +++ b/llvm/tools/llvm-profdata/llvm-profdata.cpp @@ -23,11 +23,12 @@ #include "llvm/Support/Errc.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" +#include "llvm/Support/FormattedStream.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" -#include "llvm/Support/Threading.h" #include "llvm/Support/ThreadPool.h" +#include "llvm/Support/Threading.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> @@ -70,18 +71,18 @@ static void exitWithError(Error E, StringRef Whence = "") { instrprof_error instrError = IPE.get(); StringRef Hint = ""; if (instrError == instrprof_error::unrecognized_format) { - // Hint for common error of forgetting -sample for sample profiles. - Hint = "Perhaps you forgot to use the -sample option?"; + // Hint for common error of forgetting --sample for sample profiles. + Hint = "Perhaps you forgot to use the --sample option?"; } - exitWithError(IPE.message(), Whence, Hint); + exitWithError(IPE.message(), std::string(Whence), std::string(Hint)); }); } - exitWithError(toString(std::move(E)), Whence); + exitWithError(toString(std::move(E)), std::string(Whence)); } static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") { - exitWithError(EC.message(), Whence); + exitWithError(EC.message(), std::string(Whence)); } namespace { @@ -94,7 +95,7 @@ static void warnOrExitGivenError(FailureMode FailMode, std::error_code EC, if (FailMode == failIfAnyAreInvalid) exitWithErrorCode(EC, Whence); else - warn(EC.message(), Whence); + warn(EC.message(), std::string(Whence)); } static void handleMergeWriterError(Error E, StringRef WhenceFile = "", @@ -290,6 +291,22 @@ static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) { }); } +static void writeInstrProfile(StringRef OutputFilename, + ProfileFormat OutputFormat, + InstrProfWriter &Writer) { + std::error_code EC; + raw_fd_ostream Output(OutputFilename.data(), EC, sys::fs::OF_None); + if (EC) + exitWithErrorCode(EC, OutputFilename); + + if (OutputFormat == PF_Text) { + if (Error E = Writer.writeText(Output)) + exitWithError(std::move(E)); + } else { + Writer.write(Output); + } +} + static void mergeInstrProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper, StringRef OutputFilename, @@ -307,8 +324,11 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs, // If NumThreads is not specified, auto-detect a good default. if (NumThreads == 0) - NumThreads = - std::min(hardware_concurrency(), unsigned((Inputs.size() + 1) / 2)); + 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; @@ -320,7 +340,7 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs, for (const auto &Input : Inputs) loadInput(Input, Remapper, Contexts[0].get()); } else { - ThreadPool Pool(NumThreads); + ThreadPool Pool(hardware_concurrency(NumThreads)); // Load the inputs in parallel (N/NumThreads serial steps). unsigned Ctx = 0; @@ -362,18 +382,7 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs, (NumErrors > 0 && FailMode == failIfAnyAreInvalid)) exitWithError("No profiles could be merged."); - std::error_code EC; - raw_fd_ostream Output(OutputFilename.data(), EC, sys::fs::OF_None); - if (EC) - exitWithErrorCode(EC, OutputFilename); - - InstrProfWriter &Writer = Contexts[0]->Writer; - if (OutputFormat == PF_Text) { - if (Error E = Writer.writeText(Output)) - exitWithError(std::move(E)); - } else { - Writer.write(Output); - } + writeInstrProfile(OutputFilename, OutputFormat, Contexts[0]->Writer); } /// Make a copy of the given function samples with all symbol names remapped @@ -401,7 +410,8 @@ remapSamples(const sampleprof::FunctionSamples &Samples, for (const auto &Callsite : CallsiteSamples.second) { sampleprof::FunctionSamples Remapped = remapSamples(Callsite.second, Remapper, Error); - MergeResult(Error, Target[Remapped.getName()].merge(Remapped)); + MergeResult(Error, + Target[std::string(Remapped.getName())].merge(Remapped)); } } return Result; @@ -444,7 +454,8 @@ static void handleExtBinaryWriter(sampleprof::SampleProfileWriter &Writer, ProfileFormat OutputFormat, MemoryBuffer *Buffer, sampleprof::ProfileSymbolList &WriterList, - bool CompressAllSections) { + bool CompressAllSections, bool UseMD5, + bool GenPartialProfile) { populateProfileSymbolList(Buffer, WriterList); if (WriterList.size() > 0 && OutputFormat != PF_Ext_Binary) warn("Profile Symbol list is not empty but the output format is not " @@ -453,22 +464,30 @@ static void handleExtBinaryWriter(sampleprof::SampleProfileWriter &Writer, Writer.setProfileSymbolList(&WriterList); if (CompressAllSections) { - if (OutputFormat != PF_Ext_Binary) { + if (OutputFormat != PF_Ext_Binary) warn("-compress-all-section is ignored. Specify -extbinary to enable it"); - } else { - auto ExtBinaryWriter = - static_cast<sampleprof::SampleProfileWriterExtBinary *>(&Writer); - ExtBinaryWriter->setToCompressAllSections(); - } + else + Writer.setToCompressAllSections(); + } + if (UseMD5) { + if (OutputFormat != PF_Ext_Binary) + warn("-use-md5 is ignored. Specify -extbinary to enable it"); + else + Writer.setUseMD5(); + } + if (GenPartialProfile) { + if (OutputFormat != PF_Ext_Binary) + warn("-gen-partial-profile is ignored. Specify -extbinary to enable it"); + else + Writer.setPartialProfile(); } } -static void mergeSampleProfile(const WeightedFileVector &Inputs, - SymbolRemapper *Remapper, - StringRef OutputFilename, - ProfileFormat OutputFormat, - StringRef ProfileSymbolListFile, - bool CompressAllSections, FailureMode FailMode) { +static void +mergeSampleProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper, + StringRef OutputFilename, ProfileFormat OutputFormat, + StringRef ProfileSymbolListFile, bool CompressAllSections, + bool UseMD5, bool GenPartialProfile, FailureMode FailMode) { using namespace sampleprof; StringMap<FunctionSamples> ProfileMap; SmallVector<std::unique_ptr<sampleprof::SampleProfileReader>, 5> Readers; @@ -525,7 +544,7 @@ static void mergeSampleProfile(const WeightedFileVector &Inputs, // Make sure Buffer lives as long as WriterList. auto Buffer = getInputFileBuf(ProfileSymbolListFile); handleExtBinaryWriter(*Writer, OutputFormat, Buffer.get(), WriterList, - CompressAllSections); + CompressAllSections, UseMD5, GenPartialProfile); Writer->write(ProfileMap); } @@ -537,7 +556,7 @@ static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) { if (WeightStr.getAsInteger(10, Weight) || Weight < 1) exitWithError("Input weight must be a positive integer."); - return {FileName, Weight}; + return {std::string(FileName), Weight}; } static void addWeightedInput(WeightedFileVector &WNI, const WeightedFile &WF) { @@ -546,7 +565,7 @@ static void addWeightedInput(WeightedFileVector &WNI, const WeightedFile &WF) { // If it's STDIN just pass it on. if (Filename == "-") { - WNI.push_back({Filename, Weight}); + WNI.push_back({std::string(Filename), Weight}); return; } @@ -557,7 +576,7 @@ static void addWeightedInput(WeightedFileVector &WNI, const WeightedFile &WF) { Filename); // If it's a source file, collect it. if (llvm::sys::fs::is_regular_file(Status)) { - WNI.push_back({Filename, Weight}); + WNI.push_back({std::string(Filename), Weight}); return; } @@ -589,7 +608,7 @@ static void parseInputFilenamesFile(MemoryBuffer *Buffer, continue; // If there's no comma, it's an unweighted profile. else if (SanitizedEntry.find(',') == StringRef::npos) - addWeightedInput(WFV, {SanitizedEntry, 1}); + addWeightedInput(WFV, {std::string(SanitizedEntry), 1}); else addWeightedInput(WFV, parseWeightedFile(SanitizedEntry)); } @@ -653,12 +672,19 @@ static int merge_main(int argc, const char *argv[]) { "compress-all-sections", cl::init(false), cl::Hidden, cl::desc("Compress all sections when writing the profile (only " "meaningful for -extbinary)")); + cl::opt<bool> UseMD5( + "use-md5", cl::init(false), cl::Hidden, + cl::desc("Choose to use MD5 to represent string in name table (only " + "meaningful for -extbinary)")); + cl::opt<bool> GenPartialProfile( + "gen-partial-profile", cl::init(false), cl::Hidden, + cl::desc("Generate a partial profile (only meaningful for -extbinary)")); cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n"); WeightedFileVector WeightedInputs; for (StringRef Filename : InputFilenames) - addWeightedInput(WeightedInputs, {Filename, 1}); + addWeightedInput(WeightedInputs, {std::string(Filename), 1}); for (StringRef WeightedFilename : WeightedInputFilenames) addWeightedInput(WeightedInputs, parseWeightedFile(WeightedFilename)); @@ -687,7 +713,7 @@ static int merge_main(int argc, const char *argv[]) { else mergeSampleProfile(WeightedInputs, Remapper.get(), OutputFilename, OutputFormat, ProfileSymbolListFile, CompressAllSections, - FailureMode); + UseMD5, GenPartialProfile, FailureMode); return 0; } @@ -989,15 +1015,9 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts, } if (ShowDetailedSummary) { - OS << "Detailed summary:\n"; OS << "Total number of blocks: " << PS->getNumCounts() << "\n"; OS << "Total count: " << PS->getTotalCount() << "\n"; - for (auto Entry : PS->getDetailedSummary()) { - OS << Entry.NumCounts << " blocks with count >= " << Entry.MinCount - << " account for " - << format("%0.6g", (float)Entry.Cutoff / ProfileSummary::Scale * 100) - << " percentage of the total counts.\n"; - } + PS->printDetailedSummary(OS); } return 0; } @@ -1012,11 +1032,144 @@ static void showSectionInfo(sampleprof::SampleProfileReader *Reader, } } +namespace { +struct HotFuncInfo { + StringRef FuncName; + uint64_t TotalCount; + double TotalCountPercent; + uint64_t MaxCount; + uint64_t EntryCount; + + HotFuncInfo() + : FuncName(), TotalCount(0), TotalCountPercent(0.0f), MaxCount(0), + EntryCount(0) {} + + HotFuncInfo(StringRef FN, uint64_t TS, double TSP, uint64_t MS, uint64_t ES) + : FuncName(FN), TotalCount(TS), TotalCountPercent(TSP), MaxCount(MS), + EntryCount(ES) {} +}; +} // namespace + +// Print out detailed information about hot functions in PrintValues vector. +// Users specify titles and offset of every columns through ColumnTitle and +// ColumnOffset. The size of ColumnTitle and ColumnOffset need to be the same +// and at least 4. Besides, users can optionally give a HotFuncMetric string to +// print out or let it be an empty string. +static void dumpHotFunctionList(const std::vector<std::string> &ColumnTitle, + const std::vector<int> &ColumnOffset, + const std::vector<HotFuncInfo> &PrintValues, + uint64_t HotFuncCount, uint64_t TotalFuncCount, + uint64_t HotProfCount, uint64_t TotalProfCount, + const std::string &HotFuncMetric, + raw_fd_ostream &OS) { + assert(ColumnOffset.size() == ColumnTitle.size()); + assert(ColumnTitle.size() >= 4); + assert(TotalFuncCount > 0); + double TotalProfPercent = 0; + if (TotalProfCount > 0) + TotalProfPercent = ((double)HotProfCount) / TotalProfCount * 100; + + formatted_raw_ostream FOS(OS); + FOS << HotFuncCount << " out of " << TotalFuncCount + << " functions with profile (" + << format("%.2f%%", (((double)HotFuncCount) / TotalFuncCount * 100)) + << ") are considered hot functions"; + if (!HotFuncMetric.empty()) + FOS << " (" << HotFuncMetric << ")"; + FOS << ".\n"; + FOS << HotProfCount << " out of " << TotalProfCount << " profile counts (" + << format("%.2f%%", TotalProfPercent) << ") are from hot functions.\n"; + + for (size_t I = 0; I < ColumnTitle.size(); ++I) { + FOS.PadToColumn(ColumnOffset[I]); + FOS << ColumnTitle[I]; + } + FOS << "\n"; + + for (const HotFuncInfo &R : PrintValues) { + FOS.PadToColumn(ColumnOffset[0]); + FOS << R.TotalCount << " (" << format("%.2f%%", R.TotalCountPercent) << ")"; + FOS.PadToColumn(ColumnOffset[1]); + FOS << R.MaxCount; + FOS.PadToColumn(ColumnOffset[2]); + FOS << R.EntryCount; + FOS.PadToColumn(ColumnOffset[3]); + FOS << R.FuncName << "\n"; + } + return; +} + +static int +showHotFunctionList(const StringMap<sampleprof::FunctionSamples> &Profiles, + ProfileSummary &PS, raw_fd_ostream &OS) { + using namespace sampleprof; + + const uint32_t HotFuncCutoff = 990000; + auto &SummaryVector = PS.getDetailedSummary(); + uint64_t MinCountThreshold = 0; + for (const ProfileSummaryEntry &SummaryEntry : SummaryVector) { + if (SummaryEntry.Cutoff == HotFuncCutoff) { + MinCountThreshold = SummaryEntry.MinCount; + break; + } + } + assert(MinCountThreshold != 0); + + // Traverse all functions in the profile and keep only hot functions. + // The following loop also calculates the sum of total samples of all + // functions. + std::multimap<uint64_t, std::pair<const FunctionSamples *, const uint64_t>, + std::greater<uint64_t>> + HotFunc; + uint64_t ProfileTotalSample = 0; + uint64_t HotFuncSample = 0; + uint64_t HotFuncCount = 0; + uint64_t MaxCount = 0; + for (const auto &I : Profiles) { + const FunctionSamples &FuncProf = I.second; + ProfileTotalSample += FuncProf.getTotalSamples(); + MaxCount = FuncProf.getMaxCountInside(); + + // MinCountThreshold is a block/line threshold computed for a given cutoff. + // We intentionally compare the maximum sample count in a function with this + // threshold to get an approximate threshold for hot functions. + if (MaxCount >= MinCountThreshold) { + HotFunc.emplace(FuncProf.getTotalSamples(), + std::make_pair(&(I.second), MaxCount)); + HotFuncSample += FuncProf.getTotalSamples(); + ++HotFuncCount; + } + } + + std::vector<std::string> ColumnTitle{"Total sample (%)", "Max sample", + "Entry sample", "Function name"}; + std::vector<int> ColumnOffset{0, 24, 42, 58}; + std::string Metric = + std::string("max sample >= ") + std::to_string(MinCountThreshold); + std::vector<HotFuncInfo> PrintValues; + for (const auto &FuncPair : HotFunc) { + const FunctionSamples &Func = *FuncPair.second.first; + double TotalSamplePercent = + (ProfileTotalSample > 0) + ? (Func.getTotalSamples() * 100.0) / ProfileTotalSample + : 0; + PrintValues.emplace_back(HotFuncInfo( + Func.getFuncName(), Func.getTotalSamples(), TotalSamplePercent, + FuncPair.second.second, Func.getEntrySamples())); + } + dumpHotFunctionList(ColumnTitle, ColumnOffset, PrintValues, HotFuncCount, + Profiles.size(), HotFuncSample, ProfileTotalSample, + Metric, OS); + + return 0; +} + static int showSampleProfile(const std::string &Filename, bool ShowCounts, - bool ShowAllFunctions, + bool ShowAllFunctions, bool ShowDetailedSummary, const std::string &ShowFunction, bool ShowProfileSymbolList, - bool ShowSectionInfoOnly, raw_fd_ostream &OS) { + bool ShowSectionInfoOnly, bool ShowHotFuncList, + raw_fd_ostream &OS) { using namespace sampleprof; LLVMContext Context; auto ReaderOrErr = SampleProfileReader::create(Filename, Context); @@ -1044,6 +1197,15 @@ static int showSampleProfile(const std::string &Filename, bool ShowCounts, ReaderList->dump(OS); } + if (ShowDetailedSummary) { + auto &PS = Reader->getSummary(); + PS.printSummary(OS); + PS.printDetailedSummary(OS); + } + + if (ShowHotFuncList) + showHotFunctionList(Reader->getProfiles(), Reader->getSummary(), OS); + return 0; } @@ -1070,6 +1232,9 @@ static int show_main(int argc, const char *argv[]) { cl::desc( "Cutoff percentages (times 10000) for generating detailed summary"), cl::value_desc("800000,901000,999999")); + cl::opt<bool> ShowHotFuncList( + "hot-func-list", cl::init(false), + cl::desc("Show profile summary of a list of hot functions")); cl::opt<bool> ShowAllFunctions("all-functions", cl::init(false), cl::desc("Details for every function")); cl::opt<bool> ShowCS("showcs", cl::init(false), @@ -1132,8 +1297,9 @@ static int show_main(int argc, const char *argv[]) { OnlyListBelow, ShowFunction, TextFormat, OS); else return showSampleProfile(Filename, ShowCounts, ShowAllFunctions, - ShowFunction, ShowProfileSymbolList, - ShowSectionInfoOnly, OS); + ShowDetailedSummary, ShowFunction, + ShowProfileSymbolList, ShowSectionInfoOnly, + ShowHotFuncList, OS); } int main(int argc, const char *argv[]) { |