summaryrefslogtreecommitdiff
path: root/llvm/tools/llvm-profdata/llvm-profdata.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/tools/llvm-profdata/llvm-profdata.cpp')
-rw-r--r--llvm/tools/llvm-profdata/llvm-profdata.cpp274
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[]) {