Skip to content

Commit 6d46a6d

Browse files
rafaelaulermemfrob
authored andcommitted
[PERF2BOLT] Check build-ids of binaries when aggregating
Summary: Check the build-id of the input binary against the build-id of the binary used during profiling data collection with perf, as reported in perf.data. If they differ, issue a warning, since the user should use exactly the same binary. If we cannot determine the build-id of either the input binary or the one registered in the input perf.data, cancel the build-id check but print a log message. (cherry picked from FBD6001917)
1 parent d5d52e4 commit 6d46a6d

File tree

4 files changed

+255
-26
lines changed

4 files changed

+255
-26
lines changed

bolt/DataAggregator.cpp

Lines changed: 138 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,25 @@ void DataAggregator::findPerfExecutable() {
6060

6161
void DataAggregator::start(StringRef PerfDataFilename) {
6262
Enabled = true;
63+
this->PerfDataFilename = PerfDataFilename;
6364
outs() << "PERF2BOLT: Starting data aggregation job for " << PerfDataFilename
6465
<< "\n";
6566
findPerfExecutable();
66-
launchPerfEventsNoWait(PerfDataFilename);
67-
launchPerfTasksNoWait(PerfDataFilename);
67+
launchPerfEventsNoWait();
68+
launchPerfTasksNoWait();
6869
}
6970

70-
bool DataAggregator::launchPerfEventsNoWait(StringRef PerfDataFilename) {
71+
void DataAggregator::abort() {
72+
std::string Error;
73+
74+
// Kill subprocesses in case they are not finished
75+
sys::Wait(TasksPI, 1, false, &Error);
76+
sys::Wait(EventsPI, 1, false, &Error);
77+
78+
deleteTempFiles();
79+
}
80+
81+
bool DataAggregator::launchPerfEventsNoWait() {
7182
SmallVector<const char*, 4> Argv;
7283
SmallVector<StringRef, 3> Redirects;
7384
SmallVector<const StringRef*, 3> RedirectPtrs;
@@ -112,7 +123,7 @@ bool DataAggregator::launchPerfEventsNoWait(StringRef PerfDataFilename) {
112123
return true;
113124
}
114125

115-
bool DataAggregator::launchPerfTasksNoWait(StringRef PerfDataFilename) {
126+
bool DataAggregator::launchPerfTasksNoWait() {
116127
SmallVector<const char*, 4> Argv;
117128
SmallVector<StringRef, 3> Redirects;
118129
SmallVector<const StringRef*, 3> RedirectPtrs;
@@ -156,6 +167,88 @@ bool DataAggregator::launchPerfTasksNoWait(StringRef PerfDataFilename) {
156167
return true;
157168
}
158169

170+
Optional<std::string> DataAggregator::getPerfBuildID() {
171+
SmallVector<const char *, 4> Argv;
172+
SmallVector<StringRef, 3> Redirects;
173+
SmallVector<const StringRef*, 3> RedirectPtrs;
174+
SmallVector<char, 256> OutputPath;
175+
SmallVector<char, 256> ErrPath;
176+
177+
Argv.push_back(PerfPath.data());
178+
Argv.push_back("buildid-list");
179+
Argv.push_back("-i");
180+
Argv.push_back(PerfDataFilename.data());
181+
Argv.push_back(nullptr);
182+
183+
if (auto Errc = sys::fs::createTemporaryFile("perf.buildid", "out",
184+
OutputPath)) {
185+
outs() << "PERF2BOLT: Failed to create temporary file "
186+
<< OutputPath << " with error " << Errc.message() << "\n";
187+
exit(1);
188+
}
189+
190+
if (auto Errc = sys::fs::createTemporaryFile("perf.script", "err",
191+
ErrPath)) {
192+
outs() << "PERF2BOLT: Failed to create temporary file "
193+
<< ErrPath << " with error " << Errc.message() << "\n";
194+
exit(1);
195+
}
196+
197+
Redirects.push_back(""); // Stdin
198+
Redirects.push_back(StringRef(OutputPath.data())); // Stdout
199+
Redirects.push_back(StringRef(ErrPath.data())); // Stderr
200+
RedirectPtrs.push_back(&Redirects[0]);
201+
RedirectPtrs.push_back(&Redirects[1]);
202+
RedirectPtrs.push_back(&Redirects[2]);
203+
204+
DEBUG(dbgs() << "Launching perf: " << PerfPath.data() << " 1> "
205+
<< OutputPath.data() << " 2> "
206+
<< ErrPath.data() << "\n");
207+
208+
auto RetCode = sys::ExecuteAndWait(PerfPath.data(), Argv.data(),
209+
/*envp*/ nullptr, &RedirectPtrs[0]);
210+
211+
if (RetCode != 0) {
212+
ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
213+
MemoryBuffer::getFileOrSTDIN(ErrPath.data());
214+
StringRef ErrBuf = (*MB)->getBuffer();
215+
216+
errs() << "PERF-ERROR: Return code " << RetCode << "\n";
217+
errs() << ErrBuf;
218+
deleteTempFile(ErrPath.data());
219+
deleteTempFile(OutputPath.data());
220+
return NoneType();
221+
}
222+
223+
ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
224+
MemoryBuffer::getFileOrSTDIN(OutputPath.data());
225+
if (std::error_code EC = MB.getError()) {
226+
errs() << "Cannot open " << PerfTasksOutputPath.data() << ": "
227+
<< EC.message() << "\n";
228+
deleteTempFile(ErrPath.data());
229+
deleteTempFile(OutputPath.data());
230+
return NoneType();
231+
}
232+
233+
FileBuf.reset(MB->release());
234+
ParsingBuf = FileBuf->getBuffer();
235+
Col = 0;
236+
Line = 1;
237+
auto ParseResult = parsePerfBuildID();
238+
if (!ParseResult) {
239+
outs() << "PERF2BOLT: Failed to parse build-id from perf output\n";
240+
deleteTempFile(ErrPath.data());
241+
deleteTempFile(OutputPath.data());
242+
return NoneType();
243+
}
244+
245+
outs() << "PERF2BOLT: Perf.data build-id is: " << *ParseResult << "\n";
246+
247+
deleteTempFile(ErrPath.data());
248+
deleteTempFile(OutputPath.data());
249+
return std::string(ParseResult->data(), ParseResult->size());
250+
}
251+
159252
bool DataAggregator::checkPerfDataMagic(StringRef FileName) {
160253
int FD;
161254
if (sys::fs::openFileForRead(FileName, FD)) {
@@ -175,26 +268,18 @@ bool DataAggregator::checkPerfDataMagic(StringRef FileName) {
175268
return false;
176269
}
177270

178-
void DataAggregator::deleteTempFiles() {
179-
if (auto Errc = sys::fs::remove(PerfEventsErrPath.data())) {
271+
void DataAggregator::deleteTempFile(StringRef File) {
272+
if (auto Errc = sys::fs::remove(File.data())) {
180273
outs() << "PERF2BOLT: Failed to delete temporary file "
181-
<< PerfEventsErrPath << " with error " << Errc.message() << "\n";
182-
}
183-
184-
if (auto Errc = sys::fs::remove(PerfEventsOutputPath.data())) {
185-
outs() << "PERF2BOLT: Failed to delete temporary file "
186-
<< PerfEventsOutputPath << " with error " << Errc.message() << "\n";
187-
}
188-
189-
if (auto Errc = sys::fs::remove(PerfTasksErrPath.data())) {
190-
outs() << "PERF2BOLT: Failed to delete temporary file "
191-
<< PerfTasksErrPath << " with error " << Errc.message() << "\n";
274+
<< File << " with error " << Errc.message() << "\n";
192275
}
276+
}
193277

194-
if (auto Errc = sys::fs::remove(PerfTasksOutputPath.data())) {
195-
outs() << "PERF2BOLT: Failed to delete temporary file "
196-
<< PerfTasksOutputPath << " with error " << Errc.message() << "\n";
197-
}
278+
void DataAggregator::deleteTempFiles() {
279+
deleteTempFile(PerfEventsErrPath.data());
280+
deleteTempFile(PerfEventsOutputPath.data());
281+
deleteTempFile(PerfTasksErrPath.data());
282+
deleteTempFile(PerfTasksOutputPath.data());
198283
}
199284

200285
bool DataAggregator::aggregate(BinaryContext &BC,
@@ -541,7 +626,8 @@ std::error_code DataAggregator::parseEvents() {
541626
}
542627
}
543628
outs() << format("%.1f%%", Perc);
544-
outs().resetColor();
629+
if (outs().has_colors())
630+
outs().resetColor();
545631
outs() << ")";
546632
}
547633
outs() << "\n";
@@ -623,6 +709,36 @@ std::error_code DataAggregator::parseTasks() {
623709
return std::error_code();
624710
}
625711

712+
Optional<std::pair<StringRef, StringRef>>
713+
DataAggregator::parseNameBuildIDPair() {
714+
while (checkAndConsumeFS()) {}
715+
716+
auto BuildIDStr = parseString(FieldSeparator, true);
717+
if (std::error_code EC = BuildIDStr.getError())
718+
return NoneType();
719+
720+
auto NameStr = parseString(FieldSeparator, true);
721+
if (std::error_code EC = NameStr.getError())
722+
return NoneType();
723+
724+
consumeRestOfLine();
725+
return std::make_pair(NameStr.get(), BuildIDStr.get());
726+
}
727+
728+
Optional<StringRef> DataAggregator::parsePerfBuildID() {
729+
while (hasData()) {
730+
auto IDPair = parseNameBuildIDPair();
731+
if (!IDPair)
732+
return NoneType();
733+
734+
if (sys::path::filename(IDPair->first) != BinaryName)
735+
continue;
736+
737+
return IDPair->second;
738+
}
739+
return NoneType();
740+
}
741+
626742
std::error_code DataAggregator::writeAggregatedFile() const {
627743
std::error_code EC;
628744
raw_fd_ostream OutFile(OutputFDataName, EC, sys::fs::OpenFlags::F_None);

bolt/DataAggregator.h

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ class DataAggregator : public DataReader {
7171
/// Whether aggregator was scheduled to run
7272
bool Enabled{false};
7373

74+
/// Input perf.data file
75+
StringRef PerfDataFilename;
76+
7477
/// Output file name to write aggregated fdata to
7578
StringRef OutputFDataName;
7679

@@ -92,16 +95,17 @@ class DataAggregator : public DataReader {
9295

9396
/// Launch a subprocess to read all perf samples and write them to an output
9497
/// file we will parse later
95-
bool launchPerfEventsNoWait(StringRef PerfDataFilename);
98+
bool launchPerfEventsNoWait();
9699

97100
/// Launch a subprocess to read all perf task events. They contain the mapping
98101
/// of binary file name to PIDs used during data collection time. We later use
99102
/// the PIDs to filter samples.
100-
bool launchPerfTasksNoWait(StringRef PerfDataFilename);
103+
bool launchPerfTasksNoWait();
101104

102105
/// Delete all temporary files created to hold the output generated by spawned
103106
/// subprocesses during the aggregation job
104107
void deleteTempFiles();
108+
void deleteTempFile(StringRef File);
105109

106110
// Semantic pass helpers
107111
/// Look up which function contains an address by using out map of
@@ -159,6 +163,13 @@ class DataAggregator : public DataReader {
159163
/// events with the association of binary file names and their PIDs.
160164
std::error_code parseTasks();
161165

166+
/// Parse a single pair of binary full path and associated build-id
167+
Optional<std::pair<StringRef, StringRef>> parseNameBuildIDPair();
168+
169+
/// Parse the output generated by perf buildid-list to extract the build-id
170+
/// of the binary used when collecting profiling
171+
Optional<StringRef> parsePerfBuildID();
172+
162173
public:
163174
DataAggregator(raw_ostream &Diag, StringRef BinaryName)
164175
: DataReader(Diag), BinaryName(llvm::sys::path::filename(BinaryName)) {}
@@ -174,6 +185,9 @@ class DataAggregator : public DataReader {
174185
/// job is in progress
175186
bool started() const { return Enabled; }
176187

188+
/// Force all subprocesses to stop and cancel aggregation
189+
void abort();
190+
177191
/// Dump data structures into a file readable by llvm-bolt
178192
std::error_code writeAggregatedFile() const;
179193

@@ -184,13 +198,17 @@ class DataAggregator : public DataReader {
184198
/// Check whether \p FileName is a perf.data file
185199
static bool checkPerfDataMagic(StringRef FileName);
186200

201+
/// Launch a subprocess with perf buildid-list to extract the build-id of the
202+
/// binary used when collecting profiling. Different than launchPerf*, this
203+
/// one spawns the subprocess and blocks. Then it parses the result and
204+
/// returns the build-id.
205+
Optional<std::string> getPerfBuildID();
206+
187207
/// Debugging dump methods
188208
void dump() const;
189209
void dump(const LBREntry &LBR) const;
190210
void dump(const PerfSample &Sample) const;
191211
};
192-
193-
194212
}
195213
}
196214

bolt/RewriteInstance.cpp

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,12 @@ AggregateOnly("aggregate-only",
325325
cl::Hidden,
326326
cl::cat(AggregatorCategory));
327327

328+
static cl::opt<bool>
329+
IgnoreBuildID("ignore-build-id",
330+
cl::desc("continue even if build-ids in input binary and perf.data mismatch"),
331+
cl::init(false),
332+
cl::cat(AggregatorCategory));
333+
328334
// Check against lists of functions from options if we should
329335
// optimize the function with a given name.
330336
bool shouldProcess(const BinaryFunction &Function) {
@@ -774,6 +780,86 @@ void RewriteInstance::discoverStorage() {
774780
NewTextSegmentOffset = NextAvailableOffset;
775781
}
776782

783+
Optional<std::string>
784+
RewriteInstance::getBuildID() {
785+
for (auto &Section : InputFile->sections()) {
786+
StringRef SectionName;
787+
Section.getName(SectionName);
788+
789+
if (SectionName != ".note.gnu.build-id")
790+
continue;
791+
792+
StringRef SectionContents;
793+
Section.getContents(SectionContents);
794+
795+
// Reading notes section (see Portable Formats Specification, Version 1.1,
796+
// pg 2-5, section "Note Section").
797+
DataExtractor DE = DataExtractor(SectionContents, true, 8);
798+
uint32_t Offset = 0;
799+
if (!DE.isValidOffset(Offset))
800+
return NoneType();
801+
uint32_t NameSz = DE.getU32(&Offset);
802+
if (!DE.isValidOffset(Offset))
803+
return NoneType();
804+
uint32_t DescSz = DE.getU32(&Offset);
805+
if (!DE.isValidOffset(Offset))
806+
return NoneType();
807+
uint32_t Type = DE.getU32(&Offset);
808+
809+
DEBUG(dbgs() << "NameSz = " << NameSz << "; DescSz = " << DescSz
810+
<< "; Type = " << Type << "\n");
811+
812+
// Type 3 is a GNU build-id note section
813+
if (Type != 3)
814+
return NoneType();
815+
816+
StringRef Name = SectionContents.slice(Offset, Offset + NameSz);
817+
Offset = RoundUpToAlignment(Offset + NameSz, 4);
818+
StringRef BinaryBuildID = SectionContents.slice(Offset, Offset + DescSz);
819+
if (Name.substr(0, 3) != "GNU")
820+
return NoneType();
821+
822+
std::string Str;
823+
raw_string_ostream OS(Str);
824+
auto CharIter = BinaryBuildID.bytes_begin();
825+
while (CharIter != BinaryBuildID.bytes_end()) {
826+
if (*CharIter < 0x10)
827+
OS << "0";
828+
OS << Twine::utohexstr(*CharIter);
829+
++CharIter;
830+
}
831+
outs() << "BOLT-INFO: Binary build-id is: " << OS.str() << "\n";
832+
return OS.str();
833+
}
834+
return NoneType();
835+
}
836+
837+
void RewriteInstance::checkBuildID() {
838+
auto FileBuildID = getBuildID();
839+
if (!FileBuildID) {
840+
outs() << "BOLT-WARNING: Build ID will not be checked because we could not "
841+
"read one from input binary\n";
842+
return;
843+
}
844+
auto PerfBuildID = DA.getPerfBuildID();
845+
if (!PerfBuildID) {
846+
outs() << "BOLT-WARNING: Build ID will not be checked because we could not "
847+
"read one from perf.data\n";
848+
return;
849+
}
850+
if (*FileBuildID == *PerfBuildID)
851+
return;
852+
853+
outs() << "BOLT-ERROR: Build ID mismatch! This indicates the input binary "
854+
"supplied for data aggregation is not the same recorded by perf "
855+
"when collecting profiling data.\n";
856+
857+
if (!opts::IgnoreBuildID) {
858+
DA.abort();
859+
exit(1);
860+
}
861+
}
862+
777863
void RewriteInstance::run() {
778864
if (!BC) {
779865
errs() << "BOLT-ERROR: failed to create a binary context\n";
@@ -806,6 +892,8 @@ void RewriteInstance::run() {
806892
(llvm::Triple::ArchType)InputFile->getArch())
807893
<< "\n";
808894

895+
if (DA.started())
896+
checkBuildID();
809897
unsigned PassNumber = 1;
810898
executeRewritePass({});
811899
if (opts::AggregateOnly)

0 commit comments

Comments
 (0)