static int merge_main(int argc, const char *argv[]) { cl::list<std::string> InputFilenames(cl::Positional, cl::desc("<filename...>")); cl::list<std::string> WeightedInputFilenames("weighted-input", cl::desc("<weight>,<filename>")); cl::opt<std::string> InputFilenamesFile( "input-files", cl::init(""), cl::desc("Path to file containing newline-separated " "[<weight>,]<filename> entries")); cl::alias InputFilenamesFileA("f", cl::desc("Alias for --input-files"), cl::aliasopt(InputFilenamesFile)); 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> OutputFilename("output", cl::value_desc("output"), cl::init("-"), cl::Required, cl::desc("Output file")); cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), cl::aliasopt(OutputFilename)); cl::opt<ProfileKinds> ProfileKind( cl::desc("Profile kind:"), cl::init(instr), cl::values(clEnumVal(instr, "Instrumentation profile (default)"), clEnumVal(sample, "Sample profile"))); cl::opt<ProfileFormat> OutputFormat( cl::desc("Format of output profile"), cl::init(PF_Binary), cl::values(clEnumValN(PF_Binary, "binary", "Binary encoding (default)"), clEnumValN(PF_Text, "text", "Text encoding"), clEnumValN(PF_GCC, "gcc", "GCC encoding (only meaningful for -sample)"))); cl::opt<bool> OutputSparse("sparse", cl::init(false), cl::desc("Generate a sparse profile (only meaningful for -instr)")); cl::opt<unsigned> NumThreads( "num-threads", cl::init(0), cl::desc("Number of merge threads to use (default: autodetect)")); cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"), cl::aliasopt(NumThreads)); cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n"); WeightedFileVector WeightedInputs; for (StringRef Filename : InputFilenames) addWeightedInput(WeightedInputs, {Filename, 1}); for (StringRef WeightedFilename : WeightedInputFilenames) addWeightedInput(WeightedInputs, parseWeightedFile(WeightedFilename)); // Make sure that the file buffer stays alive for the duration of the // weighted input vector's lifetime. auto Buffer = getInputFilenamesFileBuf(InputFilenamesFile); parseInputFilenamesFile(Buffer.get(), WeightedInputs); if (WeightedInputs.empty()) exitWithError("No input files specified. See " + sys::path::filename(argv[0]) + " -help"); if (DumpInputFileList) { for (auto &WF : WeightedInputs) outs() << WF.Weight << "," << WF.Filename << "\n"; return 0; } if (ProfileKind == instr) mergeInstrProfile(WeightedInputs, OutputFilename, OutputFormat, OutputSparse, NumThreads); else mergeSampleProfile(WeightedInputs, OutputFilename, OutputFormat); return 0; }
static void mergeInstrProfile(const WeightedFileVector &Inputs, StringRef OutputFilename, ProfileFormat OutputFormat, bool OutputSparse, unsigned NumThreads) { if (OutputFilename.compare("-") == 0) exitWithError("Cannot write indexed profdata format to stdout."); if (OutputFormat != PF_Binary && OutputFormat != PF_Text) exitWithError("Unknown format is specified."); std::error_code EC; raw_fd_ostream Output(OutputFilename.data(), EC, sys::fs::F_None); if (EC) exitWithErrorCode(EC, OutputFilename); std::mutex ErrorLock; SmallSet<instrprof_error, 4> WriterErrorCodes; // If NumThreads is not specified, auto-detect a good default. if (NumThreads == 0) NumThreads = std::max(1U, std::min(std::thread::hardware_concurrency(), unsigned(Inputs.size() / 2))); // Initialize the writer contexts. SmallVector<std::unique_ptr<WriterContext>, 4> Contexts; for (unsigned I = 0; I < NumThreads; ++I) Contexts.emplace_back(llvm::make_unique<WriterContext>( OutputSparse, ErrorLock, WriterErrorCodes)); if (NumThreads == 1) { for (const auto &Input : Inputs) loadInput(Input, 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()); Ctx = (Ctx + 1) % NumThreads; } Pool.wait(); // Merge the writer contexts together (~ lg(NumThreads) serial steps). unsigned Mid = Contexts.size() / 2; unsigned End = Contexts.size(); assert(Mid > 0 && "Expected more than one context"); do { for (unsigned I = 0; I < Mid; ++I) Pool.async(mergeWriterContexts, Contexts[I].get(), Contexts[I + Mid].get()); Pool.wait(); if (End & 1) { Pool.async(mergeWriterContexts, Contexts[0].get(), Contexts[End - 1].get()); Pool.wait(); } End = Mid; Mid /= 2; } while (Mid > 0); } // Handle deferred hard errors encountered during merging. for (std::unique_ptr<WriterContext> &WC : Contexts) if (WC->Err) exitWithError(std::move(WC->Err), WC->ErrWhence); InstrProfWriter &Writer = Contexts[0]->Writer; if (OutputFormat == PF_Text) Writer.writeText(Output); else Writer.write(Output); }