10b57cec5SDimitry Andric //===- xray-converter.cpp: XRay Trace Conversion --------------------------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // Implements the trace conversion functions. 100b57cec5SDimitry Andric // 110b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 120b57cec5SDimitry Andric #include "xray-converter.h" 130b57cec5SDimitry Andric 140b57cec5SDimitry Andric #include "trie-node.h" 150b57cec5SDimitry Andric #include "xray-registry.h" 160b57cec5SDimitry Andric #include "llvm/DebugInfo/Symbolize/Symbolize.h" 170b57cec5SDimitry Andric #include "llvm/Support/EndianStream.h" 180b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h" 190b57cec5SDimitry Andric #include "llvm/Support/FormatVariadic.h" 200b57cec5SDimitry Andric #include "llvm/Support/ScopedPrinter.h" 210b57cec5SDimitry Andric #include "llvm/Support/YAMLTraits.h" 220b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 230b57cec5SDimitry Andric #include "llvm/XRay/InstrumentationMap.h" 240b57cec5SDimitry Andric #include "llvm/XRay/Trace.h" 250b57cec5SDimitry Andric #include "llvm/XRay/YAMLXRayRecord.h" 260b57cec5SDimitry Andric 270b57cec5SDimitry Andric using namespace llvm; 280b57cec5SDimitry Andric using namespace xray; 290b57cec5SDimitry Andric 300b57cec5SDimitry Andric // llvm-xray convert 310b57cec5SDimitry Andric // ---------------------------------------------------------------------------- 320b57cec5SDimitry Andric static cl::SubCommand Convert("convert", "Trace Format Conversion"); 330b57cec5SDimitry Andric static cl::opt<std::string> ConvertInput(cl::Positional, 340b57cec5SDimitry Andric cl::desc("<xray log file>"), 350b57cec5SDimitry Andric cl::Required, cl::sub(Convert)); 360b57cec5SDimitry Andric enum class ConvertFormats { BINARY, YAML, CHROME_TRACE_EVENT }; 370b57cec5SDimitry Andric static cl::opt<ConvertFormats> ConvertOutputFormat( 380b57cec5SDimitry Andric "output-format", cl::desc("output format"), 390b57cec5SDimitry Andric cl::values(clEnumValN(ConvertFormats::BINARY, "raw", "output in binary"), 400b57cec5SDimitry Andric clEnumValN(ConvertFormats::YAML, "yaml", "output in yaml"), 410b57cec5SDimitry Andric clEnumValN(ConvertFormats::CHROME_TRACE_EVENT, "trace_event", 420b57cec5SDimitry Andric "Output in chrome's trace event format. " 430b57cec5SDimitry Andric "May be visualized with the Catapult trace viewer.")), 440b57cec5SDimitry Andric cl::sub(Convert)); 450b57cec5SDimitry Andric static cl::alias ConvertOutputFormat2("f", cl::aliasopt(ConvertOutputFormat), 460b57cec5SDimitry Andric cl::desc("Alias for -output-format"), 470b57cec5SDimitry Andric cl::sub(Convert)); 480b57cec5SDimitry Andric static cl::opt<std::string> 490b57cec5SDimitry Andric ConvertOutput("output", cl::value_desc("output file"), cl::init("-"), 500b57cec5SDimitry Andric cl::desc("output file; use '-' for stdout"), 510b57cec5SDimitry Andric cl::sub(Convert)); 520b57cec5SDimitry Andric static cl::alias ConvertOutput2("o", cl::aliasopt(ConvertOutput), 530b57cec5SDimitry Andric cl::desc("Alias for -output"), 540b57cec5SDimitry Andric cl::sub(Convert)); 550b57cec5SDimitry Andric 560b57cec5SDimitry Andric static cl::opt<bool> 570b57cec5SDimitry Andric ConvertSymbolize("symbolize", 580b57cec5SDimitry Andric cl::desc("symbolize function ids from the input log"), 590b57cec5SDimitry Andric cl::init(false), cl::sub(Convert)); 600b57cec5SDimitry Andric static cl::alias ConvertSymbolize2("y", cl::aliasopt(ConvertSymbolize), 610b57cec5SDimitry Andric cl::desc("Alias for -symbolize"), 620b57cec5SDimitry Andric cl::sub(Convert)); 630b57cec5SDimitry Andric 640b57cec5SDimitry Andric static cl::opt<std::string> 650b57cec5SDimitry Andric ConvertInstrMap("instr_map", 660b57cec5SDimitry Andric cl::desc("binary with the instrumentation map, or " 670b57cec5SDimitry Andric "a separate instrumentation map"), 680b57cec5SDimitry Andric cl::value_desc("binary with xray_instr_map"), 690b57cec5SDimitry Andric cl::sub(Convert), cl::init("")); 700b57cec5SDimitry Andric static cl::alias ConvertInstrMap2("m", cl::aliasopt(ConvertInstrMap), 710b57cec5SDimitry Andric cl::desc("Alias for -instr_map"), 720b57cec5SDimitry Andric cl::sub(Convert)); 730b57cec5SDimitry Andric static cl::opt<bool> ConvertSortInput( 740b57cec5SDimitry Andric "sort", 750b57cec5SDimitry Andric cl::desc("determines whether to sort input log records by timestamp"), 760b57cec5SDimitry Andric cl::sub(Convert), cl::init(true)); 770b57cec5SDimitry Andric static cl::alias ConvertSortInput2("s", cl::aliasopt(ConvertSortInput), 780b57cec5SDimitry Andric cl::desc("Alias for -sort"), 790b57cec5SDimitry Andric cl::sub(Convert)); 800b57cec5SDimitry Andric 810b57cec5SDimitry Andric using llvm::yaml::Output; 820b57cec5SDimitry Andric 830b57cec5SDimitry Andric void TraceConverter::exportAsYAML(const Trace &Records, raw_ostream &OS) { 840b57cec5SDimitry Andric YAMLXRayTrace Trace; 850b57cec5SDimitry Andric const auto &FH = Records.getFileHeader(); 860b57cec5SDimitry Andric Trace.Header = {FH.Version, FH.Type, FH.ConstantTSC, FH.NonstopTSC, 870b57cec5SDimitry Andric FH.CycleFrequency}; 880b57cec5SDimitry Andric Trace.Records.reserve(Records.size()); 890b57cec5SDimitry Andric for (const auto &R : Records) { 900b57cec5SDimitry Andric Trace.Records.push_back({R.RecordType, R.CPU, R.Type, R.FuncId, 910b57cec5SDimitry Andric Symbolize ? FuncIdHelper.SymbolOrNumber(R.FuncId) 920b57cec5SDimitry Andric : llvm::to_string(R.FuncId), 930b57cec5SDimitry Andric R.TSC, R.TId, R.PId, R.CallArgs, R.Data}); 940b57cec5SDimitry Andric } 950b57cec5SDimitry Andric Output Out(OS, nullptr, 0); 960b57cec5SDimitry Andric Out.setWriteDefaultValues(false); 970b57cec5SDimitry Andric Out << Trace; 980b57cec5SDimitry Andric } 990b57cec5SDimitry Andric 1000b57cec5SDimitry Andric void TraceConverter::exportAsRAWv1(const Trace &Records, raw_ostream &OS) { 1010b57cec5SDimitry Andric // First write out the file header, in the correct endian-appropriate format 1020b57cec5SDimitry Andric // (XRay assumes currently little endian). 1030b57cec5SDimitry Andric support::endian::Writer Writer(OS, support::endianness::little); 1040b57cec5SDimitry Andric const auto &FH = Records.getFileHeader(); 1050b57cec5SDimitry Andric Writer.write(FH.Version); 1060b57cec5SDimitry Andric Writer.write(FH.Type); 1070b57cec5SDimitry Andric uint32_t Bitfield{0}; 1080b57cec5SDimitry Andric if (FH.ConstantTSC) 1090b57cec5SDimitry Andric Bitfield |= 1uL; 1100b57cec5SDimitry Andric if (FH.NonstopTSC) 1110b57cec5SDimitry Andric Bitfield |= 1uL << 1; 1120b57cec5SDimitry Andric Writer.write(Bitfield); 1130b57cec5SDimitry Andric Writer.write(FH.CycleFrequency); 1140b57cec5SDimitry Andric 1150b57cec5SDimitry Andric // There's 16 bytes of padding at the end of the file header. 1160b57cec5SDimitry Andric static constexpr uint32_t Padding4B = 0; 1170b57cec5SDimitry Andric Writer.write(Padding4B); 1180b57cec5SDimitry Andric Writer.write(Padding4B); 1190b57cec5SDimitry Andric Writer.write(Padding4B); 1200b57cec5SDimitry Andric Writer.write(Padding4B); 1210b57cec5SDimitry Andric 1220b57cec5SDimitry Andric // Then write out the rest of the records, still in an endian-appropriate 1230b57cec5SDimitry Andric // format. 1240b57cec5SDimitry Andric for (const auto &R : Records) { 1250b57cec5SDimitry Andric switch (R.Type) { 1260b57cec5SDimitry Andric case RecordTypes::ENTER: 1270b57cec5SDimitry Andric case RecordTypes::ENTER_ARG: 1280b57cec5SDimitry Andric Writer.write(R.RecordType); 1290b57cec5SDimitry Andric Writer.write(static_cast<uint8_t>(R.CPU)); 1300b57cec5SDimitry Andric Writer.write(uint8_t{0}); 1310b57cec5SDimitry Andric break; 1320b57cec5SDimitry Andric case RecordTypes::EXIT: 1330b57cec5SDimitry Andric Writer.write(R.RecordType); 1340b57cec5SDimitry Andric Writer.write(static_cast<uint8_t>(R.CPU)); 1350b57cec5SDimitry Andric Writer.write(uint8_t{1}); 1360b57cec5SDimitry Andric break; 1370b57cec5SDimitry Andric case RecordTypes::TAIL_EXIT: 1380b57cec5SDimitry Andric Writer.write(R.RecordType); 1390b57cec5SDimitry Andric Writer.write(static_cast<uint8_t>(R.CPU)); 1400b57cec5SDimitry Andric Writer.write(uint8_t{2}); 1410b57cec5SDimitry Andric break; 1420b57cec5SDimitry Andric case RecordTypes::CUSTOM_EVENT: 1430b57cec5SDimitry Andric case RecordTypes::TYPED_EVENT: 1440b57cec5SDimitry Andric // Skip custom and typed event records for v1 logs. 1450b57cec5SDimitry Andric continue; 1460b57cec5SDimitry Andric } 1470b57cec5SDimitry Andric Writer.write(R.FuncId); 1480b57cec5SDimitry Andric Writer.write(R.TSC); 1490b57cec5SDimitry Andric Writer.write(R.TId); 1500b57cec5SDimitry Andric 1510b57cec5SDimitry Andric if (FH.Version >= 3) 1520b57cec5SDimitry Andric Writer.write(R.PId); 1530b57cec5SDimitry Andric else 1540b57cec5SDimitry Andric Writer.write(Padding4B); 1550b57cec5SDimitry Andric 1560b57cec5SDimitry Andric Writer.write(Padding4B); 1570b57cec5SDimitry Andric Writer.write(Padding4B); 1580b57cec5SDimitry Andric } 1590b57cec5SDimitry Andric } 1600b57cec5SDimitry Andric 1610b57cec5SDimitry Andric namespace { 1620b57cec5SDimitry Andric 1630b57cec5SDimitry Andric // A structure that allows building a dictionary of stack ids for the Chrome 1640b57cec5SDimitry Andric // trace event format. 1650b57cec5SDimitry Andric struct StackIdData { 1660b57cec5SDimitry Andric // Each Stack of function calls has a unique ID. 1670b57cec5SDimitry Andric unsigned id; 1680b57cec5SDimitry Andric 1690b57cec5SDimitry Andric // Bookkeeping so that IDs can be maintained uniquely across threads. 1700b57cec5SDimitry Andric // Traversal keeps sibling pointers to other threads stacks. This is helpful 1710b57cec5SDimitry Andric // to determine when a thread encounters a new stack and should assign a new 1720b57cec5SDimitry Andric // unique ID. 1730b57cec5SDimitry Andric SmallVector<TrieNode<StackIdData> *, 4> siblings; 1740b57cec5SDimitry Andric }; 1750b57cec5SDimitry Andric 1760b57cec5SDimitry Andric using StackTrieNode = TrieNode<StackIdData>; 1770b57cec5SDimitry Andric 1780b57cec5SDimitry Andric // A helper function to find the sibling nodes for an encountered function in a 1790b57cec5SDimitry Andric // thread of execution. Relies on the invariant that each time a new node is 1800b57cec5SDimitry Andric // traversed in a thread, sibling bidirectional pointers are maintained. 1810b57cec5SDimitry Andric SmallVector<StackTrieNode *, 4> 1820b57cec5SDimitry Andric findSiblings(StackTrieNode *parent, int32_t FnId, uint32_t TId, 1830b57cec5SDimitry Andric const DenseMap<uint32_t, SmallVector<StackTrieNode *, 4>> 1840b57cec5SDimitry Andric &StackRootsByThreadId) { 1850b57cec5SDimitry Andric 1860b57cec5SDimitry Andric SmallVector<StackTrieNode *, 4> Siblings{}; 1870b57cec5SDimitry Andric 1880b57cec5SDimitry Andric if (parent == nullptr) { 1890b57cec5SDimitry Andric for (auto map_iter : StackRootsByThreadId) { 1900b57cec5SDimitry Andric // Only look for siblings in other threads. 1910b57cec5SDimitry Andric if (map_iter.first != TId) 1920b57cec5SDimitry Andric for (auto node_iter : map_iter.second) { 1930b57cec5SDimitry Andric if (node_iter->FuncId == FnId) 1940b57cec5SDimitry Andric Siblings.push_back(node_iter); 1950b57cec5SDimitry Andric } 1960b57cec5SDimitry Andric } 1970b57cec5SDimitry Andric return Siblings; 1980b57cec5SDimitry Andric } 1990b57cec5SDimitry Andric 2000b57cec5SDimitry Andric for (auto *ParentSibling : parent->ExtraData.siblings) 2010b57cec5SDimitry Andric for (auto node_iter : ParentSibling->Callees) 2020b57cec5SDimitry Andric if (node_iter->FuncId == FnId) 2030b57cec5SDimitry Andric Siblings.push_back(node_iter); 2040b57cec5SDimitry Andric 2050b57cec5SDimitry Andric return Siblings; 2060b57cec5SDimitry Andric } 2070b57cec5SDimitry Andric 2080b57cec5SDimitry Andric // Given a function being invoked in a thread with id TId, finds and returns the 2090b57cec5SDimitry Andric // StackTrie representing the function call stack. If no node exists, creates 2100b57cec5SDimitry Andric // the node. Assigns unique IDs to stacks newly encountered among all threads 2110b57cec5SDimitry Andric // and keeps sibling links up to when creating new nodes. 2120b57cec5SDimitry Andric StackTrieNode *findOrCreateStackNode( 2130b57cec5SDimitry Andric StackTrieNode *Parent, int32_t FuncId, uint32_t TId, 2140b57cec5SDimitry Andric DenseMap<uint32_t, SmallVector<StackTrieNode *, 4>> &StackRootsByThreadId, 2150b57cec5SDimitry Andric DenseMap<unsigned, StackTrieNode *> &StacksByStackId, unsigned *id_counter, 2160b57cec5SDimitry Andric std::forward_list<StackTrieNode> &NodeStore) { 2170b57cec5SDimitry Andric SmallVector<StackTrieNode *, 4> &ParentCallees = 2180b57cec5SDimitry Andric Parent == nullptr ? StackRootsByThreadId[TId] : Parent->Callees; 2190b57cec5SDimitry Andric auto match = find_if(ParentCallees, [FuncId](StackTrieNode *ParentCallee) { 2200b57cec5SDimitry Andric return FuncId == ParentCallee->FuncId; 2210b57cec5SDimitry Andric }); 2220b57cec5SDimitry Andric if (match != ParentCallees.end()) 2230b57cec5SDimitry Andric return *match; 2240b57cec5SDimitry Andric 2250b57cec5SDimitry Andric SmallVector<StackTrieNode *, 4> siblings = 2260b57cec5SDimitry Andric findSiblings(Parent, FuncId, TId, StackRootsByThreadId); 2270b57cec5SDimitry Andric if (siblings.empty()) { 2280b57cec5SDimitry Andric NodeStore.push_front({FuncId, Parent, {}, {(*id_counter)++, {}}}); 2290b57cec5SDimitry Andric StackTrieNode *CurrentStack = &NodeStore.front(); 2300b57cec5SDimitry Andric StacksByStackId[*id_counter - 1] = CurrentStack; 2310b57cec5SDimitry Andric ParentCallees.push_back(CurrentStack); 2320b57cec5SDimitry Andric return CurrentStack; 2330b57cec5SDimitry Andric } 2340b57cec5SDimitry Andric unsigned stack_id = siblings[0]->ExtraData.id; 2350b57cec5SDimitry Andric NodeStore.push_front({FuncId, Parent, {}, {stack_id, std::move(siblings)}}); 2360b57cec5SDimitry Andric StackTrieNode *CurrentStack = &NodeStore.front(); 2370b57cec5SDimitry Andric for (auto *sibling : CurrentStack->ExtraData.siblings) 2380b57cec5SDimitry Andric sibling->ExtraData.siblings.push_back(CurrentStack); 2390b57cec5SDimitry Andric ParentCallees.push_back(CurrentStack); 2400b57cec5SDimitry Andric return CurrentStack; 2410b57cec5SDimitry Andric } 2420b57cec5SDimitry Andric 2430b57cec5SDimitry Andric void writeTraceViewerRecord(uint16_t Version, raw_ostream &OS, int32_t FuncId, 2440b57cec5SDimitry Andric uint32_t TId, uint32_t PId, bool Symbolize, 2450b57cec5SDimitry Andric const FuncIdConversionHelper &FuncIdHelper, 2460b57cec5SDimitry Andric double EventTimestampUs, 2470b57cec5SDimitry Andric const StackTrieNode &StackCursor, 2480b57cec5SDimitry Andric StringRef FunctionPhenotype) { 2490b57cec5SDimitry Andric OS << " "; 2500b57cec5SDimitry Andric if (Version >= 3) { 2510b57cec5SDimitry Andric OS << llvm::formatv( 2520b57cec5SDimitry Andric R"({ "name" : "{0}", "ph" : "{1}", "tid" : "{2}", "pid" : "{3}", )" 2530b57cec5SDimitry Andric R"("ts" : "{4:f4}", "sf" : "{5}" })", 2540b57cec5SDimitry Andric (Symbolize ? FuncIdHelper.SymbolOrNumber(FuncId) 2550b57cec5SDimitry Andric : llvm::to_string(FuncId)), 2560b57cec5SDimitry Andric FunctionPhenotype, TId, PId, EventTimestampUs, 2570b57cec5SDimitry Andric StackCursor.ExtraData.id); 2580b57cec5SDimitry Andric } else { 2590b57cec5SDimitry Andric OS << llvm::formatv( 2600b57cec5SDimitry Andric R"({ "name" : "{0}", "ph" : "{1}", "tid" : "{2}", "pid" : "1", )" 2610b57cec5SDimitry Andric R"("ts" : "{3:f3}", "sf" : "{4}" })", 2620b57cec5SDimitry Andric (Symbolize ? FuncIdHelper.SymbolOrNumber(FuncId) 2630b57cec5SDimitry Andric : llvm::to_string(FuncId)), 2640b57cec5SDimitry Andric FunctionPhenotype, TId, EventTimestampUs, StackCursor.ExtraData.id); 2650b57cec5SDimitry Andric } 2660b57cec5SDimitry Andric } 2670b57cec5SDimitry Andric 2680b57cec5SDimitry Andric } // namespace 2690b57cec5SDimitry Andric 2700b57cec5SDimitry Andric void TraceConverter::exportAsChromeTraceEventFormat(const Trace &Records, 2710b57cec5SDimitry Andric raw_ostream &OS) { 2720b57cec5SDimitry Andric const auto &FH = Records.getFileHeader(); 2730b57cec5SDimitry Andric auto Version = FH.Version; 2740b57cec5SDimitry Andric auto CycleFreq = FH.CycleFrequency; 2750b57cec5SDimitry Andric 2760b57cec5SDimitry Andric unsigned id_counter = 0; 2770b57cec5SDimitry Andric 2780b57cec5SDimitry Andric OS << "{\n \"traceEvents\": ["; 2790b57cec5SDimitry Andric DenseMap<uint32_t, StackTrieNode *> StackCursorByThreadId{}; 2800b57cec5SDimitry Andric DenseMap<uint32_t, SmallVector<StackTrieNode *, 4>> StackRootsByThreadId{}; 2810b57cec5SDimitry Andric DenseMap<unsigned, StackTrieNode *> StacksByStackId{}; 2820b57cec5SDimitry Andric std::forward_list<StackTrieNode> NodeStore{}; 2830b57cec5SDimitry Andric int loop_count = 0; 2840b57cec5SDimitry Andric for (const auto &R : Records) { 2850b57cec5SDimitry Andric if (loop_count++ == 0) 2860b57cec5SDimitry Andric OS << "\n"; 2870b57cec5SDimitry Andric else 2880b57cec5SDimitry Andric OS << ",\n"; 2890b57cec5SDimitry Andric 2900b57cec5SDimitry Andric // Chrome trace event format always wants data in micros. 2910b57cec5SDimitry Andric // CyclesPerMicro = CycleHertz / 10^6 2920b57cec5SDimitry Andric // TSC / CyclesPerMicro == TSC * 10^6 / CycleHertz == MicroTimestamp 2930b57cec5SDimitry Andric // Could lose some precision here by converting the TSC to a double to 2940b57cec5SDimitry Andric // multiply by the period in micros. 52 bit mantissa is a good start though. 2950b57cec5SDimitry Andric // TODO: Make feature request to Chrome Trace viewer to accept ticks and a 2960b57cec5SDimitry Andric // frequency or do some more involved calculation to avoid dangers of 2970b57cec5SDimitry Andric // conversion. 2980b57cec5SDimitry Andric double EventTimestampUs = double(1000000) / CycleFreq * double(R.TSC); 2990b57cec5SDimitry Andric StackTrieNode *&StackCursor = StackCursorByThreadId[R.TId]; 3000b57cec5SDimitry Andric switch (R.Type) { 3010b57cec5SDimitry Andric case RecordTypes::CUSTOM_EVENT: 3020b57cec5SDimitry Andric case RecordTypes::TYPED_EVENT: 3030b57cec5SDimitry Andric // TODO: Support typed and custom event rendering on Chrome Trace Viewer. 3040b57cec5SDimitry Andric break; 3050b57cec5SDimitry Andric case RecordTypes::ENTER: 3060b57cec5SDimitry Andric case RecordTypes::ENTER_ARG: 3070b57cec5SDimitry Andric StackCursor = findOrCreateStackNode(StackCursor, R.FuncId, R.TId, 3080b57cec5SDimitry Andric StackRootsByThreadId, StacksByStackId, 3090b57cec5SDimitry Andric &id_counter, NodeStore); 3100b57cec5SDimitry Andric // Each record is represented as a json dictionary with function name, 3110b57cec5SDimitry Andric // type of B for begin or E for end, thread id, process id, 3120b57cec5SDimitry Andric // timestamp in microseconds, and a stack frame id. The ids are logged 3130b57cec5SDimitry Andric // in an id dictionary after the events. 3140b57cec5SDimitry Andric writeTraceViewerRecord(Version, OS, R.FuncId, R.TId, R.PId, Symbolize, 3150b57cec5SDimitry Andric FuncIdHelper, EventTimestampUs, *StackCursor, "B"); 3160b57cec5SDimitry Andric break; 3170b57cec5SDimitry Andric case RecordTypes::EXIT: 3180b57cec5SDimitry Andric case RecordTypes::TAIL_EXIT: 3190b57cec5SDimitry Andric // No entries to record end for. 3200b57cec5SDimitry Andric if (StackCursor == nullptr) 3210b57cec5SDimitry Andric break; 3220b57cec5SDimitry Andric // Should we emit an END record anyway or account this condition? 3230b57cec5SDimitry Andric // (And/Or in loop termination below) 3240b57cec5SDimitry Andric StackTrieNode *PreviousCursor = nullptr; 3250b57cec5SDimitry Andric do { 3260b57cec5SDimitry Andric if (PreviousCursor != nullptr) { 3270b57cec5SDimitry Andric OS << ",\n"; 3280b57cec5SDimitry Andric } 3290b57cec5SDimitry Andric writeTraceViewerRecord(Version, OS, StackCursor->FuncId, R.TId, R.PId, 3300b57cec5SDimitry Andric Symbolize, FuncIdHelper, EventTimestampUs, 3310b57cec5SDimitry Andric *StackCursor, "E"); 3320b57cec5SDimitry Andric PreviousCursor = StackCursor; 3330b57cec5SDimitry Andric StackCursor = StackCursor->Parent; 3340b57cec5SDimitry Andric } while (PreviousCursor->FuncId != R.FuncId && StackCursor != nullptr); 3350b57cec5SDimitry Andric break; 3360b57cec5SDimitry Andric } 3370b57cec5SDimitry Andric } 3380b57cec5SDimitry Andric OS << "\n ],\n"; // Close the Trace Events array. 3390b57cec5SDimitry Andric OS << " " 3400b57cec5SDimitry Andric << "\"displayTimeUnit\": \"ns\",\n"; 3410b57cec5SDimitry Andric 3420b57cec5SDimitry Andric // The stackFrames dictionary substantially reduces size of the output file by 3430b57cec5SDimitry Andric // avoiding repeating the entire call stack of function names for each entry. 3440b57cec5SDimitry Andric OS << R"( "stackFrames": {)"; 3450b57cec5SDimitry Andric int stack_frame_count = 0; 3460b57cec5SDimitry Andric for (auto map_iter : StacksByStackId) { 3470b57cec5SDimitry Andric if (stack_frame_count++ == 0) 3480b57cec5SDimitry Andric OS << "\n"; 3490b57cec5SDimitry Andric else 3500b57cec5SDimitry Andric OS << ",\n"; 3510b57cec5SDimitry Andric OS << " "; 3520b57cec5SDimitry Andric OS << llvm::formatv( 3530b57cec5SDimitry Andric R"("{0}" : { "name" : "{1}")", map_iter.first, 3540b57cec5SDimitry Andric (Symbolize ? FuncIdHelper.SymbolOrNumber(map_iter.second->FuncId) 3550b57cec5SDimitry Andric : llvm::to_string(map_iter.second->FuncId))); 3560b57cec5SDimitry Andric if (map_iter.second->Parent != nullptr) 3570b57cec5SDimitry Andric OS << llvm::formatv(R"(, "parent": "{0}")", 3580b57cec5SDimitry Andric map_iter.second->Parent->ExtraData.id); 3590b57cec5SDimitry Andric OS << " }"; 3600b57cec5SDimitry Andric } 3610b57cec5SDimitry Andric OS << "\n }\n"; // Close the stack frames map. 3620b57cec5SDimitry Andric OS << "}\n"; // Close the JSON entry. 3630b57cec5SDimitry Andric } 3640b57cec5SDimitry Andric 3650b57cec5SDimitry Andric namespace llvm { 3660b57cec5SDimitry Andric namespace xray { 3670b57cec5SDimitry Andric 3680b57cec5SDimitry Andric static CommandRegistration Unused(&Convert, []() -> Error { 3690b57cec5SDimitry Andric // FIXME: Support conversion to BINARY when upgrading XRay trace versions. 3700b57cec5SDimitry Andric InstrumentationMap Map; 3710b57cec5SDimitry Andric if (!ConvertInstrMap.empty()) { 3720b57cec5SDimitry Andric auto InstrumentationMapOrError = loadInstrumentationMap(ConvertInstrMap); 3730b57cec5SDimitry Andric if (!InstrumentationMapOrError) 3740b57cec5SDimitry Andric return joinErrors(make_error<StringError>( 3750b57cec5SDimitry Andric Twine("Cannot open instrumentation map '") + 3760b57cec5SDimitry Andric ConvertInstrMap + "'", 3770b57cec5SDimitry Andric std::make_error_code(std::errc::invalid_argument)), 3780b57cec5SDimitry Andric InstrumentationMapOrError.takeError()); 3790b57cec5SDimitry Andric Map = std::move(*InstrumentationMapOrError); 3800b57cec5SDimitry Andric } 3810b57cec5SDimitry Andric 3820b57cec5SDimitry Andric const auto &FunctionAddresses = Map.getFunctionAddresses(); 3830b57cec5SDimitry Andric symbolize::LLVMSymbolizer Symbolizer; 3840b57cec5SDimitry Andric llvm::xray::FuncIdConversionHelper FuncIdHelper(ConvertInstrMap, Symbolizer, 3850b57cec5SDimitry Andric FunctionAddresses); 3860b57cec5SDimitry Andric llvm::xray::TraceConverter TC(FuncIdHelper, ConvertSymbolize); 3870b57cec5SDimitry Andric std::error_code EC; 3880b57cec5SDimitry Andric raw_fd_ostream OS(ConvertOutput, EC, 3890b57cec5SDimitry Andric ConvertOutputFormat == ConvertFormats::BINARY 3900b57cec5SDimitry Andric ? sys::fs::OpenFlags::F_None 3910b57cec5SDimitry Andric : sys::fs::OpenFlags::F_Text); 3920b57cec5SDimitry Andric if (EC) 3930b57cec5SDimitry Andric return make_error<StringError>( 3940b57cec5SDimitry Andric Twine("Cannot open file '") + ConvertOutput + "' for writing.", EC); 3950b57cec5SDimitry Andric 3960b57cec5SDimitry Andric auto TraceOrErr = loadTraceFile(ConvertInput, ConvertSortInput); 3970b57cec5SDimitry Andric if (!TraceOrErr) 3980b57cec5SDimitry Andric return joinErrors( 3990b57cec5SDimitry Andric make_error<StringError>( 4000b57cec5SDimitry Andric Twine("Failed loading input file '") + ConvertInput + "'.", 4010b57cec5SDimitry Andric std::make_error_code(std::errc::executable_format_error)), 4020b57cec5SDimitry Andric TraceOrErr.takeError()); 4030b57cec5SDimitry Andric 4040b57cec5SDimitry Andric auto &T = *TraceOrErr; 4050b57cec5SDimitry Andric switch (ConvertOutputFormat) { 4060b57cec5SDimitry Andric case ConvertFormats::YAML: 4070b57cec5SDimitry Andric TC.exportAsYAML(T, OS); 4080b57cec5SDimitry Andric break; 4090b57cec5SDimitry Andric case ConvertFormats::BINARY: 4100b57cec5SDimitry Andric TC.exportAsRAWv1(T, OS); 4110b57cec5SDimitry Andric break; 4120b57cec5SDimitry Andric case ConvertFormats::CHROME_TRACE_EVENT: 4130b57cec5SDimitry Andric TC.exportAsChromeTraceEventFormat(T, OS); 4140b57cec5SDimitry Andric break; 4150b57cec5SDimitry Andric } 4160b57cec5SDimitry Andric return Error::success(); 4170b57cec5SDimitry Andric }); 4180b57cec5SDimitry Andric 4190b57cec5SDimitry Andric } // namespace xray 4200b57cec5SDimitry Andric } // namespace llvm 421