Index: test/CMakeLists.txt =================================================================== --- test/CMakeLists.txt +++ test/CMakeLists.txt @@ -57,6 +57,7 @@ llvm-split llvm-symbolizer llvm-tblgen + llvm-xray not obj2yaml opt Index: test/tools/llvm-xray/X86/convert-roundtrip.yaml =================================================================== --- /dev/null +++ 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: ... Index: test/tools/llvm-xray/X86/extract-instrmap.ll =================================================================== --- /dev/null +++ test/tools/llvm-xray/X86/extract-instrmap.ll @@ -0,0 +1,34 @@ +; This test makes sure we can extract the instrumentation map from an +; XRay-instrumented object file. +; +; RUN: llc -filetype=obj -o %t -mtriple=x86_64-unknown-linux-gnu < %s +; RUN: llvm-xray extract -format=json %t | FileCheck %s --check-prefix=CHECK-JSON +; RUN: llvm-xray extract -format=yaml %t | FileCheck %s --check-prefix=CHECK-YAML + +define i32 @foo() nounwind noinline uwtable "function-instrument"="xray-always" { + ret i32 0 +} + +; CHECK-JSON: [ +; CHECK-JSON-NEXT: { +; CHECK-JSON-NEXT: "funcId": 1, +; CHECK-JSON-NEXT: "funcAddr": {{.*}}, +; CHECK-JSON-NEXT: "sledAddr": {{.*}}, +; CHECK-JSON-NEXT: "sledKind": "FunctionEnter", +; CHECK-JSON-NEXT: "alwaysInstrument": true +; CHECK-JSON-NEXT: }, +; CHECK-JSON-NEXT: { +; CHECK-JSON-NEXT: "funcId": 1, +; CHECK-JSON-NEXT: "funcAddr": {{.*}}, +; CHECK-JSON-NEXT: "sledAddr": {{.*}}, +; CHECK-JSON-NEXT: "sledKind": "FunctionExit", +; CHECK-JSON-NEXT: "alwaysInstrument": true +; CHECK-JSON-NEXT: } +; CHECK-JSON-NEXT: ] + +; CHECK-YAML: --- +; CHECK-YAML-NEXT: - { id: 1, address: 0x{{[0-9A-F]+}}, function: 0x{{[0-9A-F]+}}, kind: function-enter, +; CHECK-YAML-NEXT: always-instrument: true } +; CHECK-YAML-NEXT: - { id: 1, address: 0x{{[0-9A-F]+}}, function: 0x{{[0-9A-F]+}}, kind: function-exit, +; CHECK-YAML-NEXT: always-instrument: true } +; CHECK-YAML-NEXT: ... Index: test/tools/llvm-xray/X86/lit.local.cfg =================================================================== --- /dev/null +++ test/tools/llvm-xray/X86/lit.local.cfg @@ -0,0 +1 @@ +config.suffixes = ['.yaml', '.ll'] Index: tools/CMakeLists.txt =================================================================== --- tools/CMakeLists.txt +++ tools/CMakeLists.txt @@ -39,6 +39,7 @@ add_llvm_tool_subdirectory(llvm-config) add_llvm_tool_subdirectory(llvm-lto) add_llvm_tool_subdirectory(llvm-profdata) +add_llvm_tool_subdirectory(llvm-xray) # Projects supported via LLVM_EXTERNAL_*_SOURCE_DIR need to be explicitly # specified. Index: tools/llvm-xray/CMakeLists.txt =================================================================== --- /dev/null +++ tools/llvm-xray/CMakeLists.txt @@ -0,0 +1,17 @@ +set(LLVM_LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + Support + DebugInfoDWARF + Symbolize + Object) + +add_llvm_tool(xray-fc-account + xray-fc-account.cc) + +set(LLVM_XRAY_TOOLS + func-id-helper.cc + xray-converter.cc + xray-extract.cc + xray-account.cc + xray-log-reader.cc) +add_llvm_tool(llvm-xray llvm-xray.cc ${LLVM_XRAY_TOOLS}) Index: tools/llvm-xray/func-id-helper.h =================================================================== --- /dev/null +++ tools/llvm-xray/func-id-helper.h @@ -0,0 +1,51 @@ +//===- 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 FUNC_ID_HELPER_H_ +#define 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) {} + + FuncIdConversionHelper(const FuncIdConversionHelper &) = default; + + // 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 // FUNC_ID_HELPER_H_ Index: tools/llvm-xray/func-id-helper.cc =================================================================== --- /dev/null +++ tools/llvm-xray/func-id-helper.cc @@ -0,0 +1,56 @@ +//===- 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 +#include "func-id-helper.h" + +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(); + } + + auto ResOrErr = Symbolizer.symbolizeCode(BinaryInstrMap, It->second); + if (!ResOrErr || ResOrErr.get().FunctionName == "") { + F << "@(" << std::hex << It->second << ")"; + } else { + auto &DI = ResOrErr.get(); + F << DI.FunctionName; + } + 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 || ResOrErr.get().FileName == "") + return "(unknown)"; + auto &DI = ResOrErr.get(); + auto LastSlash = DI.FileName.find_last_of('/'); + if (LastSlash != std::string::npos) { + F << DI.FileName.substr(LastSlash + 1); + } else { + F << DI.FileName; + } + F << ":" << DI.Line << ":" << DI.Column; + return F.str(); +} Index: tools/llvm-xray/llvm-xray.cc =================================================================== --- /dev/null +++ tools/llvm-xray/llvm-xray.cc @@ -0,0 +1,337 @@ +//===- llvm-xray.cc - XRay Tool Main Program ------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the main entry point for the suite of XRay tools. All +// additional functionality are implemented as subcommands. +// +//===----------------------------------------------------------------------===// +// +// Basic usage: +// +// llvm-xray [options] [subcommand-specific options] +// + +#include "func-id-helper.h" +#include "xray-account.h" +#include "xray-converter.h" +#include "xray-extract.h" +#include "xray-log-reader.h" +#include "llvm/DebugInfo/Symbolize/Symbolize.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/raw_ostream.h" +#include + +using namespace llvm; + +// Define the subcommands we support. + +// llvm-xray extract +// ---------------------------------------------------------------------------- +static cl::SubCommand Extract("extract", "Extract instrumentation maps"); +static cl::opt ExtractInput(cl::Positional, + cl::desc(""), cl::Required, + cl::sub(Extract)); +static cl::opt + ExtractOutput("output", cl::value_desc("output file"), cl::init("-"), + cl::desc("output file; use '-' for stdout"), + cl::sub(Extract)); +static cl::alias ExtractOutput2("o", cl::aliasopt(ExtractOutput), + cl::desc("Alias for -output"), + cl::sub(Extract)); + +enum class ExtractOutputFormats { JSON, YAML }; +static cl::opt + ExtractFormat("format", cl::desc("output format"), + cl::values(clEnumValN(ExtractOutputFormats::JSON, "json", + "instrumentation map in json"), + clEnumValN(ExtractOutputFormats::YAML, "yaml", + "output instrumentation map in csv"), + clEnumValEnd), + cl::init(ExtractOutputFormats::YAML), cl::sub(Extract)); +static cl::alias ExtractFormat2("f", cl::desc("Alias for -format"), + cl::aliasopt(ExtractFormat), cl::sub(Extract)); + +// llvm-xray account +// ---------------------------------------------------------------------------- +static cl::SubCommand Account("account", "Function call accounting"); +static cl::opt AccountInput(cl::Positional, + cl::desc(""), + cl::Required, cl::sub(Account)); +static cl::opt + AccountKeepGoing("keep_going", cl::desc("Keep going on errors encountered"), + cl::sub(Account), cl::init(false)); +static cl::alias AccountKeepGoing2("k", cl::aliasopt(AccountKeepGoing), + cl::desc("Alias for -keep_going"), + cl::sub(Account)); +static cl::opt AccountDeduceSiblingCalls( + "deduce_sibling_calls", + cl::desc("Deduce sibling calls when unrolling function call stacks"), + cl::sub(Account), cl::init(false)); +static cl::alias + AccountDeduceSiblingCalls2("d", cl::aliasopt(AccountDeduceSiblingCalls), + cl::desc("Alias for -deduce_sibling_calls"), + cl::sub(Account)); +static cl::opt + AccountOutput("output", cl::value_desc("output file"), cl::init("-"), + cl::desc("output file; use '-' for stdout"), + cl::sub(Account)); +static cl::alias AccountOutput2("o", cl::aliasopt(AccountOutput), + cl::desc("Alias for -output"), + cl::sub(Account)); +enum AccountOutputFormats { TEXT, CSV }; +static cl::opt + AccountFormat("format", cl::desc("output format"), + cl::values(clEnumValN(AccountOutputFormats::TEXT, "text", + "report stats in text"), + clEnumValN(AccountOutputFormats::CSV, "csv", + "report stats in csv"), + clEnumValEnd), + cl::sub(Account)); +static cl::opt + AccountInstrMap("instr_map", + cl::desc("binary with the instrumentation map, or " + "a separate instrumentation map"), + cl::value_desc("binary with xray_instr_map"), + cl::sub(Account), cl::init("")); +static cl::alias AccountInstrMap2("m", cl::aliasopt(AccountInstrMap), + cl::desc("Alias for -instr_map"), + cl::sub(Account)); + +// llvm-xray dump +// ---------------------------------------------------------------------------- +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"), + clEnumValEnd), + 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"), + clEnumValEnd), + 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::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)); + +int main(int argc, char *argv[]) { + cl::ParseCommandLineOptions(argc, argv, + "XRay Tools\n\n" + " This program consolidates multiple XRay trace " + "processing tools for convenient access.\n"); + if (Extract) { + std::error_code EC; + xray::InstrumentationMapExtractor Extractor(ExtractInput, EC); + if (EC) { + errs() << "Cannot extract instrumentation map from '" << ExtractInput + << "'\n"; + return 1; + } + raw_fd_ostream OS(ExtractOutput, EC, sys::fs::OpenFlags::F_Text); + if (EC) { + errs() << "Cannot open file '" << ExtractOutput << "' for writing.\n"; + return 1; + } + switch (ExtractFormat) { + case ExtractOutputFormats::JSON: + Extractor.exportAsJSON(OS); + break; + case ExtractOutputFormats::YAML: + Extractor.exportAsYAML(OS); + break; + } + return 0; + } + + if (Account) { + int Fd; + auto EC = sys::fs::openFileForRead(AccountInput, Fd); + if (EC) { + errs() << "Cannot open file '" << AccountInput << "' for reading.\n"; + return 1; + } + raw_fd_ostream OS(AccountOutput, EC, sys::fs::OpenFlags::F_Text); + if (EC) { + errs() << "Cannot open file '" << AccountOutput << "' for writing.\n"; + return 1; + } + xray::InstrumentationMapExtractor Extractor(AccountInstrMap, EC); + if (EC) { + errs() << "Cannot load instrumentation map from '" << AccountInstrMap + << "'\n"; + if (!AccountKeepGoing) + return 1; + } + xray::NaiveLogReader LogReader(Fd, EC); + if (LogReader.getFileHeader().Version != 1) { + errs() << "Unsupported XRay file version: " + << LogReader.getFileHeader().Version << '\n'; + return 1; + } + 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); + for (const auto &Record : LogReader) { + if (!FCA.accountRecord(Record)) { + 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) { + errs() << "Failed accounting function calls in file '" << AccountInput + << "'."; + return 1; + } + } + } + switch (AccountFormat) { + case AccountOutputFormats::TEXT: + FCA.exportStatsAsTEXT(OS, LogReader.getFileHeader()); + break; + case AccountOutputFormats::CSV: + FCA.exportStatsAsCSV(OS, LogReader.getFileHeader()); + break; + } + return 0; + } + + if (Convert) { + if (ConvertInputFormat == ConvertOutputFormat) { + errs() << "-input-format and -output-format are the same, bailing out.\n"; + return 1; + } + int Fd; + auto EC = sys::fs::openFileForRead(ConvertInput, Fd); + if (EC) { + errs() << "Cannot open file '" << ConvertInput << "' for reading.\n"; + return 1; + } + xray::InstrumentationMapExtractor Extractor(ConvertInstrMap, EC); + 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); + switch (ConvertInputFormat) { + case ConvertFormats::BINARY: { + xray::NaiveLogReader LogReader(Fd, EC, ConvertSortInput); + if (EC) { + errs() << "Failed loading input file '" << ConvertInput << "'\n"; + return 1; + } + if (LogReader.getFileHeader().Version != 1) { + errs() << "Unsupported XRay file version: " + << LogReader.getFileHeader().Version << '\n'; + return 1; + } + raw_fd_ostream OS(ConvertOutput, EC, + ConvertOutputFormat == ConvertFormats::BINARY + ? sys::fs::OpenFlags::F_None + : sys::fs::OpenFlags::F_Text); + if (EC) { + errs() << "Cannot open file '" << ConvertOutput << "' for writing.\n"; + return 1; + } + switch (ConvertOutputFormat) { + case ConvertFormats::YAML: + TC.exportAsYAML(LogReader, OS); + return 0; + default: + errs() << "input-output conversion for provided formats not supported " + "yet.\n"; + return 1; + } + break; + } + case ConvertFormats::YAML: { + xray::YAMLLogReader LogReader(Fd, EC, ConvertSortInput); + if (EC) { + errs() << "Failed loading input file '" << ConvertInput << "'\n"; + return 1; + } + if (LogReader.getFileHeader().Version != 1) { + errs() << "Unsupported XRay file version: " + << LogReader.getFileHeader().Version << '\n'; + return 1; + } + int Fd; + sys::fs::openFileForWrite(ConvertOutput, Fd, sys::fs::OpenFlags::F_None); + if (EC) { + errs() << "Cannot open file '" << ConvertOutput << "' for writing.\n"; + return 1; + } + switch (ConvertOutputFormat) { + case ConvertFormats::BINARY: + TC.exportAsRAW(LogReader, Fd); + close(Fd); + return 0; + default: + errs() << "input-output conversion for provided formats not supported " + "yet.\n"; + close(Fd); + return 1; + } + break; + } + } + } + + // We need a subcommand to do anything useful. At this point show usage. + cl::PrintHelpMessage(false, true); +} Index: tools/llvm-xray/xray-account.h =================================================================== --- /dev/null +++ tools/llvm-xray/xray-account.h @@ -0,0 +1,104 @@ +//===- xray-account.h - XRay Function Call Accounting ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the interface for performing some basic function call +// accounting from an XRay trace. +// +//===----------------------------------------------------------------------===// +#ifndef XRAY_ACCOUNT_H_ +#define XRAY_ACCOUNT_H_ + +#include +#include +#include + +#include "func-id-helper.h" +#include "xray-record.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { +namespace xray { + +class LatencyAccountant { +public: + typedef std::map> FunctionLatencyMap; + typedef std::map> PerThreadMinMaxTSCMap; + typedef std::map> PerCPUMinMaxTSCMap; + typedef std::vector> FunctionStack; + typedef std::map PerThreadFunctionStackMap; + +private: + PerThreadFunctionStackMap PerThreadFunctionStack; + FunctionLatencyMap FunctionLatencies; + PerThreadMinMaxTSCMap PerThreadMinMaxTSC; + PerCPUMinMaxTSCMap PerCPUMinMaxTSC; + FuncIdConversionHelper &FuncIdHelper; + + bool DeduceSiblingCalls = false; + uint64_t CurrentMaxTSC = 0; + + void recordLatency(int32_t FuncId, uint64_t Latency) { + FunctionLatencies[FuncId].push_back(Latency); + } + +public: + explicit LatencyAccountant(FuncIdConversionHelper &FuncIdHelper, + bool DeduceSiblingCalls) + : FuncIdHelper(FuncIdHelper), DeduceSiblingCalls(DeduceSiblingCalls) {} + + const FunctionLatencyMap &getFunctionLatencies() const { + return FunctionLatencies; + } + + const PerThreadMinMaxTSCMap &getPerThreadMinMaxTSC() const { + return PerThreadMinMaxTSC; + } + + const PerCPUMinMaxTSCMap &getPerCPUMinMaxTSC() const { + return PerCPUMinMaxTSC; + } + + /// Returns false in case we fail to account the provided record. This happens + /// in the following cases: + /// + /// - An exit record does not match any entry records for the same function. + /// If we've been set to deduce sibling calls, we try walking up the stack + /// and recording times for the higher level functions. + /// - A record has a TSC that's before the latest TSC that has been + /// recorded. We still record the TSC for the min-max. + /// + bool accountRecord(const XRayRecord &Record); + + const FunctionStack *getThreadFunctionStack(pid_t TId) const { + auto I = PerThreadFunctionStack.find(TId); + if (I == PerThreadFunctionStack.end()) + return nullptr; + return &I->second; + } + + const PerThreadFunctionStackMap &getPerThreadFunctionStack() const { + return PerThreadFunctionStack; + } + + // Output Functions + // ================ + + void exportStatsAsTEXT(raw_ostream &OS, const XRayFileHeader &Header) const; + void exportStatsAsCSV(raw_ostream &OS, const XRayFileHeader &Header) const; + +private: + // Internal helper to implement common parts of the exportStatsAs... + // functions. + template void exportStats(const XRayFileHeader &Header, F fn) const; +}; + +} // namespace xray +} // namespace llvm + +#endif // XRAY_ACCOUNT_H_ Index: tools/llvm-xray/xray-account.cc =================================================================== --- /dev/null +++ tools/llvm-xray/xray-account.cc @@ -0,0 +1,169 @@ +//===- xray-account.h - XRay Function Call Accounting ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements basic function call accounting from an XRay trace. +// +//===----------------------------------------------------------------------===// + +#include +#include + +#include "xray-account.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace llvm; +using namespace llvm::xray; + +namespace { + +template void setMinMax(std::pair &MM, U &&V) { + if (MM.first == 0 || MM.second == 0) + MM = std::make_pair(std::forward(V), std::forward(V)); + else + MM = std::make_pair(std::min(MM.first, V), std::max(MM.second, V)); +} + +template T diff(T L, T R) { return std::max(L, R) - std::min(L, R); } + +} // namespace + +bool LatencyAccountant::accountRecord(const XRayRecord &Record) { + setMinMax(PerThreadMinMaxTSC[Record.TId], Record.TSC); + setMinMax(PerCPUMinMaxTSC[Record.CPU], Record.TSC); + + if (CurrentMaxTSC == 0) + CurrentMaxTSC = Record.TSC; + + if (Record.TSC < CurrentMaxTSC) + return false; + + auto &ThreadStack = PerThreadFunctionStack[Record.TId]; + switch (Record.Type) { + case 0: { + // Function Enter + ThreadStack.push_back({Record.FuncId, Record.TSC}); + break; + } + case 1: { + // Function Exit + if (ThreadStack.back().first != Record.FuncId) { + if (!DeduceSiblingCalls) + return false; + auto Parent = + std::find_if(ThreadStack.rbegin(), ThreadStack.rend(), + [&](const std::pair &E) { + return E.first == Record.FuncId; + }); + if (Parent != ThreadStack.rend()) { + // We found a matching entry for this function. + auto I = ThreadStack.rbegin(); + while (I <= Parent) { + const auto &Top = *I; + recordLatency(Record.FuncId, diff(Top.second, Record.TSC)); + ThreadStack.pop_back(); + I = ThreadStack.rbegin(); + } + return true; + } + + // We didn't find a matching entry for this exit + return false; + } + + // We did find that the top of the function call stack matches this exit. + assert(ThreadStack.back().first == Record.FuncId); + const auto &Top = ThreadStack.back(); + recordLatency(Record.FuncId, diff(Top.second, Record.TSC)); + ThreadStack.pop_back(); + break; + } + default: + llvm_unreachable("Unsupported record type."); + return false; + } + + return true; +} + +namespace { + +// We consolidate the data into a struct which we can output in various forms. +struct ResultRow { + uint64_t Count; + double Min; + double Median; + double Pct90; + double Pct99; + double Max; + std::string DebugInfo; + std::string Function; +}; + +ResultRow getStats(std::vector &Timings) { + ResultRow R; + std::nth_element(Timings.begin(), Timings.end(), Timings.end()); + R.Min = Timings.front(); + auto MedianOff = Timings.size() / 2; + std::nth_element(Timings.begin(), Timings.begin() + MedianOff, Timings.end()); + R.Median = Timings[MedianOff]; + auto Pct90Off = std::floor(Timings.size() * 0.9); + std::nth_element(Timings.begin(), Timings.begin() + Pct90Off, Timings.end()); + R.Pct90 = Timings[Pct90Off]; + auto Pct99Off = std::floor(Timings.size() * 0.99); + std::nth_element(Timings.begin(), Timings.begin() + Pct90Off, Timings.end()); + R.Pct99 = Timings[Pct99Off]; + R.Max = *std::max_element(Timings.begin(), Timings.end()); + R.Count = Timings.size(); + return R; +} + +} // namespace + +template +void LatencyAccountant::exportStats(const XRayFileHeader &Header, F Fn) const { + for (auto FT : FunctionLatencies) { + const auto &FuncId = FT.first; + auto &Timings = FT.second; + ResultRow Row = getStats(Timings); + if (Header.CycleFrequency) { + double CycleFrequency = Header.CycleFrequency; + Row.Min /= CycleFrequency; + Row.Median /= CycleFrequency; + Row.Pct90 /= CycleFrequency; + Row.Pct99 /= CycleFrequency; + Row.Max /= CycleFrequency; + } + + Row.Function = FuncIdHelper.SymbolOrNumber(FuncId); + Row.DebugInfo = FuncIdHelper.FileLineAndColumn(FuncId); + Fn(FuncId, Timings.size(), Row); + } +} + +void LatencyAccountant::exportStatsAsTEXT(raw_ostream &OS, + const XRayFileHeader &Header) const { + OS << "Functions with latencies: " << FunctionLatencies.size() << "\n"; + OS << "funcid\t\tcount\t\t[min, median, 90%ile, 99%ile, " + "max]\tdebug\t\tfunction\n"; + exportStats(Header, [&](int32_t FuncId, size_t Count, const ResultRow &Row) { + OS << FuncId << "\t\t" << Count << "\t\t[" << Row.Min << ", " << Row.Median + << ", " << Row.Pct90 << ", " << Row.Pct99 << ", " << Row.Max << "] " + << Row.DebugInfo << " " << Row.Function << "\n"; + }); +} + +void LatencyAccountant::exportStatsAsCSV(raw_ostream &OS, + const XRayFileHeader &Header) const { + OS << "funcid,count,min,median,90%ile,99%ile,max,debug,function\n"; + exportStats(Header, [&](int32_t FuncId, size_t Count, const ResultRow &Row) { + OS << FuncId << ',' << Count << ',' << Row.Min << ',' << Row.Median << ',' + << Row.Pct90 << ',' << Row.Pct99 << ',' << Row.Max << ",\"" + << Row.DebugInfo << "\",\"" << Row.Function << "\"\n"; + }); +} Index: tools/llvm-xray/xray-converter.h =================================================================== --- /dev/null +++ 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 XRAY_CONVERTER_H_ +#define 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 NaiveLogReader &Records, raw_ostream &OS); + void exportAsRAW(const YAMLLogReader &Records, int Fd); +}; + +} // namespace xray +} // namespace llvm + +#endif // XRAY_CONVERTER_H_ Index: tools/llvm-xray/xray-converter.cc =================================================================== --- /dev/null +++ tools/llvm-xray/xray-converter.cc @@ -0,0 +1,94 @@ +//===- 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-record-yaml.h" +#include "llvm/Support/YAMLTraits.h" +#include + +using namespace llvm; +using namespace xray; + +using llvm::yaml::MappingTraits; +using llvm::yaml::ScalarEnumerationTraits; +using llvm::yaml::IO; +using llvm::yaml::Output; + +void TraceConverter::exportAsYAML(const NaiveLogReader &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 == 0 ? RecordTypes::ENTER : RecordTypes::EXIT, R.FuncId, + Symbolize ? FuncIdHelper.SymbolOrNumber(R.FuncId) + : std::to_string(R.FuncId), + R.TSC, R.TId}); + } + Output Out(OS); + Out << Trace; +} + +namespace { + +void retryingWriteAll(int Fd, const char *Begin, const char *End) { + if (Begin == End) + return; + auto TotalBytes = std::distance(Begin, End); + while (auto Written = write(Fd, Begin, TotalBytes)) { + if (Written < 0) { + if (errno == EINTR) + continue; + return; + } + TotalBytes -= Written; + if (TotalBytes == 0) + break; + Begin += Written; + } + return; +} + +} // namespace + +void TraceConverter::exportAsRAW(const YAMLLogReader &Records, int Fd) { + retryingWriteAll(Fd, reinterpret_cast(&Records.getFileHeader()), + reinterpret_cast(&Records.getFileHeader()) + + sizeof(XRayFileHeader)); + + // Now we write each record as binary. + std::aligned_storage::type Buffer[1]; + for (const auto &Y : Records) { + XRayRecord &R = *reinterpret_cast(&Buffer); + R.RecordType = Y.RecordType; + R.CPU = Y.CPU; + switch (Y.Type) { + case RecordTypes::ENTER: + R.Type = 0; + break; + case RecordTypes::EXIT: + R.Type = 1; + break; + } + R.FuncId = Y.FuncId; + R.TSC = Y.TSC; + R.TId = Y.TId; + memset(R.Buffer, 0, sizeof(R.Buffer)); + retryingWriteAll(Fd, reinterpret_cast(Buffer), + reinterpret_cast(Buffer) + + sizeof(XRayRecord)); + } +} Index: tools/llvm-xray/xray-extract.h =================================================================== --- /dev/null +++ tools/llvm-xray/xray-extract.h @@ -0,0 +1,58 @@ +//===- xray-extract.h - XRay Instrumentation Map Extraction ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Defines the interface for extracting the instrumentation map from an +// XRay-instrumented binary. +// +//===----------------------------------------------------------------------===// + +#ifndef XRAY_EXTRACT_H_ +#define XRAY_EXTRACT_H_ + +#include +#include +#include +#include + +#include "xray-sleds.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { +namespace xray { + +class InstrumentationMapExtractor { +public: + typedef std::unordered_map FunctionAddressMap; + typedef std::unordered_map FunctionAddressReverseMap; + +private: + std::string Filename; + std::deque Sleds; + FunctionAddressMap FunctionAddresses; + FunctionAddressReverseMap FunctionIds; + +public: + /// Loads the instrumentation map from |Filename|. Updates |EC| in case there + /// were errors encountered opening the file. + explicit InstrumentationMapExtractor(std::string Filename, + std::error_code &EC); + + const FunctionAddressMap &getFunctionAddresses() { return FunctionAddresses; } + + /// Exports the loaded function address map as JSON through |OS|. + void exportAsJSON(raw_ostream &OS); + + /// Exports the loaded function address map as YAML through |OS|. + void exportAsYAML(raw_ostream &OS); +}; + +} // namespace xray +} // namespace llvm + +#endif // XRAY_EXTRACT_H_ Index: tools/llvm-xray/xray-extract.cc =================================================================== --- /dev/null +++ tools/llvm-xray/xray-extract.cc @@ -0,0 +1,211 @@ +//===- xray-extract.cc - XRay Instrumentation Map Extraction --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implementation of the xray-extract.h interface. +// +// FIXME: Support other XRay-instrumented binary formats other than ELF. +// +//===----------------------------------------------------------------------===// + +#include +#include + +#include "xray-extract.h" + +#include "xray-sleds.h" +#include "llvm/Object/ELF.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ELF.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/YAMLTraits.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace llvm::xray; + +namespace { + +// Returns {"message", false} on errors; {"", true} otherwise. +std::pair LoadBinaryInstrELF( + StringRef Filename, std::deque &Sleds, + InstrumentationMapExtractor::FunctionAddressMap &InstrMap, + InstrumentationMapExtractor::FunctionAddressReverseMap &FunctionIds) { + if (Filename.empty()) + llvm_unreachable("Provided Filename is empty."); + + auto ObjectFile = object::ObjectFile::createObjectFile(Filename); + + // FIXME: Maybe support other ELF formats. For now, 64-bit Little Endian only. + if (!ObjectFile || !ObjectFile->getBinary()->isELF()) + return std::make_pair("File format not supported (only does ELF).", + 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 = llvm::find_if(Sections, [&](const object::SectionRef &Section) { + StringRef Name = ""; + if (Section.getName(Name)) + return false; + return Name == "xray_instr_map"; + }); + if (I == Sections.end()) + return std::make_pair("Failed to find XRay instrumentation map.", + std::make_error_code(std::errc::not_supported)); + if (I->getContents(Contents)) + return std::make_pair( + "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(); + if (std::distance(C, Contents.bytes_end()) % sizeof(XRaySledEntry) != 0) + return std::make_pair( + "Instrumentation map entries not evenly divisible by " + "size of XRaySledEntry", + std::make_error_code(std::errc::executable_format_error)); + while (C != Contents.bytes_end()) { + // Because we have a 'const char*' we need to assume that it is not aligned. + // This means we're going to have to memcpy it into properly-aligned memory + // first, then copy that into the Sleds data structure. + std::aligned_storage::type AlignedSled; + memcpy(&AlignedSled, C, sizeof(XRaySledEntry)); + Sleds.push_back(*reinterpret_cast(&AlignedSled)); + C += sizeof(XRaySledEntry); + } + + // 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. + int32_t FuncId = 1; + uint64_t CurFn = 0; + for (const auto &Sled : Sleds) { + auto F = Sled.Function; + if (CurFn == 0) { + CurFn = F; + InstrMap[FuncId] = F; + FunctionIds[F] = FuncId; + } + if (F != CurFn) { + ++FuncId; + CurFn = F; + InstrMap[FuncId] = F; + FunctionIds[F] = FuncId; + } + } + return std::make_pair("", std::error_code()); +} + +} // namespace + +InstrumentationMapExtractor::InstrumentationMapExtractor(std::string Filename, + std::error_code &EC) + : Filename(std::move(Filename)) { + // FIXME: Report the error somehow. + std::tie(std::ignore, EC) = + LoadBinaryInstrELF(this->Filename, Sleds, FunctionAddresses, FunctionIds); +} + +void InstrumentationMapExtractor::exportAsJSON(raw_ostream &OS) { + // JSON Output for the instrumentation map should look like the following: + // + // [ + // , + // , + // ... + // + // ] + // + // Each of the elements turn into: + // + // { + // "funcId": , + // "funcAdd": , + // "sledKind": , + // "alwaysInstrument": + // } + OS << "[\n"; + auto Countdown = Sleds.size(); + for (const auto &Sled : Sleds) { + OS << " {\n \"funcId\": " << FunctionIds[Sled.Function] + << ",\n \"funcAddr\": \"" << format_hex(Sled.Function, 16) + << "\",\n \"sledAddr\": \"" << format_hex(Sled.Address, 16) + << "\",\n \"sledKind\": \""; + switch (Sled.Kind) { + case '\0': + OS << "FunctionEnter"; + break; + case '\1': + OS << "FunctionExit"; + break; + default: + OS << "Unknown"; + break; + } + OS << "\",\n \"alwaysInstrument\": " + << (Sled.AlwaysInstrument ? "true" : "false") << "\n }"; + if (--Countdown != 0) + OS << ","; + OS << "\n"; + } + OS << "]\n"; +} + +using ::llvm::yaml::MappingTraits; +using ::llvm::yaml::ScalarEnumerationTraits; +using ::llvm::yaml::IO; +using ::llvm::yaml::Hex64; +using ::llvm::yaml::Output; + +enum class FunctionKinds { ENTRY, EXIT }; + +struct YAMLXRaySledEntry { + int32_t FuncId; + Hex64 Address; + Hex64 Function; + FunctionKinds Kind; + bool AlwaysWinstrument; +}; + +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &IO, FunctionKinds &Kind) { + IO.enumCase(Kind, "function-enter", FunctionKinds::ENTRY); + IO.enumCase(Kind, "function-exit", FunctionKinds::EXIT); + } +}; + +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.AlwaysWinstrument); + } + + static constexpr bool flow = true; +}; + +LLVM_YAML_IS_SEQUENCE_VECTOR(YAMLXRaySledEntry); + +void InstrumentationMapExtractor::exportAsYAML(raw_ostream &OS) { + // First we translate the sleds into the YAMLXRaySledEntry objects in a deque. + std::vector YAMLSleds; + YAMLSleds.reserve(Sleds.size()); + for (const auto &Sled : Sleds) { + YAMLSleds.push_back( + {FunctionIds[Sled.Function], Sled.Address, Sled.Function, + Sled.Kind == '\0' ? FunctionKinds::ENTRY : FunctionKinds::EXIT, + Sled.AlwaysInstrument ? true : false}); + } + Output Out(OS); + Out << YAMLSleds; +} Index: tools/llvm-xray/xray-fc-account.cc =================================================================== --- /dev/null +++ tools/llvm-xray/xray-fc-account.cc @@ -0,0 +1,544 @@ +//===- 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. +// +//===----------------------------------------------------------------------===// +// +// This file implements the Function Call Accounting tool that takes an XRay +// trace and generates function call accounting statistics for that given trace. +// +//===----------------------------------------------------------------------===// +// +// Usage: +// +// xray-fc-account [-instr_map=] [-verbose] [-keep_going] +// [-format=csv/text] +// +// +// This tool computes some useful statistics regarding function calls present +// from an XRay trace file. The kinds of statistics this generates fall into the +// following categories: +// +// - Basic latency statistics (count, minimum, median, 90th percentile, 99th +// percentile, maximum). +// - Per-thread timestamp accounting (minimum and maximum timestamps per +// thread) +// - Per-CPU timestamp accounting (minimum and maximum timestamps per CPU) +// +// In '-verbose' mode, we also reconstruct the stack traces encountered from the +// input trace file showing entries of the following form: +// +// [,cpu=] Entry/Exit +// + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "llvm/DebugInfo/Symbolize/Symbolize.h" +#include "llvm/Object/ELF.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ELF.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +static StringRef ToolName; + +static cl::opt Input(cl::Positional, cl::desc(""), + cl::Required); + +static cl::opt BinaryInstrMap( + "instr_map", + cl::desc("file containing an xray instrumentation map section"), + cl::value_desc("filename"), cl::Optional); +static cl::alias BinaryInstrMap2("m", cl::desc("Alias for -instr_map"), + cl::aliasopt(BinaryInstrMap)); +static cl::opt Verbose( + "verbose", + cl::desc( + "play-by-play logging of encountered records, useful for debugging"), + cl::init(false)); +static cl::alias Verbose2("v", cl::desc("Alias for -verbose"), + cl::aliasopt(Verbose)); + +static cl::opt + KeepGoing("keep_going", + cl::desc("print errors and continue processing log"), + cl::init(false)); +static cl::alias KeepGoing2("k", cl::desc("Alias for -keep_going"), + cl::aliasopt(KeepGoing)); + +enum OutputFormats { TEXT, CSV }; +static cl::opt OutputFormat( + "format", cl::desc("output format"), + cl::values(clEnumValN(OutputFormats::TEXT, "text", "text summary output"), + clEnumValN(OutputFormats::CSV, "csv", "output data in csv"), + clEnumValEnd), + cl::init(OutputFormats::TEXT)); + +LLVM_ATTRIBUTE_NORETURN static void fail(Twine Error) { + outs() << ToolName << ": " << Error << ".\n"; + exit(1); +} + +class FileCloser { + int Fd; + +public: + explicit FileCloser(int Fd) : Fd(Fd) {} + + ~FileCloser() { + if (Fd != -1) + close(Fd); + } +}; + +namespace __xray { +// FIXME: Make these available in a common header between compiler-rt and this +// tool? + +struct alignas(32) XRayFileHeader { + uint16_t Version = 0; + uint16_t Type = 0; + bool ConstantTSC : 1; + bool NonstopTSC : 1; + uint64_t CycleFrequency = 0; + char Padding[14] = {}; +} __attribute__((packed)); + +struct alignas(32) XRayRecord { + uint16_t RecordType = 0; + // The CPU where the thread is running. We assume number of CPUs <= 256. + uint8_t CPU = 0; + + // The type of the event. Usually either ENTER = 0 or EXIT = 1. + uint8_t Type = 0; + + // The function ID for the record. + int32_t FuncId = 0; + + // Get the full 8 bytes of the TSC when we get the log record. + uint64_t TSC = 0; + + // The thread ID for the currently running thread. + uint32_t TId = 0; + + // Use some bytes in the end of the record for buffers. + char Buffer[4] = {}; +} __attribute__((packed)); + +struct XRaySledEntry { + uint64_t Address; + uint64_t Function; + unsigned char Kind; + unsigned char AlwaysInstrument; + unsigned char Padding[14]; +}; +} + +typedef std::unordered_map FunctionAddressMap; + +using namespace __xray; + +FunctionAddressMap LoadBinaryInstr() { + FunctionAddressMap InstrMap; + if (BinaryInstrMap.empty()) { + errs() << ToolName << ": instrumentation map not provided, using raw " + "function IDs in output.\n"; + return InstrMap; + } + + auto ObjectFile = object::ObjectFile::createObjectFile(BinaryInstrMap); + + // FIXME: Maybe support other ELF formats. For now, 64-bit Little Endian only. + if (!ObjectFile || !ObjectFile->getBinary()->isELF()) + fail("File format not supported (only does ELF)."); + + // Find the section named "xray_instr_map". + StringRef Contents = ""; + const auto &Sections = ObjectFile->getBinary()->sections(); + auto I = llvm::find_if(Sections, [&](const object::SectionRef &Section) { + StringRef Name = ""; + if (Section.getName(Name)) + return false; + return Name == "xray_instr_map"; + }); + if (I == Sections.end()) + fail("Failed to find XRay instrumentation map."); + if (I->getContents(Contents)) + fail("Failed to get contents of 'xray_instr_map' section."); + + // Treat Contents as though it were an array of XRaySledEntry elements. + ArrayRef Sleds( + reinterpret_cast(Contents.data()), + Contents.size() / sizeof(XRaySledEntry)); + + // 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. + int32_t FuncId = 1; + uint64_t CurFn = 0; + for (const auto &Sled : Sleds) { + auto F = Sled.Function; + if (CurFn == 0) { + CurFn = F; + InstrMap[FuncId] = F; + } + if (F != CurFn) { + ++FuncId; + CurFn = F; + InstrMap[FuncId] = F; + } + } + return InstrMap; +} + +// This class consolidates common operations related to Function IDs. +class FuncIdConversionHelper { + symbolize::LLVMSymbolizer &Symbolizer; + const FunctionAddressMap &FunctionAddresses; + +public: + FuncIdConversionHelper(symbolize::LLVMSymbolizer &Symbolizer, + const FunctionAddressMap &FunctionAddresses) + : Symbolizer(Symbolizer), FunctionAddresses(FunctionAddresses) {} + + FuncIdConversionHelper(const FuncIdConversionHelper &) = default; + + std::string SymbolOrNumber(int32_t FuncId) const { + std::ostringstream F; + auto It = FunctionAddresses.find(FuncId); + if (It == FunctionAddresses.end()) { + F << "#" << FuncId; + return F.str(); + } + + auto ResOrErr = Symbolizer.symbolizeCode(BinaryInstrMap, It->second); + if (!ResOrErr || ResOrErr.get().FunctionName == "") { + F << "@(" << std::hex << It->second << ")"; + } else { + auto &DI = ResOrErr.get(); + F << DI.FunctionName; + } + return F.str(); + } + + std::string 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 || ResOrErr.get().FileName == "") + return "(unknown)"; + auto &DI = ResOrErr.get(); + auto LastSlash = DI.FileName.find_last_of('/'); + if (LastSlash != std::string::npos) { + F << DI.FileName.substr(LastSlash + 1); + } else { + F << DI.FileName; + } + F << ":" << DI.Line << ":" << DI.Column; + return F.str(); + } +}; + +// We consolidate the data into a struct which we can output in various forms. +struct ResultRow { + uint64_t Count; + double Min; + double Median; + double Pct90; + double Pct99; + double Max; + std::string DebugInfo; + std::string Function; +}; + +void PrintStatsCSV(const ResultRow &Row) { + outs() << Row.Count << ',' << Row.Min << ',' << Row.Median << ',' << Row.Pct90 + << ',' << Row.Pct99 << ',' << Row.Max << ",\"" << Row.DebugInfo + << "\"," << '"' << Row.Function << "\"\n"; +} + +void PrintStatsTEXT(const ResultRow &Row) { + outs() << Row.Count << "\t" + << "[" << Row.Min << ", " << Row.Median << ", " << Row.Pct90 << ", " + << Row.Pct99 << ", " << Row.Max << "] " << Row.DebugInfo << "\t" + << Row.Function << "\n"; +} + +void PrintStatsHeader(OutputFormats OF) { + if (OF == OutputFormats::TEXT) + outs() << "count\t[min,median,90%ile,99%ile,max]\tdebug\tfunction\n"; + if (OF == OutputFormats::CSV) + outs() << "count,min,median,90%ile,99%ile,max,debuginfo,function\n"; +} + +int main(int argc, char *argv[]) { + ToolName = argv[0]; + cl::ParseCommandLineOptions( + argc, argv, "XRay Function Call Accounting (xray-fc-account)\n\n" + " This program takes an XRay trace file and does some basic " + "function call accounting statistics.\n"); + + if (Input.empty()) + fail("No input file provided."); + + FunctionAddressMap FunctionAddresses = LoadBinaryInstr(); + if (OutputFormat == OutputFormats::TEXT) + outs() << "Loaded " << FunctionAddresses.size() + << " entries for the instrumentation map.\n"; + + int Fd; + do { + Fd = open(Input.c_str(), O_RDONLY); + } while (Fd == -1 && errno == EINTR); + if (Fd == -1) + fail(Twine("Failed to open file '") + Input + "'; errno=" + Twine(errno)); + FileCloser FC(Fd); + symbolize::LLVMSymbolizer::Options Opts( + symbolize::FunctionNameKind::LinkageName, true, true, false, ""); + symbolize::LLVMSymbolizer Symbolizer(Opts); + XRayFileHeader Header; + int ReadBytes = 0; + while ((ReadBytes = read(Fd, &Header, sizeof(XRayFileHeader))) == -1 && + errno == EINTR) { + // Do nothing. + } + if (ReadBytes == 0 || ReadBytes == -1) + fail(Twine("Failed to read file '") + Input + "'; " + Twine(errno)); + + XRayRecord Record; + std::map>> FunctionStack; + std::map> FunctionTimings; + std::map> ThreadMinMaxTSC; + std::map> CPUMinMaxTSC; + uint64_t CurTSC = 0; + FuncIdConversionHelper H(Symbolizer, FunctionAddresses); + while (auto ReadBytes = read(Fd, &Record, sizeof(XRayRecord))) { + if (ReadBytes == -1) { + if (errno == EINTR) + continue; + fail(Twine("Failed to read file '") + Input + "'; errno=" + Twine(errno)); + } + + if (CurTSC == 0) + CurTSC = Record.TSC; + + auto &ThreadFuncStack = FunctionStack[Record.TId]; + if (Verbose) { + outs() << "[tid=" << Record.TId << ",cpu=" << int32_t{Record.CPU} << "] " + << Record.TSC << ": " << (Record.Type == 0 ? "E" : "X") + << Record.FuncId << "@("; + auto FuncAddr = FunctionAddresses.find(Record.FuncId); + if (FuncAddr == FunctionAddresses.end()) { + outs() << "unknown"; + } else { + outs().write_hex(FuncAddr->second); + } + outs() << ") "; + + // Let's indent according to stack depth for this thread. For Entry, we + // just add the correct number of indents. For Exit, we reduce it by 1. + for (size_t i = 0 + Record.Type; i < ThreadFuncStack.size(); i++) { + outs() << " "; + } + outs() << H.SymbolOrNumber(Record.FuncId) << "\n"; + } + + // Record Thread min/max TSC's. + { + auto &MinMaxTSC = ThreadMinMaxTSC[Record.TId]; + if (MinMaxTSC.first == 0 || MinMaxTSC.second == 0) + MinMaxTSC = std::make_pair(Record.TSC, Record.TSC); + MinMaxTSC = std::make_pair(std::min(MinMaxTSC.first, Record.TSC), + std::max(MinMaxTSC.second, Record.TSC)); + } + + // Record CPU min/max TSC's. + { + auto &MinMaxTSC = CPUMinMaxTSC[Record.CPU]; + if (MinMaxTSC.first == 0 || MinMaxTSC.second == 0) + MinMaxTSC = std::make_pair(Record.TSC, Record.TSC); + MinMaxTSC = std::make_pair(std::min(MinMaxTSC.first, Record.TSC), + std::max(MinMaxTSC.second, Record.TSC)); + } + + if (Record.TSC < CurTSC) { + errs() << "Record TSC (" << Record.TSC << ") < Latest TSC (" << CurTSC + << ")\n"; + if (!KeepGoing) + fail("Out-of-order TSC records found."); + } + + if (Record.Type == 0) + FunctionStack[Record.TId].push_back({Record.FuncId, Record.TSC}); + + if (Record.Type == 1) { + if (FunctionStack[Record.TId].back().first != Record.FuncId) { + // This could be a tail call, so look up the stack to see whether we've + // found an entry that shouldn't be there. + auto Parent = + std::find_if(ThreadFuncStack.rbegin(), ThreadFuncStack.rend(), + [&](const std::pair &Entry) { + return Entry.first == Record.FuncId; + }); + if (Parent != ThreadFuncStack.rend()) { + auto It = ThreadFuncStack.rbegin(); + while (It <= Parent) { + const auto &Top = *It; + int64_t Timing = std::max(Top.second, Record.TSC) - + std::min(Top.second, Record.TSC); + FunctionTimings[Top.first].push_back(Timing); + ThreadFuncStack.pop_back(); + It = ThreadFuncStack.rbegin(); + } + } else { + errs() << ToolName << ": Encountered X" << Record.FuncId << "(" + << H.SymbolOrNumber(Record.FuncId) << ") not matching E" + << ThreadFuncStack.back().first << "(" + << H.SymbolOrNumber(ThreadFuncStack.back().first) << ")\n"; + errs() << "Current stack dump:\n"; + for (auto &ThreadStack : FunctionStack) { + errs() << "Thread ID: " << ThreadStack.first << "\n"; + auto Level = ThreadStack.second.size(); + for (const auto &Entry : llvm::reverse(ThreadStack.second)) { + errs() << "#" << Level-- << "\t" << H.SymbolOrNumber(Entry.first) + << '\n'; + } + } + if (!KeepGoing) + fail("Corrupted input file found."); + } + } else { + const auto &Top = FunctionStack[Record.TId].back(); + int64_t Timing = + std::max(Top.second, Record.TSC) - std::min(Top.second, Record.TSC); + auto &Timings = FunctionTimings[Record.FuncId]; + Timings.push_back(Timing); + ThreadFuncStack.pop_back(); + } + } + CurTSC = Record.TSC; + } + + bool HasCyclesPerSec = Header.CycleFrequency != 0; + auto CyclesPerSec = static_cast(Header.CycleFrequency); + + if (OutputFormat == OutputFormats::TEXT && HasCyclesPerSec) + outs() << "Cycle counter frequency = " << Header.CycleFrequency << "hz\n" + << "Durations below are in seconds.\n"; + + if (!FunctionTimings.empty()) { + if (OutputFormat == OutputFormats::TEXT) + outs() << "\nTotal functions with timings: " << FunctionTimings.size() + << '\n'; + PrintStatsHeader(OutputFormat); + for (auto &FT : FunctionTimings) { + const auto &FuncId = FT.first; + auto &Timings = FT.second; + std::nth_element(Timings.begin(), Timings.begin(), Timings.end()); + auto Min = Timings.front(); + auto MedianOff = Timings.size() / 2; + std::nth_element(Timings.begin(), Timings.begin() + MedianOff, + Timings.end()); + auto Median = Timings[MedianOff]; + auto Pct90Off = std::floor(Timings.size() * 0.9); + std::nth_element(Timings.begin(), Timings.begin() + Pct90Off, + Timings.end()); + auto Pct90 = Timings[Pct90Off]; + auto Pct99Off = std::floor(Timings.size() * 0.99); + std::nth_element(Timings.begin(), Timings.begin() + Pct99Off, + Timings.end()); + auto Pct99 = Timings[Pct99Off]; + std::nth_element(Timings.begin(), Timings.begin() + Timings.size(), + Timings.end()); + auto Max = Timings.back(); + ResultRow Row; + Row.Count = Timings.size(); + Row.Min = Min; + Row.Median = Median; + Row.Pct90 = Pct90; + Row.Pct99 = Pct99; + Row.Max = Max; + if (HasCyclesPerSec) { + Row.Min /= CyclesPerSec; + Row.Median /= CyclesPerSec; + Row.Pct90 /= CyclesPerSec; + Row.Pct99 /= CyclesPerSec; + Row.Max /= CyclesPerSec; + } + Row.DebugInfo = H.FileLineAndColumn(FuncId); + Row.Function = H.SymbolOrNumber(FuncId); + switch (OutputFormat) { + case OutputFormats::TEXT: + PrintStatsTEXT(Row); + break; + case OutputFormats::CSV: + PrintStatsCSV(Row); + break; + }; + } + } + + // Print out thread total durations. + if (!ThreadMinMaxTSC.empty()) { + if (OutputFormat == OutputFormats::TEXT) + outs() << "\nTotal threads: " << ThreadMinMaxTSC.size() + << "\nTID\t(min,max)\tduration in secs\n"; + if (OutputFormat == OutputFormats::CSV) + outs() << "\nTID,min tsc,max tsc,duration\n"; + for (auto &Thread : ThreadMinMaxTSC) { + auto &MinMax = Thread.second; + if (OutputFormat == OutputFormats::TEXT) + outs() << Thread.first << "\t(" << MinMax.first << ", " << MinMax.second + << ")\t"; + if (OutputFormat == OutputFormats::CSV) + outs() << Thread.first << ',' << MinMax.first << ',' << MinMax.second + << ','; + if (HasCyclesPerSec) { + outs() << (MinMax.second - MinMax.first) / CyclesPerSec; + } else { + outs() << (MinMax.second - MinMax.first); + } + outs() << '\n'; + } + } + + // Print out CPU start/end ranges and durations. + if (!CPUMinMaxTSC.empty()) { + if (OutputFormat == OutputFormats::TEXT) + outs() << "\nTotal CPUs: " << CPUMinMaxTSC.size() + << "\nCPUID\t(min,max)\tduration in secs\n"; + if (OutputFormat == OutputFormats::CSV) + outs() << "\nCPUID,min tsc,max tsc,duration\n"; + for (auto &CPU : CPUMinMaxTSC) { + auto &MinMax = CPU.second; + if (OutputFormat == OutputFormats::TEXT) + outs() << static_cast(CPU.first) << "\t(" << MinMax.first + << ", " << MinMax.second << ")\t"; + if (OutputFormat == OutputFormats::CSV) + outs() << static_cast(CPU.first) << ',' << MinMax.first << ',' + << MinMax.second << ','; + if (HasCyclesPerSec) { + outs() << (MinMax.second - MinMax.first) / CyclesPerSec; + } else { + outs() << (MinMax.second - MinMax.first); + } + outs() << '\n'; + } + } +} Index: tools/llvm-xray/xray-log-reader.h =================================================================== --- /dev/null +++ tools/llvm-xray/xray-log-reader.h @@ -0,0 +1,79 @@ +//===- 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 XRAY_LOG_READER_H_ +#define XRAY_LOG_READER_H_ + +#include +#include +#include + +#include "xray-record-yaml.h" +#include "xray-record.h" + +namespace llvm { +namespace xray { + +class NaiveLogReader { + XRayFileHeader FileHeader; + typedef std::deque RecordsDeque; + RecordsDeque Records; + using iterator = RecordsDeque::iterator; + using citerator = RecordsDeque::const_iterator; + +public: + // Loads the contents of the file into memory, then sorts the events by TSC if + // |Sort| is true. + explicit NaiveLogReader(int Fd, std::error_code &EC, bool Sort = true); + + const XRayFileHeader &getFileHeader() const { return FileHeader; } + + iterator begin() { return Records.begin(); } + iterator end() { return Records.end(); } + citerator cbegin() { return Records.cbegin(); } + citerator cend() { return Records.cend(); } + citerator begin() const { return Records.begin(); } + citerator end() const { return Records.end(); } + citerator cbegin() const { return Records.begin(); } + citerator cend() const { return Records.end(); } + size_t size() const { return Records.size(); } +}; + +class YAMLLogReader { + XRayFileHeader FileHeader; + typedef std::vector RecordsVector; + RecordsVector Records; + using iterator = RecordsVector::iterator; + using citerator = RecordsVector::const_iterator; + +public: + // Loads the contents of the file from |Fd|, parses the YAML and turns those + // into XRayRecord entries. Sorts the events by TSC if |Sort| is true. + explicit YAMLLogReader(int Fd, std::error_code &EC, bool Sort = true); + + const XRayFileHeader &getFileHeader() const { return FileHeader; }; + iterator begin() { return Records.begin(); } + iterator end() { return Records.end(); } + citerator cbegin() { return Records.cbegin(); } + citerator cend() { return Records.cend(); } + citerator begin() const { return Records.begin(); } + citerator end() const { return Records.end(); } + citerator cbegin() const { return Records.begin(); } + citerator cend() const { return Records.end(); } + size_t size() const { return Records.size(); } +}; + +} // namespace xray +} // namespace llvm + +#endif // XRAY_LOG_READER_H_ Index: tools/llvm-xray/xray-log-reader.cc =================================================================== --- /dev/null +++ tools/llvm-xray/xray-log-reader.cc @@ -0,0 +1,86 @@ +//===- 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 + +#include "xray-log-reader.h" +#include "xray-record-yaml.h" +#include "llvm/Support/FileSystem.h" + +using namespace llvm; +using namespace llvm::xray; +using llvm::yaml::Input; + +NaiveLogReader::NaiveLogReader(int Fd, std::error_code &EC, bool Sort) { + off_t FileSize; + while ((FileSize = lseek(Fd, 0, SEEK_END)) == -1) { + if (errno == EINTR) + continue; + } + assert(FileSize != -1 && "Error seeking to the end of file"); + sys::fs::mapped_file_region MappedFile( + Fd, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0, EC); + if (EC) + return; + + // Set up the FileHeader. + memcpy(&FileHeader, MappedFile.data(), sizeof(XRayFileHeader)); + + // Start copying the data from the MappedFile into the Records data structure. + auto I = MappedFile.data() + sizeof(XRayFileHeader); + auto E = MappedFile.data() + MappedFile.size(); + while (I + sizeof(XRayRecord) <= E) { + Records.push_back({}); + memcpy(&Records.back(), I, sizeof(XRayRecord)); + I += sizeof(XRayRecord); + } + + if (Sort) + std::sort( + Records.begin(), Records.end(), + [](const XRayRecord &L, const XRayRecord &R) { return L.TSC < R.TSC; }); +} + +YAMLLogReader::YAMLLogReader(int Fd, std::error_code &EC, bool Sort) { + off_t FileSize; + while ((FileSize = lseek(Fd, 0, SEEK_END)) == -1) { + if (errno == EINTR) + continue; + } + assert(FileSize != -1 && "Error seeking to the end of the file"); + sys::fs::mapped_file_region MappedFile( + Fd, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0, EC); + if (EC) + return; + + // Load the documents from the MappedFile. + YAMLXRayTrace Trace; + Input In(StringRef(MappedFile.data(), FileSize)); + In >> Trace; + if (In.error()) { + EC = std::make_error_code(std::errc::io_error); + return; + } + // Only read the first trace in the document. + 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; + Records.swap(Trace.Records); + if (Sort) + std::sort(Records.begin(), Records.end(), + [](const YAMLXRayRecord &L, const YAMLXRayRecord &R) { + return L.TSC < R.TSC; + }); +} Index: tools/llvm-xray/xray-record-yaml.h =================================================================== --- /dev/null +++ tools/llvm-xray/xray-record-yaml.h @@ -0,0 +1,98 @@ +//===- 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 XRAY_RECORD_YAML_H +#define XRAY_RECORD_YAML_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; +}; + +enum class RecordTypes { ENTER, EXIT }; + +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; +}; + +} // 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 // XRAY_RECORD_YAML_H Index: tools/llvm-xray/xray-record.h =================================================================== --- /dev/null +++ tools/llvm-xray/xray-record.h @@ -0,0 +1,60 @@ +//===- 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 XRAY_RECORD_H_ +#define XRAY_RECORD_H_ + +#include + +namespace llvm { +namespace xray { + +struct alignas(32) XRayFileHeader { + uint16_t Version = 0; + uint16_t Type = 0; + bool ConstantTSC : 1; + bool NonstopTSC : 1; + alignas(8) uint64_t CycleFrequency = 0; +} __attribute__((packed)); + +static_assert(sizeof(XRayFileHeader) == 32, "XRayFileHeader != 32 bytes"); + +struct alignas(32) XRayRecord { + uint16_t RecordType = 0; + + // The CPU where the thread is running. We assume number of CPUs <= 256. + uint8_t CPU = 0; + + // The type of the event. Usually either ENTER = 0 or EXIT = 1. + uint8_t Type = 0; + + // The function ID for the record. + int32_t FuncId = 0; + + // Get the full 8 bytes of the TSC when we get the log record. + uint64_t TSC = 0; + + // The thread ID for the currently running thread. + uint32_t TId = 0; + + // Use some bytes in the end of the record for buffers. + char Buffer[4] = {}; +} __attribute__((packed)); + +static_assert(sizeof(XRayRecord) == 32, "XRayRecord != 32 bytes"); + +} // namespace xray +} // namespace llvm + +#endif // XRAY_RECORD_H_ Index: tools/llvm-xray/xray-sleds.h =================================================================== --- /dev/null +++ tools/llvm-xray/xray-sleds.h @@ -0,0 +1,31 @@ +//===- xray-sleds.h - XRay Sleds Data Structure ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Defines the structure used to represent XRay instrumentation map entries. +// +//===----------------------------------------------------------------------===// + +#ifndef XRAY_SLEDS_H_ +#define XRAY_SLEDS_H_ + +namespace llvm { +namespace xray { + +struct XRaySledEntry { + uint64_t Address; + uint64_t Function; + unsigned char Kind; + unsigned char AlwaysInstrument; + unsigned char Padding[14]; +}; + +} // namespace xray +} // namespace llvm + +#endif // XRAY_SLEDS_H_