From c3f092f69d436105a49162679ffd1ed7a74169c2 Mon Sep 17 00:00:00 2001 From: Patrick Wildt Date: Tue, 24 Jan 2017 08:33:19 +0000 Subject: Import LLVM 4.0.0 rc1 including clang and lld to help the current development effort on OpenBSD/arm64. --- gnu/llvm/tools/lld/COFF/Strings.cpp | 7 +- gnu/llvm/tools/lld/COFF/Strings.h | 2 +- gnu/llvm/tools/llvm-xray/llvm-xray.cc | 12 +- gnu/llvm/tools/llvm-xray/xray-account.cc | 140 +++++++-------- gnu/llvm/tools/llvm-xray/xray-converter.cc | 275 +++++------------------------ gnu/llvm/tools/llvm-xray/xray-extract.cc | 268 ++++++++++++++++++++++++---- 6 files changed, 336 insertions(+), 368 deletions(-) (limited to 'gnu/llvm/tools') diff --git a/gnu/llvm/tools/lld/COFF/Strings.cpp b/gnu/llvm/tools/lld/COFF/Strings.cpp index 89b9c5186fd..d0558413f67 100644 --- a/gnu/llvm/tools/lld/COFF/Strings.cpp +++ b/gnu/llvm/tools/lld/COFF/Strings.cpp @@ -8,7 +8,6 @@ //===----------------------------------------------------------------------===// #include "Strings.h" -#include #if defined(_MSC_VER) #include @@ -20,12 +19,8 @@ using namespace lld; using namespace lld::coff; using namespace llvm; -Optional coff::demangleMSVC(StringRef S) { +Optional coff::demangle(StringRef S) { #if defined(_MSC_VER) - // UnDecorateSymbolName is not thread-safe, so we need a mutex. - static std::mutex Mu; - std::lock_guard Lock(Mu); - char Buf[4096]; if (S.startswith("?")) if (size_t Len = UnDecorateSymbolName(S.str().c_str(), Buf, sizeof(Buf), 0)) diff --git a/gnu/llvm/tools/lld/COFF/Strings.h b/gnu/llvm/tools/lld/COFF/Strings.h index 67fc1c773c6..1f85f3e2da5 100644 --- a/gnu/llvm/tools/lld/COFF/Strings.h +++ b/gnu/llvm/tools/lld/COFF/Strings.h @@ -16,7 +16,7 @@ namespace lld { namespace coff { -llvm::Optional demangleMSVC(llvm::StringRef S); +llvm::Optional demangle(llvm::StringRef S); } } diff --git a/gnu/llvm/tools/llvm-xray/llvm-xray.cc b/gnu/llvm/tools/llvm-xray/llvm-xray.cc index 17cc9f90dd7..ac5faaa408b 100644 --- a/gnu/llvm/tools/llvm-xray/llvm-xray.cc +++ b/gnu/llvm/tools/llvm-xray/llvm-xray.cc @@ -18,6 +18,7 @@ // #include "xray-registry.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; @@ -29,21 +30,12 @@ int main(int argc, char *argv[]) { " This program consolidates multiple XRay trace " "processing tools for convenient access.\n"); for (auto *SC : cl::getRegisteredSubcommands()) { - if (*SC) { - // If no subcommand was provided, we need to explicitly check if this is - // the top-level subcommand. - if (SC == &*cl::TopLevelSubCommand) { - cl::PrintHelpMessage(false, true); - return 0; - } + if (*SC) if (auto C = dispatch(SC)) { ExitOnError("llvm-xray: ")(C()); return 0; } - } } - // If all else fails, we still print the usage message. cl::PrintHelpMessage(false, true); - return 0; } diff --git a/gnu/llvm/tools/llvm-xray/xray-account.cc b/gnu/llvm/tools/llvm-xray/xray-account.cc index 7b684aad693..671a5a073ee 100644 --- a/gnu/llvm/tools/llvm-xray/xray-account.cc +++ b/gnu/llvm/tools/llvm-xray/xray-account.cc @@ -18,10 +18,10 @@ #include #include "xray-account.h" +#include "xray-extract.h" #include "xray-registry.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FormatVariadic.h" -#include "llvm/XRay/InstrumentationMap.h" #include "llvm/XRay/Trace.h" using namespace llvm; @@ -120,6 +120,16 @@ static cl::opt static cl::alias AccountInstrMap2("m", cl::aliasopt(AccountInstrMap), cl::desc("Alias for -instr_map"), cl::sub(Account)); +static cl::opt InstrMapFormat( + "instr-map-format", cl::desc("format of instrumentation map"), + cl::values(clEnumValN(InstrumentationMapExtractor::InputFormats::ELF, "elf", + "instrumentation map in an ELF header"), + clEnumValN(InstrumentationMapExtractor::InputFormats::YAML, + "yaml", "instrumentation map in YAML")), + cl::sub(Account), cl::init(InstrumentationMapExtractor::InputFormats::ELF)); +static cl::alias InstrMapFormat2("t", cl::aliasopt(InstrMapFormat), + cl::desc("Alias for -instr-map-format"), + cl::sub(Account)); namespace { @@ -146,16 +156,13 @@ bool LatencyAccountant::accountRecord(const XRayRecord &Record) { auto &ThreadStack = PerThreadFunctionStack[Record.TId]; switch (Record.Type) { - case RecordTypes::ENTER: - case RecordTypes::ENTER_ARG: { + case RecordTypes::ENTER: { + // Function Enter ThreadStack.emplace_back(Record.FuncId, Record.TSC); break; } - case RecordTypes::EXIT: - case RecordTypes::TAIL_EXIT: { - if (ThreadStack.empty()) - return false; - + case RecordTypes::EXIT: { + // Function Exit if (ThreadStack.back().first == Record.FuncId) { const auto &Top = ThreadStack.back(); recordLatency(Top.first, diff(Top.second, Record.TSC)); @@ -410,97 +417,68 @@ void LatencyAccountant::exportStatsAsCSV(raw_ostream &OS, using namespace llvm::xray; -namespace llvm { -template <> struct format_provider { - static void format(const llvm::xray::RecordTypes &T, raw_ostream &Stream, - StringRef Style) { - switch(T) { - case RecordTypes::ENTER: - Stream << "enter"; - break; - case RecordTypes::ENTER_ARG: - Stream << "enter-arg"; - break; - case RecordTypes::EXIT: - Stream << "exit"; - break; - case RecordTypes::TAIL_EXIT: - Stream << "tail-exit"; - break; - } - } -}; -} // namespace llvm - static CommandRegistration Unused(&Account, []() -> Error { - InstrumentationMap Map; - if (!AccountInstrMap.empty()) { - auto InstrumentationMapOrError = loadInstrumentationMap(AccountInstrMap); - if (!InstrumentationMapOrError) - return joinErrors(make_error( - Twine("Cannot open instrumentation map '") + - AccountInstrMap + "'", - std::make_error_code(std::errc::invalid_argument)), - InstrumentationMapOrError.takeError()); - Map = std::move(*InstrumentationMapOrError); - } + int Fd; + auto EC = sys::fs::openFileForRead(AccountInput, Fd); + if (EC) + return make_error( + Twine("Cannot open file '") + AccountInput + "'", EC); + + Error Err = Error::success(); + xray::InstrumentationMapExtractor Extractor(AccountInstrMap, InstrMapFormat, + Err); + if (auto E = handleErrors( + std::move(Err), [&](std::unique_ptr SE) -> Error { + if (SE->convertToErrorCode() == std::errc::no_such_file_or_directory) + return Error::success(); + return Error(std::move(SE)); + })) + return E; - std::error_code EC; raw_fd_ostream OS(AccountOutput, EC, sys::fs::OpenFlags::F_Text); if (EC) return make_error( Twine("Cannot open file '") + AccountOutput + "' for writing.", EC); - const auto &FunctionAddresses = Map.getFunctionAddresses(); + const auto &FunctionAddresses = Extractor.getFunctionAddresses(); symbolize::LLVMSymbolizer::Options Opts( symbolize::FunctionNameKind::LinkageName, true, true, false, ""); symbolize::LLVMSymbolizer Symbolizer(Opts); llvm::xray::FuncIdConversionHelper FuncIdHelper(AccountInstrMap, Symbolizer, FunctionAddresses); xray::LatencyAccountant FCA(FuncIdHelper, AccountDeduceSiblingCalls); - auto TraceOrErr = loadTraceFile(AccountInput); - if (!TraceOrErr) + if (auto TraceOrErr = loadTraceFile(AccountInput)) { + auto &T = *TraceOrErr; + for (const auto &Record : T) { + if (FCA.accountRecord(Record)) + continue; + for (const auto &ThreadStack : FCA.getPerThreadFunctionStack()) { + errs() << "Thread ID: " << ThreadStack.first << "\n"; + auto Level = ThreadStack.second.size(); + for (const auto &Entry : llvm::reverse(ThreadStack.second)) + errs() << "#" << Level-- << "\t" + << FuncIdHelper.SymbolOrNumber(Entry.first) << '\n'; + } + if (!AccountKeepGoing) + return make_error( + Twine("Failed accounting function calls in file '") + AccountInput + + "'.", + std::make_error_code(std::errc::executable_format_error)); + } + switch (AccountOutputFormat) { + case AccountOutputFormats::TEXT: + FCA.exportStatsAsText(OS, T.getFileHeader()); + break; + case AccountOutputFormats::CSV: + FCA.exportStatsAsCSV(OS, T.getFileHeader()); + break; + } + } else { return joinErrors( make_error( Twine("Failed loading input file '") + AccountInput + "'", std::make_error_code(std::errc::executable_format_error)), TraceOrErr.takeError()); - - auto &T = *TraceOrErr; - for (const auto &Record : T) { - if (FCA.accountRecord(Record)) - continue; - errs() - << "Error processing record: " - << llvm::formatv( - R"({{type: {0}; cpu: {1}; record-type: {2}; function-id: {3}; tsc: {4}; thread-id: {5}}})", - Record.RecordType, Record.CPU, Record.Type, Record.FuncId, - Record.TId) - << '\n'; - for (const auto &ThreadStack : FCA.getPerThreadFunctionStack()) { - errs() << "Thread ID: " << ThreadStack.first << "\n"; - if (ThreadStack.second.empty()) { - errs() << " (empty stack)\n"; - continue; - } - auto Level = ThreadStack.second.size(); - for (const auto &Entry : llvm::reverse(ThreadStack.second)) - errs() << " #" << Level-- << "\t" - << FuncIdHelper.SymbolOrNumber(Entry.first) << '\n'; - } - if (!AccountKeepGoing) - return make_error( - Twine("Failed accounting function calls in file '") + AccountInput + - "'.", - std::make_error_code(std::errc::executable_format_error)); - } - switch (AccountOutputFormat) { - case AccountOutputFormats::TEXT: - FCA.exportStatsAsText(OS, T.getFileHeader()); - break; - case AccountOutputFormats::CSV: - FCA.exportStatsAsCSV(OS, T.getFileHeader()); - break; } return Error::success(); diff --git a/gnu/llvm/tools/llvm-xray/xray-converter.cc b/gnu/llvm/tools/llvm-xray/xray-converter.cc index aa0da55207b..31275e2902f 100644 --- a/gnu/llvm/tools/llvm-xray/xray-converter.cc +++ b/gnu/llvm/tools/llvm-xray/xray-converter.cc @@ -12,16 +12,13 @@ //===----------------------------------------------------------------------===// #include "xray-converter.h" -#include "trie-node.h" +#include "xray-extract.h" #include "xray-registry.h" #include "llvm/DebugInfo/Symbolize/Symbolize.h" #include "llvm/Support/EndianStream.h" #include "llvm/Support/FileSystem.h" -#include "llvm/Support/FormatVariadic.h" -#include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/YAMLTraits.h" #include "llvm/Support/raw_ostream.h" -#include "llvm/XRay/InstrumentationMap.h" #include "llvm/XRay/Trace.h" #include "llvm/XRay/YAMLXRayRecord.h" @@ -34,14 +31,11 @@ static cl::SubCommand Convert("convert", "Trace Format Conversion"); static cl::opt ConvertInput(cl::Positional, cl::desc(""), cl::Required, cl::sub(Convert)); -enum class ConvertFormats { BINARY, YAML, CHROME_TRACE_EVENT }; +enum class ConvertFormats { BINARY, YAML }; static cl::opt ConvertOutputFormat( "output-format", cl::desc("output format"), cl::values(clEnumValN(ConvertFormats::BINARY, "raw", "output in binary"), - clEnumValN(ConvertFormats::YAML, "yaml", "output in yaml"), - clEnumValN(ConvertFormats::CHROME_TRACE_EVENT, "trace_event", - "Output in chrome's trace event format. " - "May be visualized with the Catapult trace viewer.")), + clEnumValN(ConvertFormats::YAML, "yaml", "output in yaml")), cl::sub(Convert)); static cl::alias ConvertOutputFormat2("f", cl::aliasopt(ConvertOutputFormat), cl::desc("Alias for -output-format"), @@ -78,7 +72,18 @@ static cl::opt ConvertSortInput( static cl::alias ConvertSortInput2("s", cl::aliasopt(ConvertSortInput), cl::desc("Alias for -sort"), cl::sub(Convert)); - +static cl::opt InstrMapFormat( + "instr-map-format", cl::desc("format of instrumentation map"), + cl::values(clEnumValN(InstrumentationMapExtractor::InputFormats::ELF, "elf", + "instrumentation map in an ELF header"), + clEnumValN(InstrumentationMapExtractor::InputFormats::YAML, + "yaml", "instrumentation map in YAML")), + cl::sub(Convert), cl::init(InstrumentationMapExtractor::InputFormats::ELF)); +static cl::alias InstrMapFormat2("t", cl::aliasopt(InstrMapFormat), + cl::desc("Alias for -instr-map-format"), + cl::sub(Convert)); + +using llvm::yaml::IO; using llvm::yaml::Output; void TraceConverter::exportAsYAML(const Trace &Records, raw_ostream &OS) { @@ -90,10 +95,10 @@ void TraceConverter::exportAsYAML(const Trace &Records, raw_ostream &OS) { for (const auto &R : Records) { Trace.Records.push_back({R.RecordType, R.CPU, R.Type, R.FuncId, Symbolize ? FuncIdHelper.SymbolOrNumber(R.FuncId) - : llvm::to_string(R.FuncId), - R.TSC, R.TId, R.CallArgs}); + : std::to_string(R.FuncId), + R.TSC, R.TId}); } - Output Out(OS, nullptr, 0); + Output Out(OS); Out << Trace; } @@ -123,20 +128,14 @@ void TraceConverter::exportAsRAWv1(const Trace &Records, raw_ostream &OS) { // format. for (const auto &R : Records) { Writer.write(R.RecordType); - // The on disk naive raw format uses 8 bit CPUs, but the record has 16. - // There's no choice but truncation. - Writer.write(static_cast(R.CPU)); + Writer.write(R.CPU); switch (R.Type) { case RecordTypes::ENTER: - case RecordTypes::ENTER_ARG: Writer.write(uint8_t{0}); break; case RecordTypes::EXIT: Writer.write(uint8_t{1}); break; - case RecordTypes::TAIL_EXIT: - Writer.write(uint8_t{2}); - break; } Writer.write(R.FuncId); Writer.write(R.TSC); @@ -147,217 +146,30 @@ void TraceConverter::exportAsRAWv1(const Trace &Records, raw_ostream &OS) { } } -namespace { - -// A structure that allows building a dictionary of stack ids for the Chrome -// trace event format. -struct StackIdData { - // Each Stack of function calls has a unique ID. - unsigned id; - - // Bookkeeping so that IDs can be maintained uniquely across threads. - // Traversal keeps sibling pointers to other threads stacks. This is helpful - // to determine when a thread encounters a new stack and should assign a new - // unique ID. - SmallVector *, 4> siblings; -}; - -using StackTrieNode = TrieNode; - -// A helper function to find the sibling nodes for an encountered function in a -// thread of execution. Relies on the invariant that each time a new node is -// traversed in a thread, sibling bidirectional pointers are maintained. -SmallVector -findSiblings(StackTrieNode *parent, int32_t FnId, uint32_t TId, - const DenseMap> - &StackRootsByThreadId) { - - SmallVector Siblings{}; - - if (parent == nullptr) { - for (auto map_iter : StackRootsByThreadId) { - // Only look for siblings in other threads. - if (map_iter.first != TId) - for (auto node_iter : map_iter.second) { - if (node_iter->FuncId == FnId) - Siblings.push_back(node_iter); - } - } - return Siblings; - } - - for (auto *ParentSibling : parent->ExtraData.siblings) - for (auto node_iter : ParentSibling->Callees) - if (node_iter->FuncId == FnId) - Siblings.push_back(node_iter); - - return Siblings; -} - -// Given a function being invoked in a thread with id TId, finds and returns the -// StackTrie representing the function call stack. If no node exists, creates -// the node. Assigns unique IDs to stacks newly encountered among all threads -// and keeps sibling links up to when creating new nodes. -StackTrieNode *findOrCreateStackNode( - StackTrieNode *Parent, int32_t FuncId, uint32_t TId, - DenseMap> &StackRootsByThreadId, - DenseMap &StacksByStackId, unsigned *id_counter, - std::forward_list &NodeStore) { - SmallVector &ParentCallees = - Parent == nullptr ? StackRootsByThreadId[TId] : Parent->Callees; - auto match = find_if(ParentCallees, [FuncId](StackTrieNode *ParentCallee) { - return FuncId == ParentCallee->FuncId; - }); - if (match != ParentCallees.end()) - return *match; - - SmallVector siblings = - findSiblings(Parent, FuncId, TId, StackRootsByThreadId); - if (siblings.empty()) { - NodeStore.push_front({FuncId, Parent, {}, {(*id_counter)++, {}}}); - StackTrieNode *CurrentStack = &NodeStore.front(); - StacksByStackId[*id_counter - 1] = CurrentStack; - ParentCallees.push_back(CurrentStack); - return CurrentStack; - } - unsigned stack_id = siblings[0]->ExtraData.id; - NodeStore.push_front({FuncId, Parent, {}, {stack_id, std::move(siblings)}}); - StackTrieNode *CurrentStack = &NodeStore.front(); - for (auto *sibling : CurrentStack->ExtraData.siblings) - sibling->ExtraData.siblings.push_back(CurrentStack); - ParentCallees.push_back(CurrentStack); - return CurrentStack; -} - -void writeTraceViewerRecord(raw_ostream &OS, int32_t FuncId, uint32_t TId, - bool Symbolize, - const FuncIdConversionHelper &FuncIdHelper, - double EventTimestampUs, - const StackTrieNode &StackCursor, - StringRef FunctionPhenotype) { - OS << " "; - OS << llvm::formatv( - R"({ "name" : "{0}", "ph" : "{1}", "tid" : "{2}", "pid" : "1", )" - R"("ts" : "{3:f3}", "sf" : "{4}" })", - (Symbolize ? FuncIdHelper.SymbolOrNumber(FuncId) - : llvm::to_string(FuncId)), - FunctionPhenotype, TId, EventTimestampUs, StackCursor.ExtraData.id); -} - -} // namespace - -void TraceConverter::exportAsChromeTraceEventFormat(const Trace &Records, - raw_ostream &OS) { - const auto &FH = Records.getFileHeader(); - auto CycleFreq = FH.CycleFrequency; - - unsigned id_counter = 0; - - OS << "{\n \"traceEvents\": ["; - DenseMap StackCursorByThreadId{}; - DenseMap> StackRootsByThreadId{}; - DenseMap StacksByStackId{}; - std::forward_list NodeStore{}; - int loop_count = 0; - for (const auto &R : Records) { - if (loop_count++ == 0) - OS << "\n"; - else - OS << ",\n"; - - // Chrome trace event format always wants data in micros. - // CyclesPerMicro = CycleHertz / 10^6 - // TSC / CyclesPerMicro == TSC * 10^6 / CycleHertz == MicroTimestamp - // Could lose some precision here by converting the TSC to a double to - // multiply by the period in micros. 52 bit mantissa is a good start though. - // TODO: Make feature request to Chrome Trace viewer to accept ticks and a - // frequency or do some more involved calculation to avoid dangers of - // conversion. - double EventTimestampUs = double(1000000) / CycleFreq * double(R.TSC); - StackTrieNode *&StackCursor = StackCursorByThreadId[R.TId]; - switch (R.Type) { - case RecordTypes::ENTER: - case RecordTypes::ENTER_ARG: - StackCursor = findOrCreateStackNode(StackCursor, R.FuncId, R.TId, - StackRootsByThreadId, StacksByStackId, - &id_counter, NodeStore); - // Each record is represented as a json dictionary with function name, - // type of B for begin or E for end, thread id, process id (faked), - // timestamp in microseconds, and a stack frame id. The ids are logged - // in an id dictionary after the events. - writeTraceViewerRecord(OS, R.FuncId, R.TId, Symbolize, FuncIdHelper, - EventTimestampUs, *StackCursor, "B"); - break; - case RecordTypes::EXIT: - case RecordTypes::TAIL_EXIT: - // No entries to record end for. - if (StackCursor == nullptr) - break; - // Should we emit an END record anyway or account this condition? - // (And/Or in loop termination below) - StackTrieNode *PreviousCursor = nullptr; - do { - writeTraceViewerRecord(OS, StackCursor->FuncId, R.TId, Symbolize, - FuncIdHelper, EventTimestampUs, *StackCursor, - "E"); - PreviousCursor = StackCursor; - StackCursor = StackCursor->Parent; - } while (PreviousCursor->FuncId != R.FuncId && StackCursor != nullptr); - break; - } - } - OS << "\n ],\n"; // Close the Trace Events array. - OS << " " - << "\"displayTimeUnit\": \"ns\",\n"; - - // The stackFrames dictionary substantially reduces size of the output file by - // avoiding repeating the entire call stack of function names for each entry. - OS << R"( "stackFrames": {)"; - int stack_frame_count = 0; - for (auto map_iter : StacksByStackId) { - if (stack_frame_count++ == 0) - OS << "\n"; - else - OS << ",\n"; - OS << " "; - OS << llvm::formatv( - R"("{0}" : { "name" : "{1}")", map_iter.first, - (Symbolize ? FuncIdHelper.SymbolOrNumber(map_iter.second->FuncId) - : llvm::to_string(map_iter.second->FuncId))); - if (map_iter.second->Parent != nullptr) - OS << llvm::formatv(R"(, "parent": "{0}")", - map_iter.second->Parent->ExtraData.id); - OS << " }"; - } - OS << "\n }\n"; // Close the stack frames map. - OS << "}\n"; // Close the JSON entry. -} - namespace llvm { namespace xray { static CommandRegistration Unused(&Convert, []() -> Error { // FIXME: Support conversion to BINARY when upgrading XRay trace versions. - InstrumentationMap Map; - if (!ConvertInstrMap.empty()) { - auto InstrumentationMapOrError = loadInstrumentationMap(ConvertInstrMap); - if (!InstrumentationMapOrError) - return joinErrors(make_error( - Twine("Cannot open instrumentation map '") + - ConvertInstrMap + "'", - std::make_error_code(std::errc::invalid_argument)), - InstrumentationMapOrError.takeError()); - Map = std::move(*InstrumentationMapOrError); - } + int Fd; + auto EC = sys::fs::openFileForRead(ConvertInput, Fd); + if (EC) + return make_error( + Twine("Cannot open file '") + ConvertInput + "'", EC); - const auto &FunctionAddresses = Map.getFunctionAddresses(); + Error Err = Error::success(); + xray::InstrumentationMapExtractor Extractor(ConvertInstrMap, InstrMapFormat, + Err); + handleAllErrors(std::move(Err), + [&](const ErrorInfoBase &E) { E.log(errs()); }); + + const auto &FunctionAddresses = Extractor.getFunctionAddresses(); symbolize::LLVMSymbolizer::Options Opts( symbolize::FunctionNameKind::LinkageName, true, true, false, ""); symbolize::LLVMSymbolizer Symbolizer(Opts); llvm::xray::FuncIdConversionHelper FuncIdHelper(ConvertInstrMap, Symbolizer, FunctionAddresses); llvm::xray::TraceConverter TC(FuncIdHelper, ConvertSymbolize); - std::error_code EC; raw_fd_ostream OS(ConvertOutput, EC, ConvertOutputFormat == ConvertFormats::BINARY ? sys::fs::OpenFlags::F_None @@ -366,25 +178,22 @@ static CommandRegistration Unused(&Convert, []() -> Error { return make_error( Twine("Cannot open file '") + ConvertOutput + "' for writing.", EC); - auto TraceOrErr = loadTraceFile(ConvertInput, ConvertSortInput); - if (!TraceOrErr) + if (auto TraceOrErr = loadTraceFile(ConvertInput, ConvertSortInput)) { + auto &T = *TraceOrErr; + switch (ConvertOutputFormat) { + case ConvertFormats::YAML: + TC.exportAsYAML(T, OS); + break; + case ConvertFormats::BINARY: + TC.exportAsRAWv1(T, OS); + break; + } + } else { return joinErrors( make_error( Twine("Failed loading input file '") + ConvertInput + "'.", std::make_error_code(std::errc::executable_format_error)), TraceOrErr.takeError()); - - auto &T = *TraceOrErr; - switch (ConvertOutputFormat) { - case ConvertFormats::YAML: - TC.exportAsYAML(T, OS); - break; - case ConvertFormats::BINARY: - TC.exportAsRAWv1(T, OS); - break; - case ConvertFormats::CHROME_TRACE_EVENT: - TC.exportAsChromeTraceEventFormat(T, OS); - break; } return Error::success(); }); diff --git a/gnu/llvm/tools/llvm-xray/xray-extract.cc b/gnu/llvm/tools/llvm-xray/xray-extract.cc index cd87798d0e6..49ecd742113 100644 --- a/gnu/llvm/tools/llvm-xray/xray-extract.cc +++ b/gnu/llvm/tools/llvm-xray/xray-extract.cc @@ -13,16 +13,23 @@ // //===----------------------------------------------------------------------===// +#include +#include + +#include "xray-extract.h" -#include "func-id-helper.h" #include "xray-registry.h" +#include "xray-sleds.h" +#include "llvm/Object/ELF.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/ELF.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" +#include "llvm/Support/YAMLTraits.h" #include "llvm/Support/raw_ostream.h" -#include "llvm/XRay/InstrumentationMap.h" using namespace llvm; using namespace llvm::xray; @@ -41,57 +48,244 @@ static cl::opt static cl::alias ExtractOutput2("o", cl::aliasopt(ExtractOutput), cl::desc("Alias for -output"), cl::sub(Extract)); -static cl::opt ExtractSymbolize("symbolize", cl::value_desc("symbolize"), - cl::init(false), - cl::desc("symbolize functions"), - cl::sub(Extract)); -static cl::alias ExtractSymbolize2("s", cl::aliasopt(ExtractSymbolize), - cl::desc("alias for -symbolize"), - cl::sub(Extract)); + +struct YAMLXRaySledEntry { + int32_t FuncId; + Hex64 Address; + Hex64 Function; + SledEntry::FunctionKinds Kind; + bool AlwaysInstrument; +}; + +namespace llvm { +namespace yaml { + +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &IO, SledEntry::FunctionKinds &Kind) { + IO.enumCase(Kind, "function-enter", SledEntry::FunctionKinds::ENTRY); + IO.enumCase(Kind, "function-exit", SledEntry::FunctionKinds::EXIT); + IO.enumCase(Kind, "tail-exit", SledEntry::FunctionKinds::TAIL); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &IO, YAMLXRaySledEntry &Entry) { + IO.mapRequired("id", Entry.FuncId); + IO.mapRequired("address", Entry.Address); + IO.mapRequired("function", Entry.Function); + IO.mapRequired("kind", Entry.Kind); + IO.mapRequired("always-instrument", Entry.AlwaysInstrument); + } + + static constexpr bool flow = true; +}; +} +} + +LLVM_YAML_IS_SEQUENCE_VECTOR(YAMLXRaySledEntry) namespace { -void exportAsYAML(const InstrumentationMap &Map, raw_ostream &OS, - FuncIdConversionHelper &FH) { +llvm::Error LoadBinaryInstrELF( + StringRef Filename, std::deque &OutputSleds, + InstrumentationMapExtractor::FunctionAddressMap &InstrMap, + InstrumentationMapExtractor::FunctionAddressReverseMap &FunctionIds) { + auto ObjectFile = object::ObjectFile::createObjectFile(Filename); + + if (!ObjectFile) + return ObjectFile.takeError(); + + // FIXME: Maybe support other ELF formats. For now, 64-bit Little Endian only. + if (!ObjectFile->getBinary()->isELF()) + return make_error( + "File format not supported (only does ELF).", + std::make_error_code(std::errc::not_supported)); + if (ObjectFile->getBinary()->getArch() != Triple::x86_64) + return make_error( + "File format not supported (only does ELF little endian 64-bit).", + std::make_error_code(std::errc::not_supported)); + + // Find the section named "xray_instr_map". + StringRef Contents = ""; + const auto &Sections = ObjectFile->getBinary()->sections(); + auto I = find_if(Sections, [&](object::SectionRef Section) { + StringRef Name = ""; + if (Section.getName(Name)) + return false; + return Name == "xray_instr_map"; + }); + if (I == Sections.end()) + return make_error( + "Failed to find XRay instrumentation map.", + std::make_error_code(std::errc::not_supported)); + if (I->getContents(Contents)) + return make_error( + "Failed to get contents of 'xray_instr_map' section.", + std::make_error_code(std::errc::executable_format_error)); + + // Copy the instrumentation map data into the Sleds data structure. + auto C = Contents.bytes_begin(); + static constexpr size_t ELF64SledEntrySize = 32; + + if ((C - Contents.bytes_end()) % ELF64SledEntrySize != 0) + return make_error( + "Instrumentation map entries not evenly divisible by size of an XRay " + "sled entry in ELF64.", + std::make_error_code(std::errc::executable_format_error)); + + int32_t FuncId = 1; + uint64_t CurFn = 0; + std::deque Sleds; + for (; C != Contents.bytes_end(); C += ELF64SledEntrySize) { + DataExtractor Extractor( + StringRef(reinterpret_cast(C), ELF64SledEntrySize), true, + 8); + Sleds.push_back({}); + auto &Entry = Sleds.back(); + uint32_t OffsetPtr = 0; + Entry.Address = Extractor.getU64(&OffsetPtr); + Entry.Function = Extractor.getU64(&OffsetPtr); + auto Kind = Extractor.getU8(&OffsetPtr); + switch (Kind) { + case 0: // ENTRY + Entry.Kind = SledEntry::FunctionKinds::ENTRY; + break; + case 1: // EXIT + Entry.Kind = SledEntry::FunctionKinds::EXIT; + break; + case 2: // TAIL + Entry.Kind = SledEntry::FunctionKinds::TAIL; + break; + default: + return make_error( + Twine("Encountered unknown sled type ") + "'" + Twine(int32_t{Kind}) + + "'.", + std::make_error_code(std::errc::executable_format_error)); + } + Entry.AlwaysInstrument = Extractor.getU8(&OffsetPtr) != 0; + + // We replicate the function id generation scheme implemented in the runtime + // here. Ideally we should be able to break it out, or output this map from + // the runtime, but that's a design point we can discuss later on. For now, + // we replicate the logic and move on. + if (CurFn == 0) { + CurFn = Entry.Function; + InstrMap[FuncId] = Entry.Function; + FunctionIds[Entry.Function] = FuncId; + } + if (Entry.Function != CurFn) { + ++FuncId; + CurFn = Entry.Function; + InstrMap[FuncId] = Entry.Function; + FunctionIds[Entry.Function] = FuncId; + } + } + OutputSleds = std::move(Sleds); + return llvm::Error::success(); +} + +Error LoadYAMLInstrMap( + StringRef Filename, std::deque &Sleds, + InstrumentationMapExtractor::FunctionAddressMap &InstrMap, + InstrumentationMapExtractor::FunctionAddressReverseMap &FunctionIds) { + int Fd; + if (auto EC = sys::fs::openFileForRead(Filename, Fd)) + return make_error( + Twine("Failed opening file '") + Filename + "' for reading.", EC); + + uint64_t FileSize; + if (auto EC = sys::fs::file_size(Filename, FileSize)) + return make_error( + Twine("Failed getting size of file '") + Filename + "'.", EC); + + std::error_code EC; + sys::fs::mapped_file_region MappedFile( + Fd, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0, EC); + if (EC) + return make_error( + Twine("Failed memory-mapping file '") + Filename + "'.", EC); + + std::vector YAMLSleds; + Input In(StringRef(MappedFile.data(), MappedFile.size())); + In >> YAMLSleds; + if (In.error()) + return make_error( + Twine("Failed loading YAML document from '") + Filename + "'.", + In.error()); + + for (const auto &Y : YAMLSleds) { + InstrMap[Y.FuncId] = Y.Function; + FunctionIds[Y.Function] = Y.FuncId; + Sleds.push_back( + SledEntry{Y.Address, Y.Function, Y.Kind, Y.AlwaysInstrument}); + } + return Error::success(); +} + +} // namespace + +InstrumentationMapExtractor::InstrumentationMapExtractor(std::string Filename, + InputFormats Format, + Error &EC) { + ErrorAsOutParameter ErrAsOutputParam(&EC); + if (Filename.empty()) { + EC = Error::success(); + return; + } + switch (Format) { + case InputFormats::ELF: { + EC = handleErrors( + LoadBinaryInstrELF(Filename, Sleds, FunctionAddresses, FunctionIds), + [&](std::unique_ptr E) { + return joinErrors( + make_error( + Twine("Cannot extract instrumentation map from '") + + Filename + "'.", + std::make_error_code(std::errc::executable_format_error)), + std::move(E)); + }); + break; + } + case InputFormats::YAML: { + EC = handleErrors( + LoadYAMLInstrMap(Filename, Sleds, FunctionAddresses, FunctionIds), + [&](std::unique_ptr E) { + return joinErrors( + make_error( + Twine("Cannot load YAML instrumentation map from '") + + Filename + "'.", + std::make_error_code(std::errc::executable_format_error)), + std::move(E)); + }); + break; + } + } +} + +void InstrumentationMapExtractor::exportAsYAML(raw_ostream &OS) { // First we translate the sleds into the YAMLXRaySledEntry objects in a deque. std::vector YAMLSleds; - auto Sleds = Map.sleds(); - YAMLSleds.reserve(std::distance(Sleds.begin(), Sleds.end())); + YAMLSleds.reserve(Sleds.size()); for (const auto &Sled : Sleds) { - auto FuncId = Map.getFunctionId(Sled.Function); - if (!FuncId) - return; - YAMLSleds.push_back({*FuncId, Sled.Address, Sled.Function, Sled.Kind, - Sled.AlwaysInstrument, - ExtractSymbolize ? FH.SymbolOrNumber(*FuncId) : ""}); + YAMLSleds.push_back({FunctionIds[Sled.Function], Sled.Address, + Sled.Function, Sled.Kind, Sled.AlwaysInstrument}); } - Output Out(OS, nullptr, 0); + Output Out(OS); Out << YAMLSleds; } -} // namespace - static CommandRegistration Unused(&Extract, []() -> Error { - auto InstrumentationMapOrError = loadInstrumentationMap(ExtractInput); - if (!InstrumentationMapOrError) - return joinErrors(make_error( - Twine("Cannot extract instrumentation map from '") + - ExtractInput + "'.", - std::make_error_code(std::errc::invalid_argument)), - InstrumentationMapOrError.takeError()); + Error Err = Error::success(); + xray::InstrumentationMapExtractor Extractor( + ExtractInput, InstrumentationMapExtractor::InputFormats::ELF, Err); + if (Err) + return Err; std::error_code EC; raw_fd_ostream OS(ExtractOutput, EC, sys::fs::OpenFlags::F_Text); if (EC) return make_error( Twine("Cannot open file '") + ExtractOutput + "' for writing.", EC); - const auto &FunctionAddresses = - InstrumentationMapOrError->getFunctionAddresses(); - symbolize::LLVMSymbolizer::Options Opts( - symbolize::FunctionNameKind::LinkageName, true, true, false, ""); - symbolize::LLVMSymbolizer Symbolizer(Opts); - llvm::xray::FuncIdConversionHelper FuncIdHelper(ExtractInput, Symbolizer, - FunctionAddresses); - exportAsYAML(*InstrumentationMapOrError, OS, FuncIdHelper); + Extractor.exportAsYAML(OS); return Error::success(); }); -- cgit v1.2.3