From: Dean Michael Berris Date: Tue, 10 Jan 2017 02:38:11 +0000 (+0000) Subject: [XRay] Implement `llvm-xray convert` -- trace file conversion X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=6e1f066adf1fd96dc62b8fcf57386fadc3bdf570;p=llvm [XRay] Implement `llvm-xray convert` -- trace file conversion This is the second part of a multi-part change to define additional subcommands to the `llvm-xray` tool. This change defines a conversion subcommand to take XRay log files, and turns them from one format to another (binary or YAML). This currently only supports the first version of the log file format, defined in the compiler-rt runtime. Depends on D21987. Reviewers: dblaikie, echristo Subscribers: mehdi_amini, dberris, beanz, llvm-commits Differential Revision: https://reviews.llvm.org/D24376 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@291529 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/test/tools/llvm-xray/X86/Inputs/elf64-objcopied-instrmap.bin b/test/tools/llvm-xray/X86/Inputs/elf64-objcopied-instrmap.bin new file mode 100755 index 00000000000..4e1f982e267 Binary files /dev/null and b/test/tools/llvm-xray/X86/Inputs/elf64-objcopied-instrmap.bin differ diff --git a/test/tools/llvm-xray/X86/Inputs/elf64-sample-o2.bin b/test/tools/llvm-xray/X86/Inputs/elf64-sample-o2.bin new file mode 100755 index 00000000000..fe31f9514d3 Binary files /dev/null and b/test/tools/llvm-xray/X86/Inputs/elf64-sample-o2.bin differ diff --git a/test/tools/llvm-xray/X86/Inputs/naive-log-simple.xray b/test/tools/llvm-xray/X86/Inputs/naive-log-simple.xray new file mode 100644 index 00000000000..284bc6b63e9 Binary files /dev/null and b/test/tools/llvm-xray/X86/Inputs/naive-log-simple.xray differ diff --git a/test/tools/llvm-xray/X86/Inputs/simple-xray-instrmap.yaml b/test/tools/llvm-xray/X86/Inputs/simple-xray-instrmap.yaml new file mode 100644 index 00000000000..483d3e4f2c8 --- /dev/null +++ b/test/tools/llvm-xray/X86/Inputs/simple-xray-instrmap.yaml @@ -0,0 +1,14 @@ +--- +- { id: 1, address: 0x000000000041CA40, function: 0x000000000041CA40, kind: function-enter, + always-instrument: true } +- { id: 1, address: 0x000000000041CA50, function: 0x000000000041CA40, kind: tail-exit, + always-instrument: true } +- { id: 2, address: 0x000000000041CA70, function: 0x000000000041CA70, kind: function-enter, + always-instrument: true } +- { id: 2, address: 0x000000000041CA7C, function: 0x000000000041CA70, kind: tail-exit, + always-instrument: true } +- { id: 3, address: 0x000000000041CAA0, function: 0x000000000041CAA0, kind: function-enter, + always-instrument: true } +- { id: 3, address: 0x000000000041CAB4, function: 0x000000000041CAA0, kind: function-exit, + always-instrument: true } +... diff --git a/test/tools/llvm-xray/X86/bad-instrmap-sizes.bin b/test/tools/llvm-xray/X86/bad-instrmap-sizes.txt similarity index 100% rename from test/tools/llvm-xray/X86/bad-instrmap-sizes.bin rename to test/tools/llvm-xray/X86/bad-instrmap-sizes.txt diff --git a/test/tools/llvm-xray/X86/convert-roundtrip.yaml b/test/tools/llvm-xray/X86/convert-roundtrip.yaml new file mode 100644 index 00000000000..5eca93a2d39 --- /dev/null +++ b/test/tools/llvm-xray/X86/convert-roundtrip.yaml @@ -0,0 +1,28 @@ +#RUN: llvm-xray convert %s -i=yaml -f=raw -o %t && llvm-xray convert %t -f=yaml -o - | FileCheck %s +--- +header: + version: 1 + type: 0 + constant-tsc: true + nonstop-tsc: true + cycle-frequency: 2601000000 +records: + - { type: 0, func-id: 1, cpu: 1, thread: 111, kind: function-enter, + tsc: 10001 } + - { type: 0, func-id: 1, cpu: 1, thread: 111, kind: function-exit, + tsc: 10100 } +... + +#CHECK: --- +#CHECK-NEXT: header: +#CHECK-NEXT: version: 1 +#CHECK-NEXT: type: 0 +#CHECK-NEXT: constant-tsc: true +#CHECK-NEXT: nonstop-tsc: true +#CHECK-NEXT: cycle-frequency: 2601000000 +#CHECK-NEXT: records: +#CHECK-NEXT: - { type: 0, func-id: 1, function: '1', cpu: 1, thread: 111, kind: function-enter, +#CHECK-NEXT: tsc: 10001 } +#CHECK-NEXT: - { type: 0, func-id: 1, function: '1', cpu: 1, thread: 111, kind: function-exit, +#CHECK-NEXT: tsc: 10100 } +#CHECK-NEXT: ... diff --git a/test/tools/llvm-xray/X86/convert-to-yaml.txt b/test/tools/llvm-xray/X86/convert-to-yaml.txt new file mode 100644 index 00000000000..c402bc18d83 --- /dev/null +++ b/test/tools/llvm-xray/X86/convert-to-yaml.txt @@ -0,0 +1,23 @@ +; RUN: llvm-xray convert %S/Inputs/naive-log-simple.xray -f=yaml -o - | FileCheck %s + +; CHECK: --- +; CHECK-NEXT: header: +; CHECK-NEXT: version: 1 +; CHECK-NEXT: type: 0 +; CHECK-NEXT: constant-tsc: true +; CHECK-NEXT: nonstop-tsc: true +; CHECK-NEXT: cycle-frequency: 2601000000 +; CHECK-NEXT: records: +; CHECK-NEXT: - { type: 0, func-id: 3, function: '3', cpu: 37, thread: 84697, kind: function-enter, +; CHECK-NEXT: tsc: 3315356841453914 } +; CHECK-NEXT: - { type: 0, func-id: 2, function: '2', cpu: 37, thread: 84697, kind: function-enter, +; CHECK-NEXT: tsc: 3315356841454542 } +; CHECK-NEXT: - { type: 0, func-id: 2, function: '2', cpu: 37, thread: 84697, kind: function-exit, +; CHECK-NEXT: tsc: 3315356841454670 } +; CHECK-NEXT: - { type: 0, func-id: 1, function: '1', cpu: 37, thread: 84697, kind: function-enter, +; CHECK-NEXT: tsc: 3315356841454762 } +; CHECK-NEXT: - { type: 0, func-id: 1, function: '1', cpu: 37, thread: 84697, kind: function-exit, +; CHECK-NEXT: tsc: 3315356841454802 } +; CHECK-NEXT: - { type: 0, func-id: 3, function: '3', cpu: 37, thread: 84697, kind: function-exit, +; CHECK-NEXT: tsc: 3315356841494828 } +; CHECK-NEXT: ... diff --git a/test/tools/llvm-xray/X86/convert-with-debug-syms.txt b/test/tools/llvm-xray/X86/convert-with-debug-syms.txt new file mode 100644 index 00000000000..d7e892bedaa --- /dev/null +++ b/test/tools/llvm-xray/X86/convert-with-debug-syms.txt @@ -0,0 +1,23 @@ +; RUN: llvm-xray convert -m %S/Inputs/elf64-sample-o2.bin -y %S/Inputs/naive-log-simple.xray -f=yaml -o - 2>&1 | FileCheck %s + +; CHECK: --- +; CHECK-NEXT: header: +; CHECK-NEXT: version: 1 +; CHECK-NEXT: type: 0 +; CHECK-NEXT: constant-tsc: true +; CHECK-NEXT: nonstop-tsc: true +; CHECK-NEXT: cycle-frequency: 2601000000 +; CHECK-NEXT: records: +; CHECK-NEXT: - { type: 0, func-id: 3, function: main, cpu: 37, thread: 84697, kind: function-enter, +; CHECK-NEXT: tsc: 3315356841453914 } +; CHECK-NEXT: - { type: 0, func-id: 2, function: 'foo()', cpu: 37, thread: 84697, kind: function-enter, +; CHECK-NEXT: tsc: 3315356841454542 } +; CHECK-NEXT: - { type: 0, func-id: 2, function: 'foo()', cpu: 37, thread: 84697, kind: function-exit, +; CHECK-NEXT: tsc: 3315356841454670 } +; CHECK-NEXT: - { type: 0, func-id: 1, function: 'bar()', cpu: 37, thread: 84697, kind: function-enter, +; CHECK-NEXT: tsc: 3315356841454762 } +; CHECK-NEXT: - { type: 0, func-id: 1, function: 'bar()', cpu: 37, thread: 84697, kind: function-exit, +; CHECK-NEXT: tsc: 3315356841454802 } +; CHECK-NEXT: - { type: 0, func-id: 3, function: main, cpu: 37, thread: 84697, kind: function-exit, +; CHECK-NEXT: tsc: 3315356841494828 } +; CHECK-NEXT: ... diff --git a/test/tools/llvm-xray/X86/convert-with-standalone-instrmap.txt b/test/tools/llvm-xray/X86/convert-with-standalone-instrmap.txt new file mode 100644 index 00000000000..71c17280df4 --- /dev/null +++ b/test/tools/llvm-xray/X86/convert-with-standalone-instrmap.txt @@ -0,0 +1,23 @@ +; RUN: llvm-xray convert -m %S/Inputs/elf64-objcopied-instrmap.bin -y %S/Inputs/naive-log-simple.xray -f=yaml -o - 2>&1 | FileCheck %s + +; CHECK: --- +; CHECK-NEXT: header: +; CHECK-NEXT: version: 1 +; CHECK-NEXT: type: 0 +; CHECK-NEXT: constant-tsc: true +; CHECK-NEXT: nonstop-tsc: true +; CHECK-NEXT: cycle-frequency: 2601000000 +; CHECK-NEXT: records: +; CHECK-NEXT: - { type: 0, func-id: 3, function: '@(41caa0)', cpu: 37, thread: 84697, +; CHECK-NEXT: kind: function-enter, tsc: 3315356841453914 } +; CHECK-NEXT: - { type: 0, func-id: 2, function: '@(41ca70)', cpu: 37, thread: 84697, +; CHECK-NEXT: kind: function-enter, tsc: 3315356841454542 } +; CHECK-NEXT: - { type: 0, func-id: 2, function: '@(41ca70)', cpu: 37, thread: 84697, +; CHECK-NEXT: kind: function-exit, tsc: 3315356841454670 } +; CHECK-NEXT: - { type: 0, func-id: 1, function: '@(41ca40)', cpu: 37, thread: 84697, +; CHECK-NEXT: kind: function-enter, tsc: 3315356841454762 } +; CHECK-NEXT: - { type: 0, func-id: 1, function: '@(41ca40)', cpu: 37, thread: 84697, +; CHECK-NEXT: kind: function-exit, tsc: 3315356841454802 } +; CHECK-NEXT: - { type: 0, func-id: 3, function: '@(41caa0)', cpu: 37, thread: 84697, +; CHECK-NEXT: kind: function-exit, tsc: 3315356841494828 } +; CHECK-NEXT: ... diff --git a/test/tools/llvm-xray/X86/convert-with-yaml-instrmap.txt b/test/tools/llvm-xray/X86/convert-with-yaml-instrmap.txt new file mode 100644 index 00000000000..01191c9c2a3 --- /dev/null +++ b/test/tools/llvm-xray/X86/convert-with-yaml-instrmap.txt @@ -0,0 +1,23 @@ +; RUN: llvm-xray convert -m %S/Inputs/simple-xray-instrmap.yaml -t yaml %S/Inputs/naive-log-simple.xray -f=yaml -o - | FileCheck %s + +; CHECK: --- +; CHECK-NEXT: header: +; CHECK-NEXT: version: 1 +; CHECK-NEXT: type: 0 +; CHECK-NEXT: constant-tsc: true +; CHECK-NEXT: nonstop-tsc: true +; CHECK-NEXT: cycle-frequency: 2601000000 +; CHECK-NEXT: records: +; CHECK-NEXT: - { type: 0, func-id: 3, function: '3', cpu: 37, thread: 84697, kind: function-enter, +; CHECK-NEXT: tsc: 3315356841453914 } +; CHECK-NEXT: - { type: 0, func-id: 2, function: '2', cpu: 37, thread: 84697, kind: function-enter, +; CHECK-NEXT: tsc: 3315356841454542 } +; CHECK-NEXT: - { type: 0, func-id: 2, function: '2', cpu: 37, thread: 84697, kind: function-exit, +; CHECK-NEXT: tsc: 3315356841454670 } +; CHECK-NEXT: - { type: 0, func-id: 1, function: '1', cpu: 37, thread: 84697, kind: function-enter, +; CHECK-NEXT: tsc: 3315356841454762 } +; CHECK-NEXT: - { type: 0, func-id: 1, function: '1', cpu: 37, thread: 84697, kind: function-exit, +; CHECK-NEXT: tsc: 3315356841454802 } +; CHECK-NEXT: - { type: 0, func-id: 3, function: '3', cpu: 37, thread: 84697, kind: function-exit, +; CHECK-NEXT: tsc: 3315356841494828 } +; CHECK-NEXT: ... diff --git a/tools/llvm-xray/CMakeLists.txt b/tools/llvm-xray/CMakeLists.txt index 0084e35c1b0..5b0b854cb29 100644 --- a/tools/llvm-xray/CMakeLists.txt +++ b/tools/llvm-xray/CMakeLists.txt @@ -1,10 +1,16 @@ set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} + DebugInfoDWARF + Object Support - Object) + Symbolize) set(LLVM_XRAY_TOOLS + func-id-helper.cc + xray-converter.cc xray-extract.cc + xray-extract.cc + xray-log-reader.cc xray-registry.cc) add_llvm_tool(llvm-xray llvm-xray.cc ${LLVM_XRAY_TOOLS}) diff --git a/tools/llvm-xray/func-id-helper.cc b/tools/llvm-xray/func-id-helper.cc new file mode 100644 index 00000000000..3234010695b --- /dev/null +++ b/tools/llvm-xray/func-id-helper.cc @@ -0,0 +1,60 @@ +//===- xray-fc-account.cc - XRay Function Call Accounting Tool ------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implementation of the helper tools dealing with XRay-generated function ids. +// +//===----------------------------------------------------------------------===// + +#include "func-id-helper.h" +#include "llvm/Support/Path.h" +#include + +using namespace llvm; +using namespace xray; + +std::string FuncIdConversionHelper::SymbolOrNumber(int32_t FuncId) const { + std::ostringstream F; + auto It = FunctionAddresses.find(FuncId); + if (It == FunctionAddresses.end()) { + F << "#" << FuncId; + return F.str(); + } + + if (auto ResOrErr = Symbolizer.symbolizeCode(BinaryInstrMap, It->second)) { + auto &DI = *ResOrErr; + if (DI.FunctionName == "") + F << "@(" << std::hex << It->second << ")"; + else + F << DI.FunctionName; + } else + handleAllErrors(ResOrErr.takeError(), [&](const ErrorInfoBase &) { + F << "@(" << std::hex << It->second << ")"; + }); + + return F.str(); +} + +std::string FuncIdConversionHelper::FileLineAndColumn(int32_t FuncId) const { + auto It = FunctionAddresses.find(FuncId); + if (It == FunctionAddresses.end()) + return "(unknown)"; + + std::ostringstream F; + auto ResOrErr = Symbolizer.symbolizeCode(BinaryInstrMap, It->second); + if (!ResOrErr) { + consumeError(ResOrErr.takeError()); + return "(unknown)"; + } + + auto &DI = *ResOrErr; + F << sys::path::filename(DI.FileName).str() << ":" << DI.Line << ":" + << DI.Column; + + return F.str(); +} diff --git a/tools/llvm-xray/func-id-helper.h b/tools/llvm-xray/func-id-helper.h new file mode 100644 index 00000000000..7348a7100b0 --- /dev/null +++ b/tools/llvm-xray/func-id-helper.h @@ -0,0 +1,49 @@ +//===- func-id-helper.h - XRay Function ID Conversion Helpers -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Defines helper tools dealing with XRay-generated function ids. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_TOOLS_LLVM_XRAY_FUNC_ID_HELPER_H +#define LLVM_TOOLS_LLVM_XRAY_FUNC_ID_HELPER_H + +#include "llvm/DebugInfo/Symbolize/Symbolize.h" +#include + +namespace llvm { +namespace xray { + +// This class consolidates common operations related to Function IDs. +class FuncIdConversionHelper { +public: + using FunctionAddressMap = std::unordered_map; + +private: + std::string BinaryInstrMap; + symbolize::LLVMSymbolizer &Symbolizer; + const FunctionAddressMap &FunctionAddresses; + +public: + FuncIdConversionHelper(std::string BinaryInstrMap, + symbolize::LLVMSymbolizer &Symbolizer, + const FunctionAddressMap &FunctionAddresses) + : BinaryInstrMap(std::move(BinaryInstrMap)), Symbolizer(Symbolizer), + FunctionAddresses(FunctionAddresses) {} + + // Returns the symbol or a string representation of the function id. + std::string SymbolOrNumber(int32_t FuncId) const; + + // Returns the file and column from debug info for the given function id. + std::string FileLineAndColumn(int32_t FuncId) const; +}; + +} // namespace xray +} // namespace llvm + +#endif // LLVM_TOOLS_LLVM_XRAY_FUNC_ID_HELPER_H diff --git a/tools/llvm-xray/xray-converter.cc b/tools/llvm-xray/xray-converter.cc new file mode 100644 index 00000000000..536b2b159ed --- /dev/null +++ b/tools/llvm-xray/xray-converter.cc @@ -0,0 +1,222 @@ +//===- xray-converter.cc - XRay Trace Conversion --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implements the trace conversion functions. +// +//===----------------------------------------------------------------------===// +#include "xray-converter.h" + +#include "xray-extract.h" +#include "xray-record-yaml.h" +#include "xray-registry.h" +#include "llvm/DebugInfo/Symbolize/Symbolize.h" +#include "llvm/Support/EndianStream.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/YAMLTraits.h" +#include "llvm/Support/raw_ostream.h" +#include + +using namespace llvm; +using namespace xray; + +// llvm-xray convert +// ---------------------------------------------------------------------------- +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 }; +static cl::opt ConvertInputFormat( + "input-format", cl::desc("input format"), + cl::values(clEnumValN(ConvertFormats::BINARY, "raw", + "input is in raw binary"), + clEnumValN(ConvertFormats::YAML, "yaml", "input is in yaml")), + cl::sub(Convert)); +static cl::alias ConvertInputFormat2("i", cl::aliasopt(ConvertInputFormat), + cl::desc("Alias for -input-format"), + cl::sub(Convert)); +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")), + cl::sub(Convert)); +static cl::alias ConvertOutputFormat2("f", cl::aliasopt(ConvertOutputFormat), + cl::desc("Alias for -output-format"), + cl::sub(Convert)); +static cl::opt + ConvertOutput("output", cl::value_desc("output file"), cl::init("-"), + cl::desc("output file; use '-' for stdout"), + cl::sub(Convert)); +static cl::alias ConvertOutput2("o", cl::aliasopt(ConvertOutput), + cl::desc("Alias for -output"), + cl::sub(Convert)); + +static cl::opt + ConvertSymbolize("symbolize", + cl::desc("symbolize function ids from the input log"), + cl::init(false), cl::sub(Convert)); +static cl::alias ConvertSymbolize2("y", cl::aliasopt(ConvertSymbolize), + cl::desc("Alias for -symbolize"), + cl::sub(Convert)); + +static cl::opt + ConvertInstrMap("instr_map", + cl::desc("binary with the instrumentation map, or " + "a separate instrumentation map"), + cl::value_desc("binary with xray_instr_map"), + cl::sub(Convert), cl::init("")); +static cl::alias ConvertInstrMap2("m", cl::aliasopt(ConvertInstrMap), + cl::desc("Alias for -instr_map"), + cl::sub(Convert)); +static cl::opt ConvertSortInput( + "sort", + cl::desc("determines whether to sort input log records by timestamp"), + cl::sub(Convert), cl::init(true)); +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::MappingTraits; +using llvm::yaml::ScalarEnumerationTraits; +using llvm::yaml::IO; +using llvm::yaml::Output; + +void TraceConverter::exportAsYAML(const LogReader &Records, raw_ostream &OS) { + YAMLXRayTrace Trace; + const auto &FH = Records.getFileHeader(); + Trace.Header = {FH.Version, FH.Type, FH.ConstantTSC, FH.NonstopTSC, + FH.CycleFrequency}; + Trace.Records.reserve(Records.size()); + for (const auto &R : Records) { + Trace.Records.push_back({R.RecordType, R.CPU, R.Type, R.FuncId, + Symbolize ? FuncIdHelper.SymbolOrNumber(R.FuncId) + : std::to_string(R.FuncId), + R.TSC, R.TId}); + } + Output Out(OS); + Out << Trace; +} + +void TraceConverter::exportAsRAWv1(const LogReader &Records, raw_ostream &OS) { + // First write out the file header, in the correct endian-appropriate format + // (XRay assumes currently little endian). + support::endian::Writer Writer(OS); + const auto &FH = Records.getFileHeader(); + Writer.write(FH.Version); + Writer.write(FH.Type); + uint32_t Bitfield{0}; + if (FH.ConstantTSC) + Bitfield |= 1uL; + if (FH.NonstopTSC) + Bitfield |= 1uL << 1; + Writer.write(Bitfield); + Writer.write(FH.CycleFrequency); + + // There's 16 bytes of padding at the end of the file header. + static constexpr uint32_t Padding4B = 0; + Writer.write(Padding4B); + Writer.write(Padding4B); + Writer.write(Padding4B); + Writer.write(Padding4B); + + // Then write out the rest of the records, still in an endian-appropriate + // format. + for (const auto &R : Records) { + Writer.write(R.RecordType); + Writer.write(R.CPU); + switch (R.Type) { + case RecordTypes::ENTER: + Writer.write(uint8_t{0}); + break; + case RecordTypes::EXIT: + Writer.write(uint8_t{1}); + break; + } + Writer.write(R.FuncId); + Writer.write(R.TSC); + Writer.write(R.TId); + Writer.write(Padding4B); + Writer.write(Padding4B); + Writer.write(Padding4B); + } +} + +namespace llvm { +namespace xray { + +static CommandRegistration Unused(&Convert, []() -> Error { + // FIXME: Support conversion to BINARY when upgrading XRay trace versions. + int Fd; + auto EC = sys::fs::openFileForRead(ConvertInput, Fd); + if (EC) + return make_error( + Twine("Cannot open file '") + ConvertInput + "'", EC); + + 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); + LogReader::LoaderFunction Loader; + switch (ConvertInputFormat) { + case ConvertFormats::BINARY: + Loader = NaiveLogLoader; + break; + case ConvertFormats::YAML: + Loader = YAMLLogLoader; + break; + } + + LogReader Reader(ConvertInput, Err, ConvertSortInput, Loader); + if (Err) + return joinErrors( + make_error( + Twine("Failed loading input file '") + ConvertInput + "'.", + std::make_error_code(std::errc::protocol_error)), + std::move(Err)); + + raw_fd_ostream OS(ConvertOutput, EC, + ConvertOutputFormat == ConvertFormats::BINARY + ? sys::fs::OpenFlags::F_None + : sys::fs::OpenFlags::F_Text); + if (EC) + return make_error( + Twine("Cannot open file '") + ConvertOutput + "' for writing.", EC); + + switch (ConvertOutputFormat) { + case ConvertFormats::YAML: + TC.exportAsYAML(Reader, OS); + break; + case ConvertFormats::BINARY: + TC.exportAsRAWv1(Reader, OS); + break; + } + return Error::success(); +}); + +} // namespace xray +} // namespace llvm diff --git a/tools/llvm-xray/xray-converter.h b/tools/llvm-xray/xray-converter.h new file mode 100644 index 00000000000..f830c7fa316 --- /dev/null +++ b/tools/llvm-xray/xray-converter.h @@ -0,0 +1,39 @@ +//===- xray-converter.h - XRay Trace Conversion ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Defines the TraceConverter class for turning binary traces into +// human-readable text and vice versa. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_TOOLS_LLVM_XRAY_XRAY_CONVERTER_H +#define LLVM_TOOLS_LLVM_XRAY_XRAY_CONVERTER_H + +#include "func-id-helper.h" +#include "xray-log-reader.h" +#include "xray-record.h" + +namespace llvm { +namespace xray { + +class TraceConverter { + FuncIdConversionHelper &FuncIdHelper; + bool Symbolize; + +public: + TraceConverter(FuncIdConversionHelper &FuncIdHelper, bool Symbolize = false) + : FuncIdHelper(FuncIdHelper), Symbolize(Symbolize) {} + + void exportAsYAML(const LogReader &Records, raw_ostream &OS); + void exportAsRAWv1(const LogReader &Records, raw_ostream &OS); +}; + +} // namespace xray +} // namespace llvm + +#endif // LLVM_TOOLS_LLVM_XRAY_XRAY_CONVERTER_H diff --git a/tools/llvm-xray/xray-extract.cc b/tools/llvm-xray/xray-extract.cc index e51b64c8ad6..d7b5bd2b1fe 100644 --- a/tools/llvm-xray/xray-extract.cc +++ b/tools/llvm-xray/xray-extract.cc @@ -162,8 +162,7 @@ llvm::Error LoadBinaryInstrELF( "'.", std::make_error_code(std::errc::executable_format_error)); } - auto AlwaysInstrument = Extractor.getU8(&OffsetPtr); - Entry.AlwaysInstrument = AlwaysInstrument != 0; + 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 @@ -185,30 +184,82 @@ llvm::Error LoadBinaryInstrELF( 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) { + [&](std::unique_ptr E) { return joinErrors( make_error( Twine("Cannot extract instrumentation map from '") + - ExtractInput + "'.", + Filename + "'.", std::make_error_code(std::errc::executable_format_error)), std::move(E)); }); break; } - default: - llvm_unreachable("Input format type not supported yet."); + 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::wrong_protocol_type)), + std::move(E)); + }); break; } + } } void InstrumentationMapExtractor::exportAsYAML(raw_ostream &OS) { diff --git a/tools/llvm-xray/xray-log-reader.cc b/tools/llvm-xray/xray-log-reader.cc new file mode 100644 index 00000000000..96bbaaf2d47 --- /dev/null +++ b/tools/llvm-xray/xray-log-reader.cc @@ -0,0 +1,165 @@ +//===- xray-log-reader.cc - XRay Log Reader Implementation ----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// XRay log reader implementation. +// +//===----------------------------------------------------------------------===// +#include + +#include "xray-log-reader.h" +#include "xray-record-yaml.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/FileSystem.h" + +using namespace llvm; +using namespace llvm::xray; +using llvm::yaml::Input; + +LogReader::LogReader( + StringRef Filename, Error &Err, bool Sort, + std::function &)> + Loader) { + ErrorAsOutParameter Guard(&Err); + int Fd; + if (auto EC = sys::fs::openFileForRead(Filename, Fd)) { + Err = make_error( + Twine("Cannot read log from '") + Filename + "'", EC); + return; + } + uint64_t FileSize; + if (auto EC = sys::fs::file_size(Filename, FileSize)) { + Err = make_error( + Twine("Cannot read log from '") + Filename + "'", EC); + return; + } + + std::error_code EC; + sys::fs::mapped_file_region MappedFile( + Fd, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0, EC); + if (EC) { + Err = make_error( + Twine("Cannot read log from '") + Filename + "'", EC); + return; + } + + if (auto E = Loader(StringRef(MappedFile.data(), MappedFile.size()), + FileHeader, Records)) { + Err = std::move(E); + return; + } + + if (Sort) + std::sort( + Records.begin(), Records.end(), + [](const XRayRecord &L, const XRayRecord &R) { return L.TSC < R.TSC; }); +} + +Error llvm::xray::NaiveLogLoader(StringRef Data, XRayFileHeader &FileHeader, + std::vector &Records) { + // FIXME: Maybe deduce whether the data is little or big-endian using some + // magic bytes in the beginning of the file? + + // First 32 bytes of the file will always be the header. We assume a certain + // format here: + // + // (2) uint16 : version + // (2) uint16 : type + // (4) uint32 : bitfield + // (8) uint64 : cycle frequency + // (16) - : padding + // + if (Data.size() < 32) + return make_error( + "Not enough bytes for an XRay log.", + std::make_error_code(std::errc::invalid_argument)); + + if (Data.size() - 32 == 0 || Data.size() % 32 != 0) + return make_error( + "Invalid-sized XRay data.", + std::make_error_code(std::errc::invalid_argument)); + + DataExtractor HeaderExtractor(Data, true, 8); + uint32_t OffsetPtr = 0; + FileHeader.Version = HeaderExtractor.getU16(&OffsetPtr); + FileHeader.Type = HeaderExtractor.getU16(&OffsetPtr); + uint32_t Bitfield = HeaderExtractor.getU32(&OffsetPtr); + FileHeader.ConstantTSC = Bitfield & 1uL; + FileHeader.NonstopTSC = Bitfield & 1uL << 1; + FileHeader.CycleFrequency = HeaderExtractor.getU64(&OffsetPtr); + + if (FileHeader.Version != 1) + return make_error( + Twine("Unsupported XRay file version: ") + Twine(FileHeader.Version), + std::make_error_code(std::errc::invalid_argument)); + + // Each record after the header will be 32 bytes, in the following format: + // + // (2) uint16 : record type + // (1) uint8 : cpu id + // (1) uint8 : type + // (4) sint32 : function id + // (8) uint64 : tsc + // (4) uint32 : thread id + // (12) - : padding + for (auto S = Data.drop_front(32); !S.empty(); S = S.drop_front(32)) { + DataExtractor RecordExtractor(S, true, 8); + uint32_t OffsetPtr = 0; + Records.emplace_back(); + auto &Record = Records.back(); + Record.RecordType = RecordExtractor.getU16(&OffsetPtr); + Record.CPU = RecordExtractor.getU8(&OffsetPtr); + auto Type = RecordExtractor.getU8(&OffsetPtr); + switch (Type) { + case 0: + Record.Type = RecordTypes::ENTER; + break; + case 1: + Record.Type = RecordTypes::EXIT; + break; + default: + return make_error( + Twine("Unknown record type '") + Twine(int{Type}) + "'", + std::make_error_code(std::errc::protocol_error)); + } + Record.FuncId = RecordExtractor.getSigned(&OffsetPtr, sizeof(int32_t)); + Record.TSC = RecordExtractor.getU64(&OffsetPtr); + Record.TId = RecordExtractor.getU32(&OffsetPtr); + } + return Error::success(); +} + +Error llvm::xray::YAMLLogLoader(StringRef Data, XRayFileHeader &FileHeader, + std::vector &Records) { + + // Load the documents from the MappedFile. + YAMLXRayTrace Trace; + Input In(Data); + In >> Trace; + if (In.error()) + return make_error("Failed loading YAML Data.", In.error()); + + FileHeader.Version = Trace.Header.Version; + FileHeader.Type = Trace.Header.Type; + FileHeader.ConstantTSC = Trace.Header.ConstantTSC; + FileHeader.NonstopTSC = Trace.Header.NonstopTSC; + FileHeader.CycleFrequency = Trace.Header.CycleFrequency; + + if (FileHeader.Version != 1) + return make_error( + Twine("Unsupported XRay file version: ") + Twine(FileHeader.Version), + std::make_error_code(std::errc::invalid_argument)); + + Records.clear(); + std::transform(Trace.Records.begin(), Trace.Records.end(), + std::back_inserter(Records), [&](const YAMLXRayRecord &R) { + return XRayRecord{R.RecordType, R.CPU, R.Type, + R.FuncId, R.TSC, R.TId}; + }); + return Error::success(); +} diff --git a/tools/llvm-xray/xray-log-reader.h b/tools/llvm-xray/xray-log-reader.h new file mode 100644 index 00000000000..4dd544328cf --- /dev/null +++ b/tools/llvm-xray/xray-log-reader.h @@ -0,0 +1,57 @@ +//===- xray-log-reader.h - XRay Log Reader Interface ----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Define the interface for an XRay log reader. Currently we only support one +// version of the log (naive log) with fixed-sized records. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_TOOLS_LLVM_XRAY_XRAY_LOG_READER_H +#define LLVM_TOOLS_LLVM_XRAY_XRAY_LOG_READER_H + +#include +#include +#include + +#include "xray-record-yaml.h" +#include "xray-record.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" + +namespace llvm { +namespace xray { + +class LogReader { + XRayFileHeader FileHeader; + std::vector Records; + + typedef std::vector::const_iterator citerator; + +public: + typedef std::function &)> + LoaderFunction; + + LogReader(StringRef Filename, Error &Err, bool Sort, LoaderFunction Loader); + + const XRayFileHeader &getFileHeader() const { return FileHeader; } + + citerator begin() const { return Records.begin(); } + citerator end() const { return Records.end(); } + size_t size() const { return Records.size(); } +}; + +Error NaiveLogLoader(StringRef Data, XRayFileHeader &FileHeader, + std::vector &Records); +Error YAMLLogLoader(StringRef Data, XRayFileHeader &FileHeader, + std::vector &Records); + +} // namespace xray +} // namespace llvm + +#endif // LLVM_TOOLS_LLVM_XRAY_XRAY_LOG_READER_H diff --git a/tools/llvm-xray/xray-record-yaml.h b/tools/llvm-xray/xray-record-yaml.h new file mode 100644 index 00000000000..abce8ff60a9 --- /dev/null +++ b/tools/llvm-xray/xray-record-yaml.h @@ -0,0 +1,102 @@ +//===- xray-record-yaml.h - XRay Record YAML Support Definitions ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Types and traits specialisations for YAML I/O of XRay log entries. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_TOOLS_LLVM_XRAY_XRAY_RECORD_YAML_H +#define LLVM_TOOLS_LLVM_XRAY_XRAY_RECORD_YAML_H + +#include + +#include "xray-record.h" +#include "llvm/Support/YAMLTraits.h" + +namespace llvm { +namespace xray { + +struct YAMLXRayFileHeader { + uint16_t Version; + uint16_t Type; + bool ConstantTSC; + bool NonstopTSC; + uint64_t CycleFrequency; +}; + +struct YAMLXRayRecord { + uint16_t RecordType; + uint8_t CPU; + RecordTypes Type; + int32_t FuncId; + std::string Function; + uint64_t TSC; + uint32_t TId; +}; + +struct YAMLXRayTrace { + YAMLXRayFileHeader Header; + std::vector Records; +}; + +using XRayRecordStorage = + std::aligned_storage::type; + +} // namespace xray + +namespace yaml { + +// YAML Traits +// ----------- +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &IO, xray::RecordTypes &Type) { + IO.enumCase(Type, "function-enter", xray::RecordTypes::ENTER); + IO.enumCase(Type, "function-exit", xray::RecordTypes::EXIT); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &IO, xray::YAMLXRayFileHeader &Header) { + IO.mapRequired("version", Header.Version); + IO.mapRequired("type", Header.Type); + IO.mapRequired("constant-tsc", Header.ConstantTSC); + IO.mapRequired("nonstop-tsc", Header.NonstopTSC); + IO.mapRequired("cycle-frequency", Header.CycleFrequency); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &IO, xray::YAMLXRayRecord &Record) { + // FIXME: Make this type actually be descriptive + IO.mapRequired("type", Record.RecordType); + IO.mapRequired("func-id", Record.FuncId); + IO.mapOptional("function", Record.Function); + IO.mapRequired("cpu", Record.CPU); + IO.mapRequired("thread", Record.TId); + IO.mapRequired("kind", Record.Type); + IO.mapRequired("tsc", Record.TSC); + } + + static constexpr bool flow = true; +}; + +template <> struct MappingTraits { + static void mapping(IO &IO, xray::YAMLXRayTrace &Trace) { + // A trace file contains two parts, the header and the list of all the + // trace records. + IO.mapRequired("header", Trace.Header); + IO.mapRequired("records", Trace.Records); + } +}; + +} // namespace yaml +} // namespace llvm + +LLVM_YAML_IS_SEQUENCE_VECTOR(xray::YAMLXRayRecord) + +#endif // LLVM_TOOLS_LLVM_XRAY_XRAY_RECORD_YAML_H diff --git a/tools/llvm-xray/xray-record.h b/tools/llvm-xray/xray-record.h new file mode 100644 index 00000000000..4a794d1dcfa --- /dev/null +++ b/tools/llvm-xray/xray-record.h @@ -0,0 +1,55 @@ +//===- xray-record.h - XRay Trace Record ----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file replicates the record definition for XRay log entries. This should +// follow the evolution of the log record versions supported in the compiler-rt +// xray project. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_TOOLS_LLVM_XRAY_XRAY_RECORD_H +#define LLVM_TOOLS_LLVM_XRAY_XRAY_RECORD_H + +#include + +namespace llvm { +namespace xray { + +struct XRayFileHeader { + uint16_t Version = 0; + uint16_t Type = 0; + bool ConstantTSC; + bool NonstopTSC; + uint64_t CycleFrequency = 0; +}; + +enum class RecordTypes { ENTER, EXIT }; + +struct XRayRecord { + uint16_t RecordType; + + // The CPU where the thread is running. We assume number of CPUs <= 256. + uint8_t CPU; + + // Identifies the type of record. + RecordTypes Type; + + // The function ID for the record. + int32_t FuncId; + + // Get the full 8 bytes of the TSC when we get the log record. + uint64_t TSC; + + // The thread ID for the currently running thread. + uint32_t TId; +}; + +} // namespace xray +} // namespace llvm + +#endif // LLVM_TOOLS_LLVM_XRAY_XRAY_RECORD_H