Support for remapping profile data when symbols change, for sample-based
profiling. Reviewers: davidxl, tejohnson, dlj, erik.pilkington Subscribers: llvm-commits Differential Revision: https://reviews.llvm.org/D51248 llvm-svn: 344187
This commit is contained in:
parent
6ef8002c2c
commit
2843635829
|
@ -222,6 +222,7 @@
|
||||||
#include "llvm/Support/Debug.h"
|
#include "llvm/Support/Debug.h"
|
||||||
#include "llvm/Support/ErrorOr.h"
|
#include "llvm/Support/ErrorOr.h"
|
||||||
#include "llvm/Support/MemoryBuffer.h"
|
#include "llvm/Support/MemoryBuffer.h"
|
||||||
|
#include "llvm/Support/SymbolRemappingReader.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
@ -289,11 +290,16 @@ public:
|
||||||
// The function name may have been updated by adding suffix. In sample
|
// The function name may have been updated by adding suffix. In sample
|
||||||
// profile, the function names are all stripped, so we need to strip
|
// profile, the function names are all stripped, so we need to strip
|
||||||
// the function name suffix before matching with profile.
|
// the function name suffix before matching with profile.
|
||||||
StringRef Fname = F.getName().split('.').first;
|
return getSamplesFor(F.getName().split('.').first);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the samples collected for function \p F.
|
||||||
|
virtual FunctionSamples *getSamplesFor(StringRef Fname) {
|
||||||
std::string FGUID;
|
std::string FGUID;
|
||||||
Fname = getRepInFormat(Fname, getFormat(), FGUID);
|
Fname = getRepInFormat(Fname, getFormat(), FGUID);
|
||||||
if (Profiles.count(Fname))
|
auto It = Profiles.find(Fname);
|
||||||
return &Profiles[Fname];
|
if (It != Profiles.end())
|
||||||
|
return &It->second;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,6 +343,12 @@ protected:
|
||||||
/// Profile summary information.
|
/// Profile summary information.
|
||||||
std::unique_ptr<ProfileSummary> Summary;
|
std::unique_ptr<ProfileSummary> Summary;
|
||||||
|
|
||||||
|
/// Take ownership of the summary of this reader.
|
||||||
|
static std::unique_ptr<ProfileSummary>
|
||||||
|
takeSummary(SampleProfileReader &Reader) {
|
||||||
|
return std::move(Reader.Summary);
|
||||||
|
}
|
||||||
|
|
||||||
/// Compute summary for this profile.
|
/// Compute summary for this profile.
|
||||||
void computeSummary();
|
void computeSummary();
|
||||||
|
|
||||||
|
@ -525,6 +537,40 @@ protected:
|
||||||
static const uint32_t GCOVTagAFDOFunction = 0xac000000;
|
static const uint32_t GCOVTagAFDOFunction = 0xac000000;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// A profile data reader proxy that remaps the profile data from another
|
||||||
|
/// sample profile data reader, by applying a provided set of equivalences
|
||||||
|
/// between components of the symbol names in the profile.
|
||||||
|
class SampleProfileReaderItaniumRemapper : public SampleProfileReader {
|
||||||
|
public:
|
||||||
|
SampleProfileReaderItaniumRemapper(
|
||||||
|
std::unique_ptr<MemoryBuffer> B, LLVMContext &C,
|
||||||
|
std::unique_ptr<SampleProfileReader> Underlying)
|
||||||
|
: SampleProfileReader(std::move(B), C, Underlying->getFormat()) {
|
||||||
|
Profiles = std::move(Underlying->getProfiles());
|
||||||
|
Summary = takeSummary(*Underlying);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a remapped sample profile from the given remapping file and
|
||||||
|
/// underlying samples.
|
||||||
|
static ErrorOr<std::unique_ptr<SampleProfileReader>>
|
||||||
|
create(const Twine &Filename, LLVMContext &C,
|
||||||
|
std::unique_ptr<SampleProfileReader> Underlying);
|
||||||
|
|
||||||
|
/// Read and validate the file header.
|
||||||
|
std::error_code readHeader() override { return sampleprof_error::success; }
|
||||||
|
|
||||||
|
/// Read remapping file and apply it to the sample profile.
|
||||||
|
std::error_code read() override;
|
||||||
|
|
||||||
|
/// Return the samples collected for function \p F.
|
||||||
|
FunctionSamples *getSamplesFor(StringRef FunctionName) override;
|
||||||
|
using SampleProfileReader::getSamplesFor;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SymbolRemappingReader Remappings;
|
||||||
|
DenseMap<SymbolRemappingReader::Key, FunctionSamples*> SampleMap;
|
||||||
|
};
|
||||||
|
|
||||||
} // end namespace sampleprof
|
} // end namespace sampleprof
|
||||||
|
|
||||||
} // end namespace llvm
|
} // end namespace llvm
|
||||||
|
|
|
@ -912,6 +912,40 @@ bool SampleProfileReaderGCC::hasFormat(const MemoryBuffer &Buffer) {
|
||||||
return Magic == "adcg*704";
|
return Magic == "adcg*704";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::error_code SampleProfileReaderItaniumRemapper::read() {
|
||||||
|
// If the underlying data is in compact format, we can't remap it because
|
||||||
|
// we don't know what the original function names were.
|
||||||
|
if (getFormat() == SPF_Compact_Binary) {
|
||||||
|
Ctx.diagnose(DiagnosticInfoSampleProfile(
|
||||||
|
Buffer->getBufferIdentifier(),
|
||||||
|
"Profile data remapping cannot be applied to profile data "
|
||||||
|
"in compact format (original mangled names are not available).",
|
||||||
|
DS_Warning));
|
||||||
|
return sampleprof_error::success;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Error E = Remappings.read(*Buffer)) {
|
||||||
|
handleAllErrors(
|
||||||
|
std::move(E), [&](const SymbolRemappingParseError &ParseError) {
|
||||||
|
reportError(ParseError.getLineNum(), ParseError.getMessage());
|
||||||
|
});
|
||||||
|
return sampleprof_error::malformed;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &Sample : getProfiles())
|
||||||
|
if (auto Key = Remappings.insert(Sample.first()))
|
||||||
|
SampleMap.insert({Key, &Sample.second});
|
||||||
|
|
||||||
|
return sampleprof_error::success;
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionSamples *
|
||||||
|
SampleProfileReaderItaniumRemapper::getSamplesFor(StringRef Fname) {
|
||||||
|
if (auto Key = Remappings.lookup(Fname))
|
||||||
|
return SampleMap.lookup(Key);
|
||||||
|
return SampleProfileReader::getSamplesFor(Fname);
|
||||||
|
}
|
||||||
|
|
||||||
/// Prepare a memory buffer for the contents of \p Filename.
|
/// Prepare a memory buffer for the contents of \p Filename.
|
||||||
///
|
///
|
||||||
/// \returns an error code indicating the status of the buffer.
|
/// \returns an error code indicating the status of the buffer.
|
||||||
|
@ -944,6 +978,27 @@ SampleProfileReader::create(const Twine &Filename, LLVMContext &C) {
|
||||||
return create(BufferOrError.get(), C);
|
return create(BufferOrError.get(), C);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a sample profile remapper from the given input, to remap the
|
||||||
|
/// function names in the given profile data.
|
||||||
|
///
|
||||||
|
/// \param Filename The file to open.
|
||||||
|
///
|
||||||
|
/// \param C The LLVM context to use to emit diagnostics.
|
||||||
|
///
|
||||||
|
/// \param Underlying The underlying profile data reader to remap.
|
||||||
|
///
|
||||||
|
/// \returns an error code indicating the status of the created reader.
|
||||||
|
ErrorOr<std::unique_ptr<SampleProfileReader>>
|
||||||
|
SampleProfileReaderItaniumRemapper::create(
|
||||||
|
const Twine &Filename, LLVMContext &C,
|
||||||
|
std::unique_ptr<SampleProfileReader> Underlying) {
|
||||||
|
auto BufferOrError = setupMemoryBuffer(Filename);
|
||||||
|
if (std::error_code EC = BufferOrError.getError())
|
||||||
|
return EC;
|
||||||
|
return llvm::make_unique<SampleProfileReaderItaniumRemapper>(
|
||||||
|
std::move(BufferOrError.get()), C, std::move(Underlying));
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a sample profile reader based on the format of the input data.
|
/// Create a sample profile reader based on the format of the input data.
|
||||||
///
|
///
|
||||||
/// \param B The memory buffer to create the reader from (assumes ownership).
|
/// \param B The memory buffer to create the reader from (assumes ownership).
|
||||||
|
|
|
@ -58,7 +58,7 @@ struct SampleProfTest : ::testing::Test {
|
||||||
Reader->collectFuncsToUse(M);
|
Reader->collectFuncsToUse(M);
|
||||||
}
|
}
|
||||||
|
|
||||||
void testRoundTrip(SampleProfileFormat Format) {
|
void testRoundTrip(SampleProfileFormat Format, bool Remap) {
|
||||||
SmallVector<char, 128> ProfilePath;
|
SmallVector<char, 128> ProfilePath;
|
||||||
ASSERT_TRUE(NoError(llvm::sys::fs::createTemporaryFile("profile", "", ProfilePath)));
|
ASSERT_TRUE(NoError(llvm::sys::fs::createTemporaryFile("profile", "", ProfilePath)));
|
||||||
StringRef Profile(ProfilePath.data(), ProfilePath.size());
|
StringRef Profile(ProfilePath.data(), ProfilePath.size());
|
||||||
|
@ -108,22 +108,35 @@ struct SampleProfTest : ::testing::Test {
|
||||||
EC = Reader->read();
|
EC = Reader->read();
|
||||||
ASSERT_TRUE(NoError(EC));
|
ASSERT_TRUE(NoError(EC));
|
||||||
|
|
||||||
StringMap<FunctionSamples> &ReadProfiles = Reader->getProfiles();
|
if (Remap) {
|
||||||
ASSERT_EQ(2u, ReadProfiles.size());
|
auto MemBuffer = llvm::MemoryBuffer::getMemBuffer(R"(
|
||||||
|
# Types 'int' and 'long' are equivalent
|
||||||
|
type i l
|
||||||
|
# Function names 'foo' and 'faux' are equivalent
|
||||||
|
name 3foo 4faux
|
||||||
|
)");
|
||||||
|
Reader.reset(new SampleProfileReaderItaniumRemapper(
|
||||||
|
std::move(MemBuffer), Context, std::move(Reader)));
|
||||||
|
FooName = "_Z4fauxi";
|
||||||
|
BarName = "_Z3barl";
|
||||||
|
|
||||||
std::string FooGUID;
|
EC = Reader->read();
|
||||||
StringRef FooRep = getRepInFormat(FooName, Format, FooGUID);
|
ASSERT_TRUE(NoError(EC));
|
||||||
FunctionSamples &ReadFooSamples = ReadProfiles[FooRep];
|
}
|
||||||
ASSERT_EQ(7711u, ReadFooSamples.getTotalSamples());
|
|
||||||
ASSERT_EQ(610u, ReadFooSamples.getHeadSamples());
|
|
||||||
|
|
||||||
std::string BarGUID;
|
ASSERT_EQ(2u, Reader->getProfiles().size());
|
||||||
StringRef BarRep = getRepInFormat(BarName, Format, BarGUID);
|
|
||||||
FunctionSamples &ReadBarSamples = ReadProfiles[BarRep];
|
FunctionSamples *ReadFooSamples = Reader->getSamplesFor(FooName);
|
||||||
ASSERT_EQ(20301u, ReadBarSamples.getTotalSamples());
|
ASSERT_TRUE(ReadFooSamples != nullptr);
|
||||||
ASSERT_EQ(1437u, ReadBarSamples.getHeadSamples());
|
ASSERT_EQ(7711u, ReadFooSamples->getTotalSamples());
|
||||||
|
ASSERT_EQ(610u, ReadFooSamples->getHeadSamples());
|
||||||
|
|
||||||
|
FunctionSamples *ReadBarSamples = Reader->getSamplesFor(BarName);
|
||||||
|
ASSERT_TRUE(ReadBarSamples != nullptr);
|
||||||
|
ASSERT_EQ(20301u, ReadBarSamples->getTotalSamples());
|
||||||
|
ASSERT_EQ(1437u, ReadBarSamples->getHeadSamples());
|
||||||
ErrorOr<SampleRecord::CallTargetMap> CTMap =
|
ErrorOr<SampleRecord::CallTargetMap> CTMap =
|
||||||
ReadBarSamples.findCallTargetMapAt(1, 0);
|
ReadBarSamples->findCallTargetMapAt(1, 0);
|
||||||
ASSERT_FALSE(CTMap.getError());
|
ASSERT_FALSE(CTMap.getError());
|
||||||
|
|
||||||
std::string MconstructGUID;
|
std::string MconstructGUID;
|
||||||
|
@ -184,15 +197,23 @@ struct SampleProfTest : ::testing::Test {
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(SampleProfTest, roundtrip_text_profile) {
|
TEST_F(SampleProfTest, roundtrip_text_profile) {
|
||||||
testRoundTrip(SampleProfileFormat::SPF_Text);
|
testRoundTrip(SampleProfileFormat::SPF_Text, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SampleProfTest, roundtrip_raw_binary_profile) {
|
TEST_F(SampleProfTest, roundtrip_raw_binary_profile) {
|
||||||
testRoundTrip(SampleProfileFormat::SPF_Binary);
|
testRoundTrip(SampleProfileFormat::SPF_Binary, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SampleProfTest, roundtrip_compact_binary_profile) {
|
TEST_F(SampleProfTest, roundtrip_compact_binary_profile) {
|
||||||
testRoundTrip(SampleProfileFormat::SPF_Compact_Binary);
|
testRoundTrip(SampleProfileFormat::SPF_Compact_Binary, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SampleProfTest, remap_text_profile) {
|
||||||
|
testRoundTrip(SampleProfileFormat::SPF_Text, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SampleProfTest, remap_raw_binary_profile) {
|
||||||
|
testRoundTrip(SampleProfileFormat::SPF_Binary, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SampleProfTest, sample_overflow_saturation) {
|
TEST_F(SampleProfTest, sample_overflow_saturation) {
|
||||||
|
|
Loading…
Reference in New Issue