[PGO] Add --text option for llvm-profdata show|merge commands

The new option is similar to the SampleProfile dump option.

- dump raw/indexed format into text profile format
- merge the profile and output into text profile format.

Note that Value Profiling data text format is not yet designed. 
That functionality will be added later.

Differential Revision: http://reviews.llvm.org/D14894

llvm-svn: 253913
This commit is contained in:
Xinliang David Li 2015-11-23 20:47:38 +00:00
parent d28d079aa7
commit 0f29a15199
6 changed files with 168 additions and 37 deletions

View File

@ -51,7 +51,17 @@ OPTIONS
.. option:: -instr (default)
Specify that the input profile is an instrumentation-based profile.
Specify that the input profile is an instrumentation-based profile. When
using instrumentation-based profiles, the format of the generated file
can be generated in one of the two ways:
.. option:: -binary (default)
Emit the profile using a binary encoding in indexed profile format.
.. option:: -text
Emit the profile in text format.
.. option:: -sample
@ -121,6 +131,13 @@ OPTIONS
Specify that the input profile is an instrumentation-based profile.
.. option:: -text
Instruct the profile dumper to show profile counts in the text format of the
instrumentation-based profile data representation. By default, the profile
information is dumped in a more human readable form (also in text) with
annotations.
.. option:: -sample
Specify that the input profile is a sample-based profile.

View File

@ -43,6 +43,11 @@ public:
std::error_code addRecord(InstrProfRecord &&I);
/// Write the profile to \c OS
void write(raw_fd_ostream &OS);
/// Write the profile in text format to \c OS
void writeText(raw_fd_ostream &OS);
/// Write \c Record in text format to \c OS
static void writeRecordInText(const InstrProfRecord &Record,
raw_fd_ostream &OS);
/// Write the profile, returning the raw data. For testing.
std::unique_ptr<MemoryBuffer> writeBuffer();

View File

