[perf2bolt] Enforce file matching in perf2bolt

Summary:
If the input binary does not have a build-id and the name does not match
any file names in perf.data, then reject the binary, and issue an error
message suggesting to rename it to one of the listed names from
perf.data.

(cherry picked from FBD8846181)
This commit is contained in:
Maksim Panchenko 2018-07-13 15:26:41 -07:00
parent f2f164f474
commit 6e45f5aeec
2 changed files with 68 additions and 37 deletions

View File

@ -23,6 +23,7 @@
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Regex.h"
#include "llvm/Support/Timer.h"
#include <map>
#include <unistd.h>
@ -309,9 +310,8 @@ void DataAggregator::processFileBuildID(StringRef FileBuildID) {
exit(1);
}
} else if (*FileName != BinaryName) {
errs() << "PERF2BOLT-WARNING: build-id matched a different file name. "
"Using \"" << *FileName << "\" for profile parsing.\n";
BinaryName = *FileName;
errs() << "PERF2BOLT-WARNING: build-id matched a different file name\n";
BuildIDBinaryName = *FileName;
} else {
outs() << "PERF2BOLT: matched build-id and file name\n";
}
@ -991,39 +991,37 @@ std::error_code DataAggregator::parseMemEvents() {
return std::error_code();
}
ErrorOr<int64_t> DataAggregator::parseTaskPID() {
ErrorOr<std::pair<StringRef, int64_t>> DataAggregator::parseTaskPID() {
while (checkAndConsumeFS()) {}
auto CommNameStr = parseString(FieldSeparator, true);
if (std::error_code EC = CommNameStr.getError())
return EC;
if (CommNameStr.get() != BinaryName.substr(0, 15)) {
consumeRestOfLine();
return -1;
}
auto LineEnd = ParsingBuf.find_first_of("\n");
if (LineEnd == StringRef::npos) {
reportError("expected rest of line");
Diag << "Found: " << ParsingBuf << "\n";
return make_error_code(llvm::errc::io_error);
}
StringRef Line = ParsingBuf.substr(0, LineEnd);
if (Line.find("PERF_RECORD_COMM") != StringRef::npos) {
int64_t PID;
StringRef PIDStr = Line.rsplit(':').second.split('/').first;
if (PIDStr.getAsInteger(10, PID)) {
reportError("expected PID");
Diag << "Found: " << PIDStr << "\n";
return make_error_code(llvm::errc::io_error);
}
return PID;
if (Line.find("PERF_RECORD_COMM") == StringRef::npos) {
consumeRestOfLine();
return std::make_pair(StringRef(), -1);
}
auto FileName = Line.split(FieldSeparator).first;
if (FileName == "PERF_RECORD_COMM")
FileName = Line.rsplit(':').first.rsplit(FieldSeparator).second;
int64_t PID;
StringRef PIDStr = Line.rsplit(':').second.split('/').first;
if (PIDStr.getAsInteger(10, PID)) {
reportError("expected PID");
Diag << "Found: " << PIDStr << "\n";
return make_error_code(llvm::errc::io_error);
}
consumeRestOfLine();
return -1;
return std::make_pair(FileName, PID);
}
std::error_code DataAggregator::parseTasks() {
@ -1031,30 +1029,59 @@ std::error_code DataAggregator::parseTasks() {
NamedRegionTimer T("parseTasks", "Tasks parsing", TimerGroupName,
TimerGroupDesc, opts::TimeAggregator);
std::multimap<StringRef, int64_t> BinaryPIDs;
while (hasData()) {
auto PIDRes = parseTaskPID();
if (std::error_code EC = PIDRes.getError())
auto NamePIDRes = parseTaskPID();
if (std::error_code EC = NamePIDRes.getError())
return EC;
auto PID = PIDRes.get();
if (PID == -1) {
auto NamePIDPair = NamePIDRes.get();
if (NamePIDPair.second == -1)
continue;
}
PIDs.insert(PID);
BinaryPIDs.insert(NamePIDPair);
}
DEBUG(
dbgs() << "FileName -> PID mapping:\n";
for (const auto &Pair : BinaryPIDs) {
dbgs() << " " << Pair.first << " : " << Pair.second << '\n';
}
);
auto NameToUse = BinaryName.substr(0, 15);
if (BinaryPIDs.count(NameToUse) == 0 && !BuildIDBinaryName.empty()) {
errs() << "PERF2BOLT-WARNING: using \"" << BuildIDBinaryName
<< "\" for profile matching\n";
NameToUse = BuildIDBinaryName.substr(0, 15);
}
auto Range = BinaryPIDs.equal_range(NameToUse);
for (auto I = Range.first; I != Range.second; ++I) {
PIDs.insert(I->second);
}
if (!PIDs.empty()) {
outs() << "PERF2BOLT: Input binary is associated with " << PIDs.size()
<< " PID(s)\n";
} else {
if (errs().has_colors())
errs().changeColor(raw_ostream::YELLOW);
errs() << "PERF2BOLT-WARNING: Could not bind input binary to a PID - will "
"parse all samples in perf data. This could result in corrupted "
"samples for the input binary if system-wide profile collection "
"was used.\n";
errs().changeColor(raw_ostream::RED);
errs() << "PERF2BOLT-ERROR: could not find a profile matching binary \""
<< BinaryName << "\".";
if (!BinaryPIDs.empty()) {
errs() << " Profile for the following binary name(s) is available:\n";
for (auto I = BinaryPIDs.begin(), IE = BinaryPIDs.end(); I != IE;
I = BinaryPIDs.upper_bound(I->first)) {
errs() << " " << I->first << '\n';
}
errs() << "Please rename the input binary.\n";
} else {
errs() << " Failed to extract any binary name from a profile.\n";
}
if (errs().has_colors())
errs().resetColor();
exit(1);
}
return std::error_code();

View File

@ -87,6 +87,10 @@ class DataAggregator : public DataReader {
/// Our sampled binary name to look for in perf.data
std::string BinaryName;
/// Name of the binary with matching build-id from perf.data if different
/// from BinaryName;
std::string BuildIDBinaryName;
DenseSet<int64_t> PIDs;
/// References to core BOLT data structures
@ -178,9 +182,9 @@ class DataAggregator : public DataReader {
std::error_code parseMemEvents();
/// Parse a single line of a PERF_RECORD_COMM event looking for an association
/// between the binary name and its PID. Return -1 if binary name is not
/// correct.
ErrorOr<int64_t> parseTaskPID();
/// between the binary name and its PID. On success return a <FileName, PID>
/// pair.
ErrorOr<std::pair<StringRef, int64_t>> parseTaskPID();
/// Parse the full output generated by perf script to report PERF_RECORD_COMM
/// events with the association of binary file names and their PIDs.