Skip to content

Commit 66873fb

Browse files
wlei-llvmtstellar
authored andcommitted
[CSSPGO][llvm-profgen] Renovate perfscript check and command line input validation
This include some changes related with PerfReader's the input check and command line change: 1) It appears there might be thousands of leading MMAP-Event line in the perfscript for large workload. For this case, the 4k threshold is not eligible to determine it's a hybrid sample. This change renovated the `isHybridPerfScript` by going through the script without threshold limitation checking whether there is a non-empty call stack immediately followed by a LBR sample. It will stop once it find a valid one. 2) Added several input validations for the command line switches in PerfReader. 3) Changed the command line `show-disassembly` to `show-disassembly-only`, it will print to stdout and exit early which leave an empty output profile. Reviewed By: hoy, wenlei Differential Revision: https://reviews.llvm.org/D96387
1 parent beb80ff commit 66873fb

File tree

9 files changed

+131
-55
lines changed

9 files changed

+131
-55
lines changed

llvm/test/tools/llvm-profgen/disassemble.s

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# REQUIRES: x86-registered-target
22
# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t
3-
# RUN: llvm-profgen --binary=%t --perfscript=%s --output=%t1 -show-disassembly -x86-asm-syntax=intel | FileCheck %s --match-full-lines
3+
# RUN: llvm-profgen --binary=%t --perfscript=%s --output=%t1 -show-disassembly-only -x86-asm-syntax=intel | FileCheck %s --match-full-lines
44

55
# CHECK: Disassembly of section .text [0x0, 0x66]:
66
# CHECK: <foo1>:
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
; RUN: llvm-profgen --perfscript=%s --binary=%S/Inputs/noinline-cs-noprobe.perfbin --output=%t 2>%t1
2+
; RUN: FileCheck %s --input-file %t1
3+
4+
4005dc
5+
400634
6+
400684
7+
7f68c5788793
8+
9+
; XFAIL: *

llvm/test/tools/llvm-profgen/pseudoprobe-decoding.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
; RUN: llvm-profgen --perfscript=%s --binary=%S/Inputs/inline-cs-pseudoprobe.perfbin --output=%t --show-pseudo-probe --show-disassembly | FileCheck %s
1+
; RUN: llvm-profgen --perfscript=%s --binary=%S/Inputs/inline-cs-pseudoprobe.perfbin --output=%t --show-pseudo-probe --show-disassembly-only | FileCheck %s
22

33
PERF_RECORD_MMAP2 2854748/2854748: [0x400000(0x1000) @ 0 00:1d 123291722 526021]: r-xp /home/inline-cs-pseudoprobe.perfbin
44

llvm/test/tools/llvm-profgen/symbolize.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
; REQUIRES: x86-registered-target
22
; RUN: llc -filetype=obj %s -o %t
3-
; RUN: llvm-profgen --binary=%t --perfscript=%s --output=%t1 --show-disassembly -x86-asm-syntax=intel --show-source-locations | FileCheck %s --match-full-lines
3+
; RUN: llvm-profgen --binary=%t --perfscript=%s --output=%t1 --show-disassembly-only -x86-asm-syntax=intel --show-source-locations | FileCheck %s --match-full-lines
44

55
; CHECK: Disassembly of section .text [0x0, 0x4a]:
66
; CHECK: <funcA>:

llvm/tools/llvm-profgen/PerfReader.cpp

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ static cl::opt<bool> ShowUnwinderOutput("show-unwinder-output",
1717
cl::ZeroOrMore,
1818
cl::desc("Print unwinder output"));
1919

