diff --git a/llvm/docs/CommandGuide/index.rst b/llvm/docs/CommandGuide/index.rst --- a/llvm/docs/CommandGuide/index.rst +++ b/llvm/docs/CommandGuide/index.rst @@ -25,6 +25,7 @@ llvm-nm llvm-objdump llvm-config + llvm-cxxmap llvm-diff llvm-cov llvm-profdata diff --git a/llvm/docs/CommandGuide/llvm-cxxmap.rst b/llvm/docs/CommandGuide/llvm-cxxmap.rst new file mode 100644 --- /dev/null +++ b/llvm/docs/CommandGuide/llvm-cxxmap.rst @@ -0,0 +1,91 @@ +llvm-cxxmap - Mangled name remapping tool +========================================= + +SYNOPSIS +-------- + +:program:`llvm-cxxmap` [*options*] *symbol-file-1* *symbol-file-2* + +DESCRIPTION +----------- + +The :program:`llvm-cxxmap` tool performs fuzzy matching of C++ mangled names, +based on a file describing name components that should be considered equivalent. + +The symbol files should contain a list of C++ mangled names (one per line). +Blank lines and lines starting with ``#`` are ignored. The output is a list +of pairs of equivalent symbols, one per line, of the form + +.. code-block:: none + + + +where ```` is a symbol from *symbol-file-1* and ```` is +a symbol from *symbol-file-2*. Mappings for which the two symbols are identical +are omitted. + +OPTIONS +------- + +.. program:: llvm-cxxmap + +.. option:: -remapping-file=file, -r=file + + Specify a file containing a list of equivalence rules that should be used + to determine whether two symbols are equivalent. Required. + See :ref:`remapping-file`. + +.. option:: -output=file, -o=file + + Specify a file to write the list of matched names to. If unspecified, the + list will be written to stdout. + +.. option:: -Wambiguous + + Produce a warning if there are multiple equivalent (but distinct) symbols in + *symbol-file-2*. + +.. option:: -Wincomplete + + Produce a warning if *symbol-file-1* contains a symbol for which there is no + equivalent symbol in *symbol-file-2*. + +.. _remapping-file: + +REMAPPING FILE +-------------- + +The remapping file is a text file containing lines of the form + +.. code-block:: none + + fragmentkind fragment1 fragment2 + +where ``fragmentkind`` is one of ``name``, ``type``, or ``encoding``, +indicating whether the following mangled name fragments are +<`name `_>s, +<`type `_>s, or +<`encoding `_>s, +respectively. +Blank lines and lines starting with ``#`` are ignored. + +For convenience, built-in s such as ``St`` and ``Ss`` +are accepted as s (even though they technically are not s). + +For example, to specify that ``absl::string_view`` and ``std::string_view`` +should be treated as equivalent, the following remapping file could be used: + +.. code-block:: none + + # absl::string_view is considered equivalent to std::string_view + type N4absl11string_viewE St17basic_string_viewIcSt11char_traitsIcEE + + # std:: might be std::__1:: in libc++ or std::__cxx11:: in libstdc++ + name St St3__1 + name St St7__cxx11 + +.. note:: + + Symbol remapping is currently only supported for C++ mangled names + following the Itanium C++ ABI mangling scheme. This covers all C++ targets + supported by Clang other than Windows targets. diff --git a/llvm/docs/CommandGuide/llvm-profdata.rst b/llvm/docs/CommandGuide/llvm-profdata.rst --- a/llvm/docs/CommandGuide/llvm-profdata.rst +++ b/llvm/docs/CommandGuide/llvm-profdata.rst @@ -74,6 +74,16 @@ file are newline-separated. Lines starting with '#' are skipped. Entries may be of the form or ,. +.. option:: -remapping-file=path, -r=path + + Specify a file which contains a remapping from symbol names in the input + profile to the symbol names that should be used in the output profile. The + file should consist of lines of the form `` ``. + Blank lines and lines starting with ``#`` are skipped. + + The :doc:`llvm-cxxmap ` tool can be used to generate the symbol + remapping file. + .. option:: -instr (default) Specify that the input profile is an instrumentation-based profile. diff --git a/llvm/test/tools/llvm-cxxmap/Inputs/after.sym b/llvm/test/tools/llvm-cxxmap/Inputs/after.sym new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-cxxmap/Inputs/after.sym @@ -0,0 +1,2 @@ +_ZN1N1fEN1M1X1YE +_ZN3foo6detail3qux1fEv diff --git a/llvm/test/tools/llvm-cxxmap/Inputs/ambiguous.sym b/llvm/test/tools/llvm-cxxmap/Inputs/ambiguous.sym new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-cxxmap/Inputs/ambiguous.sym @@ -0,0 +1,2 @@ +_ZN1N1fENS_1X1YE +_ZN1N1fEN1M1X1YE diff --git a/llvm/test/tools/llvm-cxxmap/Inputs/before.sym b/llvm/test/tools/llvm-cxxmap/Inputs/before.sym new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-cxxmap/Inputs/before.sym @@ -0,0 +1,2 @@ +_ZN3foo4quux1fEv +_ZN1N1fENS_1X1YE diff --git a/llvm/test/tools/llvm-cxxmap/Inputs/expected b/llvm/test/tools/llvm-cxxmap/Inputs/expected new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-cxxmap/Inputs/expected @@ -0,0 +1,2 @@ +_ZN3foo4quux1fEv _ZN3foo6detail3qux1fEv +_ZN1N1fENS_1X1YE _ZN1N1fEN1M1X1YE diff --git a/llvm/test/tools/llvm-cxxmap/Inputs/incomplete.sym b/llvm/test/tools/llvm-cxxmap/Inputs/incomplete.sym new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-cxxmap/Inputs/incomplete.sym @@ -0,0 +1 @@ +_ZN3foo6detail3qux1fEv diff --git a/llvm/test/tools/llvm-cxxmap/Inputs/remap.map b/llvm/test/tools/llvm-cxxmap/Inputs/remap.map new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-cxxmap/Inputs/remap.map @@ -0,0 +1,8 @@ +# foo:: and foo::detail:: are equivalent +name 3foo N3foo6detailE + +# foo::qux and foo::quux are equivalent +type N3foo3quxE N3foo4quuxE + +# N::X and M::X are equivalent +name N1N1XE N1M1XE diff --git a/llvm/test/tools/llvm-cxxmap/ambiguous.test b/llvm/test/tools/llvm-cxxmap/ambiguous.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-cxxmap/ambiguous.test @@ -0,0 +1,2 @@ +RUN: llvm-cxxmap %S/Inputs/before.sym %S/Inputs/ambiguous.sym -r %S/Inputs/remap.map -o /dev/null -Wambiguous 2>&1 | FileCheck %s +CHECK: warning: {{.*}}:2: symbol _ZN1N1fEN1M1X1YE is equivalent to earlier symbol _ZN1N1fENS_1X1YE diff --git a/llvm/test/tools/llvm-cxxmap/incomplete.test b/llvm/test/tools/llvm-cxxmap/incomplete.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-cxxmap/incomplete.test @@ -0,0 +1,2 @@ +RUN: llvm-cxxmap %S/Inputs/before.sym %S/Inputs/incomplete.sym -r %S/Inputs/remap.map -o /dev/null -Wincomplete 2>&1 | FileCheck %s +CHECK: warning: {{.*}}:2: no new symbol matches old symbol _ZN1N1fENS_1X1YE diff --git a/llvm/test/tools/llvm-cxxmap/remap.test b/llvm/test/tools/llvm-cxxmap/remap.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-cxxmap/remap.test @@ -0,0 +1,5 @@ +RUN: llvm-cxxmap %S/Inputs/before.sym %S/Inputs/after.sym -r %S/Inputs/remap.map -o %t.output -Wambiguous -Wincomplete 2>&1 | FileCheck %s --allow-empty +RUN: diff %S/Inputs/expected %t.output + +CHECK-NOT: warning +CHECK-NOT: error diff --git a/llvm/test/tools/llvm-profdata/Inputs/instr-remap.expected b/llvm/test/tools/llvm-profdata/Inputs/instr-remap.expected new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-profdata/Inputs/instr-remap.expected @@ -0,0 +1,29 @@ +# IR level Instrumentation Flag +:ir +bar +# Func Hash: +1234 +# Num Counters: +2 +# Counter Values: +31 +42 + +bar +# Func Hash: +5678 +# Num Counters: +2 +# Counter Values: +500 +600 + +baz +# Func Hash: +5678 +# Num Counters: +2 +# Counter Values: +7 +8 + diff --git a/llvm/test/tools/llvm-profdata/Inputs/instr-remap.proftext b/llvm/test/tools/llvm-profdata/Inputs/instr-remap.proftext new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-profdata/Inputs/instr-remap.proftext @@ -0,0 +1,25 @@ +# :ir is the flag to indicate this is IR level profile. +:ir +foo +1234 +2 +1 +2 + +bar +1234 +2 +30 +40 + +foo +5678 +2 +500 +600 + +baz +5678 +2 +7 +8 diff --git a/llvm/test/tools/llvm-profdata/Inputs/instr-remap.remap b/llvm/test/tools/llvm-profdata/Inputs/instr-remap.remap new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-profdata/Inputs/instr-remap.remap @@ -0,0 +1 @@ +foo bar diff --git a/llvm/test/tools/llvm-profdata/Inputs/sample-remap.expected b/llvm/test/tools/llvm-profdata/Inputs/sample-remap.expected new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-profdata/Inputs/sample-remap.expected @@ -0,0 +1,16 @@ +main:184019:0 + 4: 534 + 4.2: 534 + 5: 1075 + 5.1: 1075 + 6: 2080 + 7: 534 + 9: 2064 _Z3bazi:1471 _Z3fooi:631 + 10: inline2:2000 + 1: 2000 + 10: inline42:1000 + 1: 1000 +_Z3bazi:40602:2437 + 1: 2437 +_Z3fooi:7711:610 + 1: 610 diff --git a/llvm/test/tools/llvm-profdata/Inputs/sample-remap.proftext b/llvm/test/tools/llvm-profdata/Inputs/sample-remap.proftext new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-profdata/Inputs/sample-remap.proftext @@ -0,0 +1,18 @@ +_Z3bari:20301:1437 + 1: 1437 +_Z3fooi:7711:610 + 1: 610 +main:184019:0 + 4: 534 + 4.2: 534 + 5: 1075 + 5.1: 1075 + 6: 2080 + 7: 534 + 9: 2064 _Z3bari:1471 _Z3fooi:631 + 10: inline1:1000 + 1: 1000 + 10: inline2:2000 + 1: 2000 +_Z3bazi:20301:1000 + 1: 1000 diff --git a/llvm/test/tools/llvm-profdata/Inputs/sample-remap.remap b/llvm/test/tools/llvm-profdata/Inputs/sample-remap.remap new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-profdata/Inputs/sample-remap.remap @@ -0,0 +1,2 @@ +_Z3bari _Z3bazi +inline1 inline42 diff --git a/llvm/test/tools/llvm-profdata/instr-remap.test b/llvm/test/tools/llvm-profdata/instr-remap.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-profdata/instr-remap.test @@ -0,0 +1,2 @@ +; RUN: llvm-profdata merge -text %S/Inputs/instr-remap.proftext -r %S/Inputs/instr-remap.remap -o %t.output +; RUN: diff %S/Inputs/instr-remap.expected %t.output diff --git a/llvm/test/tools/llvm-profdata/sample-remap.test b/llvm/test/tools/llvm-profdata/sample-remap.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-profdata/sample-remap.test @@ -0,0 +1,2 @@ +; RUN: llvm-profdata merge -sample -text %S/Inputs/sample-remap.proftext -r %S/Inputs/sample-remap.remap -o %t.output +; RUN: diff %S/Inputs/sample-remap.expected %t.output diff --git a/llvm/tools/llvm-cxxmap/CMakeLists.txt b/llvm/tools/llvm-cxxmap/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-cxxmap/CMakeLists.txt @@ -0,0 +1,8 @@ +set(LLVM_LINK_COMPONENTS + Core + Support + ) + +add_llvm_tool(llvm-cxxmap + llvm-cxxmap.cpp + ) diff --git a/llvm/tools/llvm-cxxmap/LLVMBuild.txt b/llvm/tools/llvm-cxxmap/LLVMBuild.txt new file mode 100644 --- /dev/null +++ b/llvm/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 diff --git a/llvm/tools/llvm-cxxmap/llvm-cxxmap.cpp b/llvm/tools/llvm-cxxmap/llvm-cxxmap.cpp new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-cxxmap/llvm-cxxmap.cpp @@ -0,0 +1,155 @@ +//===- 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")); + +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) { + 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); +} diff --git a/llvm/tools/llvm-profdata/llvm-profdata.cpp b/llvm/tools/llvm-profdata/llvm-profdata.cpp --- a/llvm/tools/llvm-profdata/llvm-profdata.cpp +++ b/llvm/tools/llvm-profdata/llvm-profdata.cpp @@ -123,6 +123,47 @@ } } +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 \p Name if no such symbol was found. + StringRef operator()(StringRef Name) { + StringRef New = RemappingTable.lookup(Name); + return New.empty() ? Name : New; + } +}; +} + struct WeightedFile { std::string Filename; uint64_t Weight; @@ -161,7 +202,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 +234,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 +280,7 @@ } static void mergeInstrProfile(const WeightedFileVector &Inputs, + SymbolRemapper *Remapper, StringRef OutputFilename, ProfileFormat OutputFormat, bool OutputSparse, unsigned NumThreads) { @@ -267,14 +312,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 +367,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 +434,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 +542,10 @@ 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::alias RemappingFileA("r", cl::desc("Alias for --remapping-file"), + cl::aliasopt(RemappingFile)); cl::opt OutputFilename("output", cl::value_desc("output"), cl::init("-"), cl::Required, cl::desc("Output file")); @@ -509,11 +594,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; }