@ -171,6 +171,21 @@ void InstrProfWriter::write(raw_fd_ostream &OS) {
endian::Writer<little>(OS).write<uint64_t>(TableStart.second);
}
void InstrProfWriter::writeRecordInText(const InstrProfRecord &Func,
raw_fd_ostream &OS) {
OS << Func.Name << "\n" << Func.Hash << "\n" << Func.Counts.size() << "\n";
for (uint64_t Count : Func.Counts)
OS << Count << "\n";
OS << "\n";
}
void InstrProfWriter::writeText(raw_fd_ostream &OS) {
for (const auto &I : FunctionData)
for (const auto &Func : I.getValue())
writeRecordInText(Func.second, OS);
}
std::unique_ptr<MemoryBuffer> InstrProfWriter::writeBuffer() {
std::string Data;
llvm::raw_string_ostream OS(Data);

View File

@ -0,0 +1,40 @@
foo
10
2
499500
179900
main
16650
4
1
1000
1000000
499500
foo2
10
2
500500
180100
foo
10
2
499500
179900
main
16650
4
1
1000
1000000
499500
foo2
10
2
500500
180100

View File

@ -0,0 +1,21 @@
Basic tests for testing text dump functions.
RUN: llvm-profdata show --all-functions -counts --text %p/Inputs/basic.proftext > %t-basic.proftext1
RUN: llvm-profdata merge -o %t-basic.proftext2 --text %p/Inputs/basic.proftext
RUN: llvm-profdata merge -binary -o %t-basic.profdata1 %t-basic.proftext1
RUN: llvm-profdata merge -o %t-basic.profdata2 %t-basic.proftext2
RUN: llvm-profdata show --all-functions -counts %t-basic.profdata1 > %t-basic.dump3
RUN: llvm-profdata show --all-functions -counts %t-basic.profdata2 > %t-basic.dump4
RUN: llvm-profdata merge -text -o %t-basic.proftext5 %t-basic.profdata1
RUN: llvm-profdata merge -text -o %t-basic.proftext6 %t-basic.profdata2
RUN: diff %t-basic.dump3 %t-basic.dump4
RUN: diff %t-basic.proftext5 %t-basic.proftext6
RUN: not llvm-profdata merge -gcc -o %t-basic-profdata3 %t-basic.proftext2 2>&1 | FileCheck %s --check-prefix=UNKNOWN
UNKNOWN: Unknown

View File

@ -30,6 +30,8 @@
using namespace llvm;
enum ProfileFormat { PF_None = 0, PF_Text, PF_Binary, PF_GCC };
static void exitWithError(const Twine &Message,
StringRef Whence = "",
StringRef Hint = "") {
@ -92,10 +94,14 @@ static void handleMergeWriterError(std::error_code &Error,
}
static void mergeInstrProfile(const cl::list<std::string> &Inputs,
StringRef OutputFilename) {
StringRef OutputFilename,
ProfileFormat OutputFormat) {
if (OutputFilename.compare("-") == 0)
exitWithError("Cannot write indexed profdata format to stdout.");
if (OutputFormat != PF_Binary && OutputFormat != PF_Text)
exitWithError("Unknown format is specified.");
std::error_code EC;
raw_fd_ostream Output(OutputFilename.data(), EC, sys::fs::F_None);
if (EC)
@ -119,14 +125,22 @@ static void mergeInstrProfile(const cl::list<std::string> &Inputs,
if (Reader->hasError())
exitWithErrorCode(Reader->getError(), Filename);
}
Writer.write(Output);
if (OutputFormat == PF_Text)
Writer.writeText(Output);
else
Writer.write(Output);
}
static sampleprof::SampleProfileFormat FormatMap[] = {
sampleprof::SPF_None, sampleprof::SPF_Text, sampleprof::SPF_Binary,
sampleprof::SPF_GCC};
static void mergeSampleProfile(const cl::list<std::string> &Inputs,
StringRef OutputFilename,
sampleprof::SampleProfileFormat OutputFormat) {
ProfileFormat OutputFormat) {
using namespace sampleprof;
auto WriterOrErr = SampleProfileWriter::create(OutputFilename, OutputFormat);
auto WriterOrErr =
SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]);
if (std::error_code EC = WriterOrErr.getError())
exitWithErrorCode(EC, OutputFilename);
@ -174,19 +188,18 @@ static int merge_main(int argc, const char *argv[]) {
cl::values(clEnumVal(instr, "Instrumentation profile (default)"),
clEnumVal(sample, "Sample profile"), clEnumValEnd));
cl::opt<sampleprof::SampleProfileFormat> OutputFormat(
cl::desc("Format of output profile (only meaningful with --sample)"),
cl::init(sampleprof::SPF_Binary),
cl::values(clEnumValN(sampleprof::SPF_Binary, "binary",
"Binary encoding (default)"),
clEnumValN(sampleprof::SPF_Text, "text", "Text encoding"),
clEnumValN(sampleprof::SPF_GCC, "gcc", "GCC encoding"),
cl::opt<ProfileFormat> OutputFormat(
cl::desc("Format of output profile"), cl::init(PF_Binary),
cl::values(clEnumValN(PF_Binary, "binary", "Binary encoding (default)"),
clEnumValN(PF_Text, "text", "Text encoding"),
clEnumValN(PF_GCC, "gcc",
"GCC encoding (only meaningful for -sample)"),
clEnumValEnd));
cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n");
if (ProfileKind == instr)
mergeInstrProfile(Inputs, OutputFilename);
mergeInstrProfile(Inputs, OutputFilename, OutputFormat);
else
mergeSampleProfile(Inputs, OutputFilename, OutputFormat);
@ -195,7 +208,8 @@ static int merge_main(int argc, const char *argv[]) {
static int showInstrProfile(std::string Filename, bool ShowCounts,
bool ShowIndirectCallTargets, bool ShowAllFunctions,
std::string ShowFunction, raw_fd_ostream &OS) {
std::string ShowFunction, bool TextFormat,
raw_fd_ostream &OS) {
auto ReaderOrErr = InstrProfReader::create(Filename);
if (std::error_code EC = ReaderOrErr.getError())
exitWithErrorCode(EC, Filename);
@ -208,53 +222,69 @@ static int showInstrProfile(std::string Filename, bool ShowCounts,
ShowAllFunctions || (!ShowFunction.empty() &&
Func.Name.find(ShowFunction) != Func.Name.npos);
bool doTextFormatDump = (Show && ShowCounts && TextFormat);
if (doTextFormatDump) {
InstrProfWriter::writeRecordInText(Func, OS);
continue;
}
++TotalFunctions;
assert(Func.Counts.size() > 0 && "function missing entry counter");
if (Func.Counts[0] > MaxFunctionCount)
MaxFunctionCount = Func.Counts[0];
for (size_t I = 1, E = Func.Counts.size(); I < E; ++I) {
if (Func.Counts[I] > MaxBlockCount)
MaxBlockCount = Func.Counts[I];
}
if (Show) {
if (!ShownFunctions)
OS << "Counters:\n";
++ShownFunctions;
OS << " " << Func.Name << ":\n"
<< " Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n"
<< " Counters: " << Func.Counts.size() << "\n"
<< " Function count: " << Func.Counts[0] << "\n";
if (ShowIndirectCallTargets)
OS << " Indirect Call Site Count: "
<< Func.getNumValueSites(IPVK_IndirectCallTarget) << "\n";
}
if (Show && ShowCounts)
OS << " Block counts: [";
for (size_t I = 1, E = Func.Counts.size(); I < E; ++I) {
if (Func.Counts[I] > MaxBlockCount)
MaxBlockCount = Func.Counts[I];
if (Show && ShowCounts)
OS << (I == 1 ? "" : ", ") << Func.Counts[I];
}
if (Show && ShowCounts)
OS << "]\n";
if (ShowCounts) {
OS << " Block counts: [";
for (size_t I = 1, E = Func.Counts.size(); I < E; ++I) {
OS << (I == 1 ? "" : ", ") << Func.Counts[I];
}
OS << "]\n";
}
if (Show && ShowIndirectCallTargets) {
uint32_t NS = Func.getNumValueSites(IPVK_IndirectCallTarget);
OS << " Indirect Target Results: \n";
for (size_t I = 0; I < NS; ++I) {
uint32_t NV = Func.getNumValueDataForSite(IPVK_IndirectCallTarget, I);
std::unique_ptr<InstrProfValueData[]> VD =
Func.getValueForSite(IPVK_IndirectCallTarget, I);
for (uint32_t V = 0; V < NV; V++) {
OS << "\t[ " << I << ", ";
OS << (const char *)VD[V].Value << ", " << VD[V].Count << " ]\n";
if (ShowIndirectCallTargets) {
uint32_t NS = Func.getNumValueSites(IPVK_IndirectCallTarget);
OS << " Indirect Target Results: \n";
for (size_t I = 0; I < NS; ++I) {
uint32_t NV = Func.getNumValueDataForSite(IPVK_IndirectCallTarget, I);
std::unique_ptr<InstrProfValueData[]> VD =
Func.getValueForSite(IPVK_IndirectCallTarget, I);
for (uint32_t V = 0; V < NV; V++) {
OS << "\t[ " << I << ", ";
OS << (const char *)VD[V].Value << ", " << VD[V].Count << " ]\n";
}
}
}
}
}
if (Reader->hasError())
exitWithErrorCode(Reader->getError(), Filename);
if (ShowCounts && TextFormat)
return 0;
if (ShowAllFunctions || !ShowFunction.empty())
OS << "Functions shown: " << ShownFunctions << "\n";
OS << "Total functions: " << TotalFunctions << "\n";
@ -289,6 +319,9 @@ 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<bool> TextFormat(
"text", cl::init(false),
cl::desc("Show instr profile data in text dump format"));
cl::opt<bool> ShowIndirectCallTargets(
"ic-targets", cl::init(false),
cl::desc("Show indirect call site target values for shown functions"));
@ -314,14 +347,14 @@ static int show_main(int argc, const char *argv[]) {
std::error_code EC;
raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::F_Text);
if (EC)
exitWithErrorCode(EC, OutputFilename);
exitWithErrorCode(EC, OutputFilename);
if (ShowAllFunctions && !ShowFunction.empty())
errs() << "warning: -function argument ignored: showing all functions\n";
if (ProfileKind == instr)
return showInstrProfile(Filename, ShowCounts, ShowIndirectCallTargets,
ShowAllFunctions, ShowFunction, OS);
ShowAllFunctions, ShowFunction, TextFormat, OS);
else
return showSampleProfile(Filename, ShowCounts, ShowAllFunctions,
ShowFunction, OS);