20+
extern cl::opt<bool> ShowDisassemblyOnly;
21+
extern cl::opt<bool> ShowSourceLocations;
22+
2023
namespace llvm {
2124
namespace sampleprof {
2225

@@ -230,7 +233,44 @@ bool VirtualUnwinder::unwind(const HybridSample *Sample, uint64_t Repeat) {
230233
return true;
231234
}
232235

233-
PerfReader::PerfReader(cl::list<std::string> &BinaryFilenames) {
236+
void PerfReader::validateCommandLine(
237+
cl::list<std::string> &BinaryFilenames,
238+
cl::list<std::string> &PerfTraceFilenames) {
239+
// Allow the invalid perfscript if we only use to show binary disassembly
240+
if (!ShowDisassemblyOnly) {
241+
for (auto &File : PerfTraceFilenames) {
242+
if (!llvm::sys::fs::exists(File)) {
243+
std::string Msg = "Input perf script(" + File + ") doesn't exist!";
244+
exitWithError(Msg);
245+
}
246+
}
247+
}
248+
if (BinaryFilenames.size() > 1) {
249+
// TODO: remove this if everything is ready to support multiple binaries.
250+
exitWithError(
251+
"Currently only support one input binary, multiple binaries' "
252+
"profile will be merged in one profile and make profile "
253+
"summary info inaccurate. Please use `llvm-perfdata` to merge "
254+
"profiles from multiple binaries.");
255+
}
256+
for (auto &Binary : BinaryFilenames) {
257+
if (!llvm::sys::fs::exists(Binary)) {
258+
std::string Msg = "Input binary(" + Binary + ") doesn't exist!";
259+
exitWithError(Msg);
260+
}
261+
}
262+
if (CSProfileGenerator::MaxCompressionSize < -1) {
263+
exitWithError("Value of --compress-recursion should >= -1");
264+
}
265+
if (ShowSourceLocations && !ShowDisassemblyOnly) {
266+
exitWithError("--show-source-locations should work together with "
267+
"--show-disassembly-only!");
268+
}
269+
}
270+
271+
PerfReader::PerfReader(cl::list<std::string> &BinaryFilenames,
272+
cl::list<std::string> &PerfTraceFilenames) {
273+
validateCommandLine(BinaryFilenames, PerfTraceFilenames);
234274
// Load the binaries.
235275
for (auto Filename : BinaryFilenames)
236276
loadBinary(Filename, /*AllowNameConflict*/ false);
@@ -591,27 +631,13 @@ void PerfReader::parseAndAggregateTrace(StringRef Filename) {
591631

592632
void PerfReader::checkAndSetPerfType(
593633
cl::list<std::string> &PerfTraceFilenames) {
594-
bool HasHybridPerf = true;
595634
for (auto FileName : PerfTraceFilenames) {
596-
if (!isHybridPerfScript(FileName)) {
597-
HasHybridPerf = false;
598-
break;
599-
}
600-
}
601-
602-
if (HasHybridPerf) {
603-
PerfType = PERF_LBR_STACK;
604-
} else {
605-
// TODO: Support other type of perf script
606-
PerfType = PERF_INVILID;
607-
}
608-
609-
if (BinaryTable.size() > 1) {
610-
// TODO: remove this if everything is ready to support multiple binaries.
611-
exitWithError("Currently only support one input binary, multiple binaries' "
612-
"profile will be merged in one profile and make profile "
613-
"summary info inaccurate. Please use `perfdata` to merge "
614-
"profiles from multiple binaries.");
635+
PerfScriptType Type = checkPerfScriptType(FileName);
636+
if (Type == PERF_INVALID)
637+
exitWithError("Invalid perf script input!");
638+
if (PerfType != PERF_UNKNOWN && PerfType != Type)
639+
exitWithError("Inconsistent sample among different perf scripts");
640+
PerfType = Type;
615641
}
616642
}
617643

llvm/tools/llvm-profgen/PerfReader.h

Lines changed: 53 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,10 @@ class TraceStream {
5959

6060
// The type of perfscript
6161
enum PerfScriptType {
62-
PERF_INVILID = 0,
63-
PERF_LBR = 1, // Only LBR sample
64-
PERF_LBR_STACK = 2, // Hybrid sample including call stack and LBR stack.
62+
PERF_UNKNOWN = 0,
63+
PERF_INVALID = 1,
64+
PERF_LBR = 2, // Only LBR sample
65+
PERF_LBR_STACK = 3, // Hybrid sample including call stack and LBR stack.
6566
};
6667

6768
// The parsed LBR sample entry.
@@ -502,19 +503,52 @@ using BinarySampleCounterMap =
502503
class PerfReader {
503504

504505
public:
505-
PerfReader(cl::list<std::string> &BinaryFilenames);
506-
507-
// Hybrid sample(call stack + LBRs) profile traces are seprated by double line
508-
// break, search for that within the first 4k charactors to avoid going
509-
// through the whole file.
510-
static bool isHybridPerfScript(StringRef FileName) {
511-
auto BufOrError = MemoryBuffer::getFileOrSTDIN(FileName, 4000);
512-
if (!BufOrError)
513-
exitWithError(BufOrError.getError(), FileName);
514-
auto Buffer = std::move(BufOrError.get());
515-
if (Buffer->getBuffer().find("\n\n") == StringRef::npos)
506+
PerfReader(cl::list<std::string> &BinaryFilenames,
507+
cl::list<std::string> &PerfTraceFilenames);
508+
509+
// A LBR sample is like:
510+
// 0x5c6313f/0x5c63170/P/-/-/0 0x5c630e7/0x5c63130/P/-/-/0 ...
511+
// A heuristic for fast detection by checking whether a
512+
// leading " 0x" and the '/' exist.
513+
static bool isLBRSample(StringRef Line) {
514+
if (!Line.startswith(" 0x"))
516515
return false;
517-
return true;
516+
if (Line.find('/') != StringRef::npos)
517+
return true;
518+
return false;
519+
}
520+
521+
// The raw hybird sample is like
522+
// e.g.
523+
// 4005dc # call stack leaf
524+
// 400634
525+
// 400684 # call stack root
526+
// 0x4005c8/0x4005dc/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 ...
527+
// ... 0x4005c8/0x4005dc/P/-/-/0 # LBR Entries
528+
// Determine the perfscript contains hybrid samples(call stack + LBRs) by
529+
// checking whether there is a non-empty call stack immediately followed by
530+
// a LBR sample
531+
static PerfScriptType checkPerfScriptType(StringRef FileName) {
532+
TraceStream TraceIt(FileName);
533+
uint64_t FrameAddr = 0;
534+
while (!TraceIt.isAtEoF()) {
535+
int32_t Count = 0;
536+
while (!TraceIt.isAtEoF() &&
537+
!TraceIt.getCurrentLine().ltrim().getAsInteger(16, FrameAddr)) {
538+
Count++;
539+
TraceIt.advance();
540+
}
541+
if (!TraceIt.isAtEoF()) {
542+
if (isLBRSample(TraceIt.getCurrentLine())) {
543+
if (Count > 0)
544+
return PERF_LBR_STACK;
545+
else
546+
return PERF_LBR;
547+
}
548+
TraceIt.advance();
549+
}
550+
}
551+
return PERF_INVALID;
518552
}
519553

520554
// The parsed MMap event
@@ -540,6 +574,9 @@ class PerfReader {
540574
}
541575

542576
private:
577+
/// Validate the command line input
578+
void validateCommandLine(cl::list<std::string> &BinaryFilenames,
579+
cl::list<std::string> &PerfTraceFilenames);
543580
/// Parse a single line of a PERF_RECORD_MMAP2 event looking for a
544581
/// mapping between the binary name and its memory layout.
545582
///
@@ -574,7 +611,7 @@ class PerfReader {
574611
BinarySampleCounterMap BinarySampleCounters;
575612
// Samples with the repeating time generated by the perf reader
576613
AggregatedCounter AggregatedSamples;
577-
PerfScriptType PerfType;
614+
PerfScriptType PerfType = PERF_UNKNOWN;
578615
};
579616

580617
} // end namespace sampleprof

llvm/tools/llvm-profgen/ProfileGenerator.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
static cl::opt<std::string> OutputFilename("output", cl::value_desc("output"),
1212
cl::Required,
1313
cl::desc("Output profile file"));
14+
static cl::alias OutputA("o", cl::desc("Alias for --output"),
15+
cl::aliasopt(OutputFilename));
1416

1517
static cl::opt<SampleProfileFormat> OutputFormat(
1618
"format", cl::desc("Format of output profile"), cl::init(SPF_Text),

llvm/tools/llvm-profgen/ProfiledBinary.cpp

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,15 @@
2222
using namespace llvm;
2323
using namespace sampleprof;
2424

25-
static cl::opt<bool> ShowDisassembly("show-disassembly", cl::ReallyHidden,
26-
cl::init(false), cl::ZeroOrMore,
27-
cl::desc("Print disassembled code."));
25+
cl::opt<bool> ShowDisassemblyOnly("show-disassembly-only", cl::ReallyHidden,
26+
cl::init(false), cl::ZeroOrMore,
27+
cl::desc("Print disassembled code."));
2828

29-
static cl::opt<bool> ShowSourceLocations("show-source-locations",
30-
cl::ReallyHidden, cl::init(false),
31-
cl::ZeroOrMore,
32-
cl::desc("Print source locations."));
29+
cl::opt<bool> ShowSourceLocations("show-source-locations", cl::ReallyHidden,
30+
cl::init(false), cl::ZeroOrMore,
31+
cl::desc("Print source locations."));
3332

34-
static cl::opt<bool> ShowPseudoProbe(
33+
cl::opt<bool> ShowPseudoProbe(
3534
"show-pseudo-probe", cl::ReallyHidden, cl::init(false), cl::ZeroOrMore,
3635
cl::desc("Print pseudo probe section and disassembled info."));
3736

@@ -199,7 +198,6 @@ void ProfiledBinary::decodePseudoProbe(const ELFObjectFileBase *Obj) {
199198
bool ProfiledBinary::dissassembleSymbol(std::size_t SI, ArrayRef<uint8_t> Bytes,
200199
SectionSymbolsTy &Symbols,
201200
const SectionRef &Section) {
202-
203201
std::size_t SE = Symbols.size();
204202
uint64_t SectionOffset = Section.getAddress() - PreferredBaseAddress;
205203
uint64_t SectSize = Section.getSize();
@@ -211,7 +209,7 @@ bool ProfiledBinary::dissassembleSymbol(std::size_t SI, ArrayRef<uint8_t> Bytes,
211209
return true;
212210

213211
std::string &&SymbolName = Symbols[SI].Name.str();
214-
if (ShowDisassembly)
212+
if (ShowDisassemblyOnly)
215213
outs() << '<' << SymbolName << ">:\n";
216214

217215
uint64_t Offset = StartOffset;
@@ -223,7 +221,7 @@ bool ProfiledBinary::dissassembleSymbol(std::size_t SI, ArrayRef<uint8_t> Bytes,
223221
Offset + PreferredBaseAddress, nulls()))
224222
return false;
225223

226-
if (ShowDisassembly) {
224+
if (ShowDisassemblyOnly) {
227225
if (ShowPseudoProbe) {
228226
ProbeDecoder.printProbeForAddress(outs(),
229227
Offset + PreferredBaseAddress);
@@ -257,7 +255,7 @@ bool ProfiledBinary::dissassembleSymbol(std::size_t SI, ArrayRef<uint8_t> Bytes,
257255
Offset += Size;
258256
}
259257

260-
if (ShowDisassembly)
258+
if (ShowDisassemblyOnly)
261259
outs() << "\n";
262260

263261
FuncStartAddrMap[StartOffset] = Symbols[SI].Name.str();
@@ -323,7 +321,7 @@ void ProfiledBinary::disassemble(const ELFObjectFileBase *Obj) {
323321
for (std::pair<const SectionRef, SectionSymbolsTy> &SecSyms : AllSymbols)
324322
stable_sort(SecSyms.second);
325323

326-
if (ShowDisassembly)
324+
if (ShowDisassemblyOnly)
327325
outs() << "\nDisassembly of " << FileName << ":\n";
328326

329327
// Dissassemble a text section.
@@ -342,7 +340,7 @@ void ProfiledBinary::disassemble(const ELFObjectFileBase *Obj) {
342340
// Register the text section.
343341
TextSections.insert({SectionOffset, SectSize});
344342

345-
if (ShowDisassembly) {
343+
if (ShowDisassemblyOnly) {
346344
StringRef SectionName = unwrapOrError(Section.getName(), FileName);
347345
outs() << "\nDisassembly of section " << SectionName;
348346
outs() << " [" << format("0x%" PRIx64, SectionOffset) << ", "

llvm/tools/llvm-profgen/llvm-profgen.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ static cl::list<std::string>
2929
llvm::cl::MiscFlags::CommaSeparated,
3030
cl::desc("Path of profiled binary files"));
3131

32+
extern cl::opt<bool> ShowDisassemblyOnly;
33+
3234
using namespace llvm;
3335
using namespace sampleprof;
3436

@@ -43,7 +45,9 @@ int main(int argc, const char *argv[]) {
4345
cl::ParseCommandLineOptions(argc, argv, "llvm SPGO profile generator\n");
4446

4547
// Load binaries and parse perf events and samples
46-
PerfReader Reader(BinaryFilenames);
48+
PerfReader Reader(BinaryFilenames, PerfTraceFilenames);
49+
if (ShowDisassemblyOnly)
50+
return EXIT_SUCCESS;
4751
Reader.parsePerfTraces(PerfTraceFilenames);
4852

4953
std::unique_ptr<ProfileGenerator> Generator = ProfileGenerator::create(

0 commit comments

Comments
 (0)