155 changes: 155 additions & 0 deletions llvm/tools/llvm-cxxmap/llvm-cxxmap.cpp
Original file line number Diff line number Diff line change
@@ -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<std::string> OldSymbolFile(cl::Positional, cl::Required,
cl::desc("<symbol-file>"));
cl::opt<std::string> NewSymbolFile(cl::Positional, cl::Required,
cl::desc("<symbol-file>"));
cl::opt<std::string> RemappingFile("remapping-file", cl::Required,
cl::desc("Remapping file"));
cl::alias RemappingFileA("r", cl::aliasopt(RemappingFile));
cl::opt<std::string> OutputFilename("output", cl::value_desc("output"),
cl::init("-"), cl::desc("Output file"));
cl::alias OutputFilenameA("o", cl::aliasopt(OutputFilename));

cl::opt<bool> WarnAmbiguous(
"Wambiguous",
cl::desc("Warn on equivalent symbols in the output symbol list"));
cl::opt<bool> 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<SymbolRemappingReader::Key, StringRef> MappedNames;
DenseSet<StringRef> 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);
}
108 changes: 99 additions & 9 deletions llvm/tools/llvm-profdata/llvm-profdata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,47 @@ static void handleMergeWriterError(Error E, StringRef WhenceFile = "",
}
}

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<MemoryBuffer> File;
DenseMap<StringRef, StringRef> RemappingTable;

public:
/// Build a SymbolRemapper from a file containing a list of old/new symbols.
static std::unique_ptr<SymbolRemapper> create(StringRef InputFile) {
auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile);
if (!BufOrError)
exitWithErrorCode(BufOrError.getError(), InputFile);

auto Remapper = llvm::make_unique<SymbolRemapper>();
Remapper->File = std::move(BufOrError.get());

for (line_iterator LineIt(*Remapper->File, /*SkipBlanks=*/true, '#');
!LineIt.is_at_eof(); ++LineIt) {
std::pair<StringRef, StringRef> 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;
Expand Down Expand Up @@ -161,7 +202,8 @@ static bool isFatalError(instrprof_error IPE) {
}

/// 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<std::mutex> CtxGuard{WC->Lock};

// If there's a pending hard error, don't do more work.
Expand Down Expand Up @@ -192,6 +234,8 @@ static void loadInput(const WeightedFile &Input, WriterContext *WC) {
}

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) {
Expand Down Expand Up @@ -236,6 +280,7 @@ static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) {
}

static void mergeInstrProfile(const WeightedFileVector &Inputs,
SymbolRemapper *Remapper,
StringRef OutputFilename,
ProfileFormat OutputFormat, bool OutputSparse,
unsigned NumThreads) {
Expand Down Expand Up @@ -267,14 +312,14 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs,

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();
Expand Down Expand Up @@ -322,11 +367,43 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs,
}
}

/// 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;
Expand Down Expand Up @@ -357,9 +434,13 @@ static void mergeSampleProfile(const WeightedFileVector &Inputs,
for (StringMap<FunctionSamples>::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);
Expand Down Expand Up @@ -461,6 +542,10 @@ static int merge_main(int argc, const char *argv[]) {
cl::opt<bool> 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<std::string> 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<std::string> OutputFilename("output", cl::value_desc("output"),
cl::init("-"), cl::Required,
cl::desc("Output file"));
Expand Down Expand Up @@ -509,11 +594,16 @@ static int merge_main(int argc, const char *argv[]) {
return 0;
}

std::unique_ptr<SymbolRemapper> 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;
}
Expand Down