Index: tools/llvm-cxxmap/CMakeLists.txt =================================================================== --- /dev/null +++ tools/llvm-cxxmap/CMakeLists.txt @@ -0,0 +1,8 @@ +set(LLVM_LINK_COMPONENTS + Core + Support + ) + +add_llvm_tool(llvm-cxxmap + llvm-cxxmap.cpp + ) Index: tools/llvm-cxxmap/LLVMBuild.txt =================================================================== --- /dev/null +++ tools/llvm-cxxmap/LLVMBuild.txt @@ -0,0 +1,22 @@ +;===- ./tools/llvm-cxxmap/LLVMBuild.txt ------------------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Tool +name = llvm-cxxmap +parent = Tools +required_libraries = Support Index: tools/llvm-cxxmap/llvm-cxxmap.cpp =================================================================== --- /dev/null +++ tools/llvm-cxxmap/llvm-cxxmap.cpp @@ -0,0 +1,163 @@ +//===- llvm-cxxmap.cpp ----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// llvm-cxxmap computes a correspondence between old symbol names and new +// symbol names based on a symbol equivalence file. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/LineIterator.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/SymbolRemappingReader.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +cl::opt OldSymbolFile(cl::Positional, cl::Required, + cl::desc("")); +cl::opt NewSymbolFile(cl::Positional, cl::Required, + cl::desc("")); +cl::opt RemappingFile("remapping-file", cl::Required, + cl::desc("Remapping file")); +cl::alias RemappingFileA("r", cl::aliasopt(RemappingFile)); +cl::opt OutputFilename("output", cl::value_desc("output"), + cl::init("-"), cl::desc("Output file")); +cl::alias OutputFilenameA("o", cl::aliasopt(OutputFilename)); + +cl::opt WarnAmbiguous( + "Wambiguous", + cl::desc("Warn on equivalent symbols in the output symbol list")); +cl::opt WarnIncomplete( + "Wincomplete", + cl::desc("Warn on input symbols missing from output symbol list")); +cl::opt WarnInvalidMangling( + "Winvalid-mangling", + cl::desc("Warn on mangled names that cannot be demangled")); + +static void warn(Twine Message, Twine Whence = "", + std::string Hint = "") { + WithColor::warning(); + std::string WhenceStr = Whence.str(); + if (!WhenceStr.empty()) + errs() << WhenceStr << ": "; + errs() << Message << "\n"; + if (!Hint.empty()) + WithColor::note() << Hint << "\n"; +} + +static void exitWithError(Twine Message, Twine Whence = "", + std::string Hint = "") { + WithColor::error(); + std::string WhenceStr = Whence.str(); + if (!WhenceStr.empty()) + errs() << WhenceStr << ": "; + errs() << Message << "\n"; + if (!Hint.empty()) + WithColor::note() << Hint << "\n"; + ::exit(1); +} + +static void exitWithError(Error E, StringRef Whence = "") { + exitWithError(toString(std::move(E)), Whence); +} + +static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") { + exitWithError(EC.message(), Whence); +} + +static void remapSymbols(MemoryBuffer &OldSymbolFile, + MemoryBuffer &NewSymbolFile, + MemoryBuffer &RemappingFile, + raw_ostream &Out) { + // Load the remapping file and prepare to canonicalize symbols. + SymbolRemappingReader Reader; + if (Error E = Reader.read(RemappingFile)) + exitWithError(std::move(E)); + + // Canonicalize the new symbols. + DenseMap MappedNames; + DenseSet UnparseableSymbols; + for (line_iterator LineIt(NewSymbolFile, /*SkipBlanks=*/true, '#'); + !LineIt.is_at_eof(); ++LineIt) { + StringRef Symbol = *LineIt; + + auto K = Reader.insert(Symbol); + if (!K) { + if (WarnInvalidMangling && Symbol.startswith("_Z")) { + warn("could not demangle symbol " + Symbol, + NewSymbolFile.getBufferIdentifier() + ":" + + Twine(LineIt.line_number())); + } + UnparseableSymbols.insert(Symbol); + continue; + } + + auto ItAndIsNew = MappedNames.insert({K, Symbol}); + if (WarnAmbiguous && !ItAndIsNew.second && + ItAndIsNew.first->second != Symbol) { + warn("symbol " + Symbol + " is equivalent to earlier symbol " + + ItAndIsNew.first->second, + NewSymbolFile.getBufferIdentifier() + ":" + + Twine(LineIt.line_number()), + "later symbol will not be the target of any remappings"); + } + } + + // Figure out which new symbol each old symbol is equivalent to. + for (line_iterator LineIt(OldSymbolFile, /*SkipBlanks=*/true, '#'); + !LineIt.is_at_eof(); ++LineIt) { + StringRef Symbol = *LineIt; + + auto K = Reader.lookup(Symbol); + StringRef NewSymbol = MappedNames.lookup(K); + + if (NewSymbol.empty()) { + if (WarnIncomplete && !UnparseableSymbols.count(Symbol)) { + warn("no new symbol matches old symbol " + Symbol, + OldSymbolFile.getBufferIdentifier() + ":" + + Twine(LineIt.line_number())); + } + continue; + } + + Out << Symbol << " " << NewSymbol << "\n"; + } +} + +int main(int argc, const char *argv[]) { + InitLLVM X(argc, argv); + + cl::ParseCommandLineOptions(argc, argv, "LLVM C++ mangled name remapper\n"); + + auto OldSymbolBufOrError = MemoryBuffer::getFileOrSTDIN(OldSymbolFile); + if (!OldSymbolBufOrError) + exitWithErrorCode(OldSymbolBufOrError.getError(), OldSymbolFile); + + auto NewSymbolBufOrError = MemoryBuffer::getFileOrSTDIN(NewSymbolFile); + if (!NewSymbolBufOrError) + exitWithErrorCode(NewSymbolBufOrError.getError(), NewSymbolFile); + + auto RemappingBufOrError = MemoryBuffer::getFileOrSTDIN(RemappingFile); + if (!RemappingBufOrError) + exitWithErrorCode(RemappingBufOrError.getError(), RemappingFile); + + std::error_code EC; + raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::F_Text); + if (EC) + exitWithErrorCode(EC, OutputFilename); + + remapSymbols(*OldSymbolBufOrError.get(), *NewSymbolBufOrError.get(), + *RemappingBufOrError.get(), OS); +} Index: tools/llvm-profdata/llvm-profdata.cpp =================================================================== --- tools/llvm-profdata/llvm-profdata.cpp +++ tools/llvm-profdata/llvm-profdata.cpp @@ -123,6 +123,45 @@ } } +namespace { +/// A remapper from original symbol names to new symbol names based on a file +/// containing a list of mappings from old name to new name. +class SymbolRemapper { + std::unique_ptr File; + DenseMap RemappingTable; + +public: + /// Build a SymbolRemapper from a file containing a list of old/new symbols. + static std::unique_ptr create(StringRef InputFile) { + auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile); + if (!BufOrError) + exitWithErrorCode(BufOrError.getError(), InputFile); + + auto Remapper = llvm::make_unique(); + Remapper->File = std::move(BufOrError.get()); + + for (line_iterator LineIt(*Remapper->File, /*SkipBlanks=*/true, '#'); + !LineIt.is_at_eof(); ++LineIt) { + std::pair Parts = LineIt->split(' '); + if (Parts.first.empty() || Parts.second.empty() || + Parts.second.count(' ')) { + exitWithError("unexpected line in remapping file", + (InputFile + ":" + Twine(LineIt.line_number())).str(), + "expected 'old_symbol new_symbol'"); + } + Remapper->RemappingTable.insert(Parts); + } + return Remapper; + } + + /// Attempt to map the given old symbol into a new symbol. + /// + /// \return The new symbol, or an empty StringRef if no such symbol was + /// found. + StringRef operator()(StringRef Name) { return RemappingTable.lookup(Name); } +}; +} + struct WeightedFile { std::string Filename; uint64_t Weight; @@ -161,7 +200,8 @@ } /// Load an input into a writer context. -static void loadInput(const WeightedFile &Input, WriterContext *WC) { +static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper, + WriterContext *WC) { std::unique_lock CtxGuard{WC->Lock}; // If there's a pending hard error, don't do more work. @@ -192,6 +232,8 @@ } for (auto &I : *Reader) { + if (Remapper) + I.Name = (*Remapper)(I.Name); const StringRef FuncName = I.Name; bool Reported = false; WC->Writer.addRecord(std::move(I), Input.Weight, [&](Error E) { @@ -236,6 +278,7 @@ } static void mergeInstrProfile(const WeightedFileVector &Inputs, + SymbolRemapper *Remapper, StringRef OutputFilename, ProfileFormat OutputFormat, bool OutputSparse, unsigned NumThreads) { @@ -267,14 +310,14 @@ if (NumThreads == 1) { for (const auto &Input : Inputs) - loadInput(Input, Contexts[0].get()); + loadInput(Input, Remapper, Contexts[0].get()); } else { ThreadPool Pool(NumThreads); // Load the inputs in parallel (N/NumThreads serial steps). unsigned Ctx = 0; for (const auto &Input : Inputs) { - Pool.async(loadInput, Input, Contexts[Ctx].get()); + Pool.async(loadInput, Input, Remapper, Contexts[Ctx].get()); Ctx = (Ctx + 1) % NumThreads; } Pool.wait(); @@ -322,11 +365,43 @@ } } +/// Make a copy of the given function samples with all symbol names remapped +/// by the provided symbol remapper. +static sampleprof::FunctionSamples +remapSamples(const sampleprof::FunctionSamples &Samples, + SymbolRemapper &Remapper, sampleprof_error &Error) { + sampleprof::FunctionSamples Result; + Result.setName(Remapper(Samples.getName())); + Result.addTotalSamples(Samples.getTotalSamples()); + Result.addHeadSamples(Samples.getHeadSamples()); + for (const auto &BodySample : Samples.getBodySamples()) { + Result.addBodySamples(BodySample.first.LineOffset, + BodySample.first.Discriminator, + BodySample.second.getSamples()); + for (const auto &Target : BodySample.second.getCallTargets()) { + Result.addCalledTargetSamples(BodySample.first.LineOffset, + BodySample.first.Discriminator, + Remapper(Target.first()), Target.second); + } + } + for (const auto &CallsiteSamples : Samples.getCallsiteSamples()) { + sampleprof::FunctionSamplesMap &Target = + Result.functionSamplesAt(CallsiteSamples.first); + for (const auto &Callsite : CallsiteSamples.second) { + sampleprof::FunctionSamples Remapped = + remapSamples(Callsite.second, Remapper, Error); + MergeResult(Error, Target[Remapped.getName()].merge(Remapped)); + } + } + return Result; +} + static sampleprof::SampleProfileFormat FormatMap[] = { sampleprof::SPF_None, sampleprof::SPF_Text, sampleprof::SPF_Compact_Binary, sampleprof::SPF_GCC, sampleprof::SPF_Binary}; static void mergeSampleProfile(const WeightedFileVector &Inputs, + SymbolRemapper *Remapper, StringRef OutputFilename, ProfileFormat OutputFormat) { using namespace sampleprof; @@ -357,9 +432,13 @@ for (StringMap::iterator I = Profiles.begin(), E = Profiles.end(); I != E; ++I) { - StringRef FName = I->first(); - FunctionSamples &Samples = I->second; - sampleprof_error Result = ProfileMap[FName].merge(Samples, Input.Weight); + sampleprof_error Result = sampleprof_error::success; + FunctionSamples Remapped = + Remapper ? remapSamples(I->second, *Remapper, Result) + : FunctionSamples(); + FunctionSamples &Samples = Remapper ? Remapped : I->second; + StringRef FName = Samples.getName(); + MergeResult(Result, ProfileMap[FName].merge(Samples, Input.Weight)); if (Result != sampleprof_error::success) { std::error_code EC = make_error_code(Result); handleMergeWriterError(errorCodeToError(EC), Input.Filename, FName); @@ -461,6 +540,8 @@ cl::opt DumpInputFileList( "dump-input-file-list", cl::init(false), cl::Hidden, cl::desc("Dump the list of input files and their weights, then exit")); + cl::opt RemappingFile("remapping-file", cl::value_desc("file"), + cl::desc("Symbol remapping file")); cl::opt OutputFilename("output", cl::value_desc("output"), cl::init("-"), cl::Required, cl::desc("Output file")); @@ -509,11 +590,16 @@ return 0; } + std::unique_ptr Remapper; + if (!RemappingFile.empty()) + Remapper = SymbolRemapper::create(RemappingFile); + if (ProfileKind == instr) - mergeInstrProfile(WeightedInputs, OutputFilename, OutputFormat, - OutputSparse, NumThreads); + mergeInstrProfile(WeightedInputs, Remapper.get(), OutputFilename, + OutputFormat, OutputSparse, NumThreads); else - mergeSampleProfile(WeightedInputs, OutputFilename, OutputFormat); + mergeSampleProfile(WeightedInputs, Remapper.get(), OutputFilename, + OutputFormat); return 0; }