Index: include/llvm/ProfileData/InstrProfReader.h =================================================================== --- include/llvm/ProfileData/InstrProfReader.h +++ include/llvm/ProfileData/InstrProfReader.h @@ -348,6 +348,9 @@ using OnDiskHashTableImplV3 = OnDiskIterableChainedHashTable; +template +class InstrProfReaderItaniumRemapper; + template class InstrProfReaderIndex : public InstrProfReaderIndexBase { private: @@ -355,6 +358,8 @@ typename HashTableImpl::data_iterator RecordIterator; uint64_t FormatVersion; + friend class InstrProfReaderItaniumRemapper; + public: InstrProfReaderIndex(const unsigned char *Buckets, const unsigned char *const Payload, @@ -391,6 +396,8 @@ private: /// The profile data file contents. std::unique_ptr DataBuffer; + /// The profile remapping file contents. + std::unique_ptr RemappingBuffer; /// The index into the profile data. std::unique_ptr Index; /// Profile summary data. @@ -404,8 +411,11 @@ const unsigned char *Cur); public: - IndexedInstrProfReader(std::unique_ptr DataBuffer) - : DataBuffer(std::move(DataBuffer)), RecordIndex(0) {} + IndexedInstrProfReader( + std::unique_ptr DataBuffer, + std::unique_ptr RemappingBuffer = nullptr) + : DataBuffer(std::move(DataBuffer)), + RemappingBuffer(std::move(RemappingBuffer)), RecordIndex(0) {} IndexedInstrProfReader(const IndexedInstrProfReader &) = delete; IndexedInstrProfReader &operator=(const IndexedInstrProfReader &) = delete; @@ -434,10 +444,11 @@ /// Factory method to create an indexed reader. static Expected> - create(const Twine &Path); + create(const Twine &Path, const Twine &RemappingPath = ""); static Expected> - create(std::unique_ptr Buffer); + create(std::unique_ptr Buffer, + std::unique_ptr RemappingBuffer = nullptr); // Used for testing purpose only. void setValueProfDataEndianness(support::endianness Endianness) { Index: include/llvm/ProfileData/ProfRemappingReader.h =================================================================== --- /dev/null +++ include/llvm/ProfileData/ProfRemappingReader.h @@ -0,0 +1,125 @@ +//===- ProfRemappingReader.h - Read profile remapping file ------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains definitions needed for reading and applying profile +// remapping files. +// +// NOTE: If you are making changes to this file format, please remember +// to document them in the Clang documentation at +// tools/clang/docs/UsersManual.rst. +// +// File format +// ----------- +// +// The profile remappings are written as an ASCII text file. Blank lines and +// lines starting with a # are ignored. All other lines specify a kind of +// mangled name fragment, along with two fragments of that kind that should +// be treated as equivalent, separated by spaces. +// +// See http://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling for a +// description of the Itanium name mangling scheme. +// +// The accepted fragment kinds are: +// +// * name A , such as 6foobar or St3__1 +// * type A , such as Ss or N4llvm9StringRefE +// * encoding An (a complete mangling without the leading _Z) +// +// For example: +// +// # Ignore int / long differences to allow profile reuse between +// # 32-bit and 64-bit builds with differing size_t / ptrdiff_t / intptr_t. +// type i l +// type j m +// +// # Ignore differences between libc++ and libstdc++, and between libstdc++'s +// # C++98 and C++11 ABIs. +// name 3std St3__1 +// name 3std St7__cxx11 +// +// # Substitutions must be remapped separately from namespace 'std' for now. +// name Sa NSt3__19allocatorE +// name Sb NSt3__112basic_stringE +// type Ss NSt3__112basic_stringIcSt11char_traitsIcESaE +// # ... +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_PROFILEDATA_PROFREMAPPINGREADER_H +#define LLVM_PROFILEDATA_PROFREMAPPINGREADER_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/Support/ItaniumManglingCanonicalizer.h" +#include "llvm/Support/MemoryBuffer.h" + +namespace llvm { + +class ProfileRemappingParseError + : public ErrorInfo { +public: + ProfileRemappingParseError(StringRef File, int64_t Line, Twine Message) + : File(File), Line(Line), Message(Message.str()) {} + + void log(llvm::raw_ostream &OS) const override { + OS << File << ':' << Line << ": " << Message; + } + std::error_code convertToErrorCode() const override { + return llvm::inconvertibleErrorCode(); + } + + StringRef getFileName() const { return File; } + int64_t getLineNum() const { return Line; } + StringRef getMessage() const { return Message; } + + static char ID; + +private: + std::string File; + int64_t Line; + std::string Message; +}; + +/// Profile name remapper. +/// +/// Remaps the symbol names in profile data to match those in the program +/// according to a set of rules specified in a given file. +class ItaniumProfileRemappingReader { +public: + /// Read remappings from the given buffer, which must live as long as + /// the remapper. + Error read(MemoryBuffer &B); + + using Key = ItaniumManglingCanonicalizer::Key; + + /// Map the given function name from the profiling data into a profile + /// data key. The result will be Key() if the name cannot be remapped. + Key remap(StringRef FunctionName) { + return Canonicalizer.canonicalize(FunctionName); + } + + /// Map the given function name from the program into a profile data key. + /// + /// The result will be Key() if the name has no corresponding entry in the + /// profile data, in which case the original name should be looked up in the + /// profile data. + /// + /// There is no guarantee that return values other than Key() have ever been + /// returned by remap(...), but this should be rare. + Key lookup(StringRef FunctionName) { + return Canonicalizer.lookup(FunctionName); + } + +private: + ItaniumManglingCanonicalizer Canonicalizer; +}; + +} // end namespace llvm + +#endif // LLVM_PROFILEDATA_PROFREMAPPINGREADER_H Index: include/llvm/ProfileData/SampleProfReader.h =================================================================== --- include/llvm/ProfileData/SampleProfReader.h +++ include/llvm/ProfileData/SampleProfReader.h @@ -219,6 +219,7 @@ #include "llvm/IR/ProfileSummary.h" #include "llvm/ProfileData/GCOV.h" #include "llvm/ProfileData/SampleProf.h" +#include "llvm/ProfileData/ProfRemappingReader.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/MemoryBuffer.h" @@ -287,11 +288,16 @@ // The function name may have been updated by adding suffix. In sample // profile, the function names are all stripped, so we need to strip // 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; Fname = getRepInFormat(Fname, getFormat(), FGUID); - if (Profiles.count(Fname)) - return &Profiles[Fname]; + auto It = Profiles.find(Fname); + if (It != Profiles.end()) + return &It->second; return nullptr; } @@ -335,6 +341,12 @@ /// Profile summary information. std::unique_ptr Summary; + /// Take ownership of the summary of this reader. + static std::unique_ptr + takeSummary(SampleProfileReader &Reader) { + return std::move(Reader.Summary); + } + /// Compute summary for this profile. void computeSummary(); @@ -503,6 +515,37 @@ static const uint32_t GCOVTagAFDOFunction = 0xac000000; }; +class SampleProfileReaderItaniumRemapper : public SampleProfileReader { +public: + SampleProfileReaderItaniumRemapper( + std::unique_ptr B, LLVMContext &C, + std::unique_ptr 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> + create(const Twine &Filename, LLVMContext &C, + std::unique_ptr Underlying); + + /// Read and validate the file header. + std::error_code readHeader() override { return sampleprof_error::success; } + + /// Read sample profiles from the associated file. + std::error_code read() override; + + /// Return the samples collected for function \p F. + FunctionSamples *getSamplesFor(StringRef FunctionName) override; + using SampleProfileReader::getSamplesFor; + +private: + ItaniumProfileRemappingReader Remappings; + DenseMap SampleMap; +}; + } // end namespace sampleprof } // end namespace llvm Index: lib/ProfileData/CMakeLists.txt =================================================================== --- lib/ProfileData/CMakeLists.txt +++ lib/ProfileData/CMakeLists.txt @@ -4,6 +4,7 @@ InstrProfReader.cpp InstrProfWriter.cpp ProfileSummaryBuilder.cpp + ProfRemappingReader.cpp SampleProf.cpp SampleProfReader.cpp SampleProfWriter.cpp Index: lib/ProfileData/InstrProfReader.cpp =================================================================== --- lib/ProfileData/InstrProfReader.cpp +++ lib/ProfileData/InstrProfReader.cpp @@ -19,6 +19,7 @@ #include "llvm/IR/ProfileSummary.h" #include "llvm/ProfileData/InstrProf.h" #include "llvm/ProfileData/ProfileCommon.h" +#include "llvm/ProfileData/ProfRemappingReader.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorOr.h" @@ -88,16 +89,29 @@ } Expected> -IndexedInstrProfReader::create(const Twine &Path) { +IndexedInstrProfReader::create(const Twine &Path, const Twine &RemappingPath) { // Set up the buffer to read. auto BufferOrError = setupMemoryBuffer(Path); if (Error E = BufferOrError.takeError()) return std::move(E); - return IndexedInstrProfReader::create(std::move(BufferOrError.get())); + + // Set up the remapping buffer if requested. + std::unique_ptr RemappingBuffer; + std::string RemappingPathStr = RemappingPath.str(); + if (!RemappingPathStr.empty()) { + auto RemappingBufferOrError = setupMemoryBuffer(RemappingPathStr); + if (Error E = RemappingBufferOrError.takeError()) + return std::move(E); + RemappingBuffer = std::move(RemappingBufferOrError.get()); + } + + return IndexedInstrProfReader::create(std::move(BufferOrError.get()), + std::move(RemappingBuffer)); } Expected> -IndexedInstrProfReader::create(std::unique_ptr Buffer) { +IndexedInstrProfReader::create(std::unique_ptr Buffer, + std::unique_ptr RemappingBuffer) { // Sanity check the buffer. if (uint64_t(Buffer->getBufferSize()) > std::numeric_limits::max()) return make_error(instrprof_error::too_large); @@ -105,7 +119,8 @@ // Create the reader. if (!IndexedInstrProfReader::hasFormat(*Buffer)) return make_error(instrprof_error::bad_magic); - auto Result = llvm::make_unique(std::move(Buffer)); + auto Result = llvm::make_unique( + std::move(Buffer), std::move(RemappingBuffer)); // Initialize the reader and return the result. if (Error E = initializeReader(*Result)) @@ -587,6 +602,124 @@ RecordIterator = HashTable->data_begin(); } +template +class llvm::InstrProfReaderItaniumRemapper : public InstrProfReaderIndexBase { +public: + InstrProfReaderItaniumRemapper( + std::unique_ptr RemapBuffer, + std::unique_ptr> Underlying) + : RemapBuffer(std::move(RemapBuffer)), Underlying(std::move(Underlying)) { + } + + /// Extract the original function name from a PGO function name. + static StringRef extractName(StringRef Name) { + // We can have multiple :-separated pieces; there can be pieces both + // before and after the mangled name. Find the first part that starts + // with '_Z'; we'll assume that's the mangled name we want. + std::pair Parts = {StringRef(), Name}; + while (true) { + Parts = Parts.second.split(':'); + if (Parts.first.startswith("_Z")) + return Parts.first; + if (Parts.second.empty()) + return Name; + } + } + + /// Given a mangled name extracted from a PGO function name, and a new + /// form for that mangled name, reconstitute the name. + static void reconstituteName(StringRef OrigName, StringRef ExtractedName, + StringRef Replacement, + llvm::SmallVectorImpl &Out) { + Out.reserve(OrigName.size() + Replacement.size() - ExtractedName.size()); + Out.insert(Out.end(), OrigName.begin(), ExtractedName.begin()); + Out.insert(Out.end(), Replacement.begin(), Replacement.end()); + Out.insert(Out.end(), ExtractedName.end(), OrigName.end()); + } + + Error populateRemappings() { + if (Error E = Remappings.read(*RemapBuffer)) + return E; + for (StringRef Name : Underlying->HashTable->keys()) { + StringRef RealName = extractName(Name); + if (auto Key = Remappings.remap(RealName)) { + // FIXME: We could theoretically map the same equivalence class to + // multiple names in the profile data. If that happens, we should + // return NamedInstrProfRecords from all of them. + MappedNames.insert({Key, RealName}); + } + } + return Error::success(); + } + + Error getRecords(StringRef FuncName, + ArrayRef &Data) override { + StringRef RealName = extractName(FuncName); + if (auto Key = Remappings.lookup(RealName)) { + StringRef Remapped = MappedNames.lookup(Key); + if (!Remapped.empty()) { + if (RealName.begin() == FuncName.begin() && + RealName.end() == FuncName.end()) + FuncName = Remapped; + else { + // Try rebuilding the name from the given remapping. + llvm::SmallString<256> Reconstituted; + reconstituteName(FuncName, RealName, Remapped, Reconstituted); + Error E = Underlying->getRecords(Reconstituted, Data); + if (!E) + return E; + + // If we failed because the name doesn't exist, fall back to asking + // about the original name. + if (Error Unhandled = handleErrors( + std::move(E), [](std::unique_ptr Err) { + return Err->get() == instrprof_error::unknown_function + ? Error::success() + : Error(std::move(Err)); + })) + return Unhandled; + } + } + } + return Underlying->getRecords(FuncName, Data); + } + + Error getRecords(ArrayRef &Data) override { + return Underlying->getRecords(Data); + } + void advanceToNextKey() override { return Underlying->advanceToNextKey(); } + bool atEnd() const override { return Underlying->atEnd(); } + void setValueProfDataEndianness(support::endianness Endianness) override { + return Underlying->setValueProfDataEndianness(Endianness); + } + uint64_t getVersion() const override { + return Underlying->getVersion(); + } + bool isIRLevelProfile() const override { + return Underlying->isIRLevelProfile(); + } + Error populateSymtab(InstrProfSymtab &Symtab) override { + return Underlying->populateSymtab(Symtab); + } + +private: + /// The memory buffer containing the remapping configuration. Remappings + /// holds pointers into this buffer. + std::unique_ptr RemapBuffer; + + /// The mangling remapper. + ItaniumProfileRemappingReader Remappings; + + /// Mapping from mangled name keys to the name used for the key in the + /// profile data. + /// FIXME: Can we store a location within the on-disk hash table instead of + /// redoing lookup? + llvm::DenseMap MappedNames; + + /// The real profile data reader. + std::unique_ptr> Underlying; +}; + bool IndexedInstrProfReader::hasFormat(const MemoryBuffer &DataBuffer) { using namespace support; @@ -683,10 +816,22 @@ uint64_t HashOffset = endian::byte_swap(Header->HashOffset); // The rest of the file is an on disk hash table. - InstrProfReaderIndexBase *IndexPtr = nullptr; - IndexPtr = new InstrProfReaderIndex( - Start + HashOffset, Cur, Start, HashType, FormatVersion); - Index.reset(IndexPtr); + auto IndexPtr = + llvm::make_unique>( + Start + HashOffset, Cur, Start, HashType, FormatVersion); + + // Load the remapping table now if requested. + if (RemappingBuffer) { + auto Remapper = llvm::make_unique< + InstrProfReaderItaniumRemapper>( + std::move(RemappingBuffer), std::move(IndexPtr)); + if (Error E = Remapper->populateRemappings()) + return E; + Index = std::move(Remapper); + } else { + Index = std::move(IndexPtr); + } + return success(); } Index: lib/ProfileData/ProfRemappingReader.cpp =================================================================== --- /dev/null +++ lib/ProfileData/ProfRemappingReader.cpp @@ -0,0 +1,83 @@ +//===- ProfRemappingReader.cpp - Read profile remapping file --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains definitions needed for reading and applying profile +// remapping files. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ProfileData/ProfRemappingReader.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Twine.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/DiagnosticInfo.h" +#include "llvm/Support/LineIterator.h" + +using namespace llvm; + +char ProfileRemappingParseError::ID; + +/// Load a set of name remappings from a text file. +/// +/// See the documentation at the top of the file for an explanation of +/// the expected format. +Error ItaniumProfileRemappingReader::read(MemoryBuffer &B) { + line_iterator LineIt(B, /*SkipBlanks=*/true, '#'); + + auto ReportError = [&](Twine Msg) { + return llvm::make_error( + B.getBufferIdentifier(), LineIt.line_number(), Msg); + }; + + for (; !LineIt.is_at_eof(); ++LineIt) { + StringRef Line = *LineIt; + Line = Line.ltrim(' '); + // line_iterator only detects comments starting in column 1. + if (Line.startswith("#") || Line.empty()) + continue; + + SmallVector Parts; + Line.split(Parts, ' ', /*MaxSplits*/-1, /*KeepEmpty*/false); + + if (Parts.size() != 3) + return ReportError("Expected 'kind mangled_name mangled_name', " + "found '" + Line + "'"); + + using FK = ItaniumManglingCanonicalizer::FragmentKind; + Optional FragmentKind = StringSwitch>(Parts[0]) + .Case("name", FK::Name) + .Case("type", FK::Type) + .Case("encoding", FK::Encoding) + .Default(None); + if (!FragmentKind) + return ReportError("Invalid kind, expected 'name', 'type', or 'encoding'," + " found '" + Parts[0] + "'"); + + using EE = ItaniumManglingCanonicalizer::EquivalenceError; + switch (Canonicalizer.addEquivalence(*FragmentKind, Parts[1], Parts[2])) { + case EE::Success: + break; + + case EE::ManglingAlreadyUsed: + return ReportError("Manglings '" + Parts[1] + "' and '" + Parts[2] + "' " + "have both been used in prior remappings. Move this " + "remapping earlier in the file."); + + case EE::InvalidFirstMangling: + return ReportError("Could not demangle '" + Parts[1] + "' " + "as a <" + Parts[0] + ">; invalid mangling?"); + + case EE::InvalidSecondMangling: + return ReportError("Could not demangle '" + Parts[2] + "' " + "as a <" + Parts[0] + ">; invalid mangling?"); + } + } + + return Error::success(); +} Index: lib/ProfileData/SampleProfReader.cpp =================================================================== --- lib/ProfileData/SampleProfReader.cpp +++ lib/ProfileData/SampleProfReader.cpp @@ -827,6 +827,40 @@ 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 ProfileRemappingParseError &ParseError) { + reportError(ParseError.getLineNum(), ParseError.getMessage()); + }); + return sampleprof_error::malformed; + } + + for (auto &Sample : getProfiles()) + if (auto Key = Remappings.remap(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. /// /// \returns an error code indicating the status of the buffer. @@ -859,6 +893,27 @@ 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> +SampleProfileReaderItaniumRemapper::create( + const Twine &Filename, LLVMContext &C, + std::unique_ptr Underlying) { + auto BufferOrError = setupMemoryBuffer(Filename); + if (std::error_code EC = BufferOrError.getError()) + return EC; + return llvm::make_unique( + std::move(BufferOrError.get()), C, std::move(Underlying)); +} + /// 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). Index: unittests/ProfileData/InstrProfTest.cpp =================================================================== --- unittests/ProfileData/InstrProfTest.cpp +++ unittests/ProfileData/InstrProfTest.cpp @@ -42,8 +42,10 @@ void SetUp() { Writer.setOutputSparse(false); } - void readProfile(std::unique_ptr Profile) { - auto ReaderOrErr = IndexedInstrProfReader::create(std::move(Profile)); + void readProfile(std::unique_ptr Profile, + std::unique_ptr Remapping = nullptr) { + auto ReaderOrErr = IndexedInstrProfReader::create(std::move(Profile), + std::move(Remapping)); EXPECT_THAT_ERROR(ReaderOrErr.takeError(), Succeeded()); Reader = std::move(ReaderOrErr.get()); } @@ -990,6 +992,44 @@ } } +TEST_P(MaybeSparseInstrProfTest, remapping_test) { + Writer.addRecord({"_Z3fooi", 0x1234, {1, 2, 3, 4}}, Err); + Writer.addRecord({"file:_Z3barf", 0x567, {5, 6, 7}}, Err); + auto Profile = Writer.writeBuffer(); + readProfile(std::move(Profile), llvm::MemoryBuffer::getMemBuffer(R"( + type i l + name 3bar 4quux + )")); + + std::vector Counts; + for (StringRef FooName : {"_Z3fooi", "_Z3fool"}) { + EXPECT_THAT_ERROR(Reader->getFunctionCounts(FooName, 0x1234, Counts), + Succeeded()); + ASSERT_EQ(4u, Counts.size()); + EXPECT_EQ(1u, Counts[0]); + EXPECT_EQ(2u, Counts[1]); + EXPECT_EQ(3u, Counts[2]); + EXPECT_EQ(4u, Counts[3]); + } + + for (StringRef BarName : {"file:_Z3barf", "file:_Z4quuxf"}) { + EXPECT_THAT_ERROR(Reader->getFunctionCounts(BarName, 0x567, Counts), + Succeeded()); + ASSERT_EQ(3u, Counts.size()); + EXPECT_EQ(5u, Counts[0]); + EXPECT_EQ(6u, Counts[1]); + EXPECT_EQ(7u, Counts[2]); + } + + for (StringRef BadName : {"_Z3foof", "_Z4quuxi", "_Z3barl", "", "_ZZZ", + "_Z3barf", "otherfile:_Z4quuxf"}) { + EXPECT_THAT_ERROR(Reader->getFunctionCounts(BadName, 0x1234, Counts), + Failed()); + EXPECT_THAT_ERROR(Reader->getFunctionCounts(BadName, 0x567, Counts), + Failed()); + } +} + TEST_F(SparseInstrProfTest, preserve_no_records) { Writer.addRecord({"foo", 0x1234, {0}}, Err); Writer.addRecord({"bar", 0x4321, {0, 0}}, Err); Index: unittests/ProfileData/SampleProfTest.cpp =================================================================== --- unittests/ProfileData/SampleProfTest.cpp +++ unittests/ProfileData/SampleProfTest.cpp @@ -57,7 +57,7 @@ Reader = std::move(ReaderOrErr.get()); } - void testRoundTrip(SampleProfileFormat Format) { + void testRoundTrip(SampleProfileFormat Format, bool Remap) { createWriter(Format); StringRef FooName("_Z3fooi"); @@ -99,22 +99,35 @@ EC = Reader->read(); ASSERT_TRUE(NoError(EC)); - StringMap &ReadProfiles = Reader->getProfiles(); - ASSERT_EQ(2u, ReadProfiles.size()); - - std::string FooGUID; - StringRef FooRep = getRepInFormat(FooName, Format, FooGUID); - FunctionSamples &ReadFooSamples = ReadProfiles[FooRep]; - ASSERT_EQ(7711u, ReadFooSamples.getTotalSamples()); - ASSERT_EQ(610u, ReadFooSamples.getHeadSamples()); - - std::string BarGUID; - StringRef BarRep = getRepInFormat(BarName, Format, BarGUID); - FunctionSamples &ReadBarSamples = ReadProfiles[BarRep]; - ASSERT_EQ(20301u, ReadBarSamples.getTotalSamples()); - ASSERT_EQ(1437u, ReadBarSamples.getHeadSamples()); + if (Remap) { + 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"; + + EC = Reader->read(); + ASSERT_TRUE(NoError(EC)); + } + + ASSERT_EQ(2u, Reader->getProfiles().size()); + + FunctionSamples *ReadFooSamples = Reader->getSamplesFor(FooName); + ASSERT_TRUE(ReadFooSamples != nullptr); + 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 CTMap = - ReadBarSamples.findCallTargetMapAt(1, 0); + ReadBarSamples->findCallTargetMapAt(1, 0); ASSERT_FALSE(CTMap.getError()); std::string MconstructGUID; @@ -176,15 +189,19 @@ }; TEST_F(SampleProfTest, roundtrip_text_profile) { - testRoundTrip(SampleProfileFormat::SPF_Text); + testRoundTrip(SampleProfileFormat::SPF_Text, false); } TEST_F(SampleProfTest, roundtrip_raw_binary_profile) { - testRoundTrip(SampleProfileFormat::SPF_Binary); + testRoundTrip(SampleProfileFormat::SPF_Binary, false); } 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, sample_overflow_saturation) {