10b57cec5SDimitry Andric //===- Profile.cpp - XRay Profile Abstraction -----------------------------===// 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 // Defines the XRay Profile class representing the latency profile generated by 100b57cec5SDimitry Andric // XRay's profiling mode. 110b57cec5SDimitry Andric // 120b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 130b57cec5SDimitry Andric #include "llvm/XRay/Profile.h" 140b57cec5SDimitry Andric 150b57cec5SDimitry Andric #include "llvm/Support/DataExtractor.h" 160b57cec5SDimitry Andric #include "llvm/Support/Error.h" 170b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h" 180b57cec5SDimitry Andric #include "llvm/XRay/Trace.h" 190b57cec5SDimitry Andric #include <deque> 200b57cec5SDimitry Andric #include <memory> 210b57cec5SDimitry Andric 220b57cec5SDimitry Andric namespace llvm { 230b57cec5SDimitry Andric namespace xray { 240b57cec5SDimitry Andric 250b57cec5SDimitry Andric Profile::Profile(const Profile &O) { 260b57cec5SDimitry Andric // We need to re-create all the tries from the original (O), into the current 270b57cec5SDimitry Andric // Profile being initialized, through the Block instances we see. 280b57cec5SDimitry Andric for (const auto &Block : O) { 290b57cec5SDimitry Andric Blocks.push_back({Block.Thread, {}}); 300b57cec5SDimitry Andric auto &B = Blocks.back(); 310b57cec5SDimitry Andric for (const auto &PathData : Block.PathData) 320b57cec5SDimitry Andric B.PathData.push_back({internPath(cantFail(O.expandPath(PathData.first))), 330b57cec5SDimitry Andric PathData.second}); 340b57cec5SDimitry Andric } 350b57cec5SDimitry Andric } 360b57cec5SDimitry Andric 370b57cec5SDimitry Andric Profile &Profile::operator=(const Profile &O) { 380b57cec5SDimitry Andric Profile P = O; 390b57cec5SDimitry Andric *this = std::move(P); 400b57cec5SDimitry Andric return *this; 410b57cec5SDimitry Andric } 420b57cec5SDimitry Andric 430b57cec5SDimitry Andric namespace { 440b57cec5SDimitry Andric 450b57cec5SDimitry Andric struct BlockHeader { 460b57cec5SDimitry Andric uint32_t Size; 470b57cec5SDimitry Andric uint32_t Number; 480b57cec5SDimitry Andric uint64_t Thread; 490b57cec5SDimitry Andric }; 500b57cec5SDimitry Andric 510b57cec5SDimitry Andric static Expected<BlockHeader> readBlockHeader(DataExtractor &Extractor, 528bcb0991SDimitry Andric uint64_t &Offset) { 530b57cec5SDimitry Andric BlockHeader H; 548bcb0991SDimitry Andric uint64_t CurrentOffset = Offset; 550b57cec5SDimitry Andric H.Size = Extractor.getU32(&Offset); 560b57cec5SDimitry Andric if (Offset == CurrentOffset) 570b57cec5SDimitry Andric return make_error<StringError>( 580b57cec5SDimitry Andric Twine("Error parsing block header size at offset '") + 590b57cec5SDimitry Andric Twine(CurrentOffset) + "'", 600b57cec5SDimitry Andric std::make_error_code(std::errc::invalid_argument)); 610b57cec5SDimitry Andric CurrentOffset = Offset; 620b57cec5SDimitry Andric H.Number = Extractor.getU32(&Offset); 630b57cec5SDimitry Andric if (Offset == CurrentOffset) 640b57cec5SDimitry Andric return make_error<StringError>( 650b57cec5SDimitry Andric Twine("Error parsing block header number at offset '") + 660b57cec5SDimitry Andric Twine(CurrentOffset) + "'", 670b57cec5SDimitry Andric std::make_error_code(std::errc::invalid_argument)); 680b57cec5SDimitry Andric CurrentOffset = Offset; 690b57cec5SDimitry Andric H.Thread = Extractor.getU64(&Offset); 700b57cec5SDimitry Andric if (Offset == CurrentOffset) 710b57cec5SDimitry Andric return make_error<StringError>( 720b57cec5SDimitry Andric Twine("Error parsing block header thread id at offset '") + 730b57cec5SDimitry Andric Twine(CurrentOffset) + "'", 740b57cec5SDimitry Andric std::make_error_code(std::errc::invalid_argument)); 750b57cec5SDimitry Andric return H; 760b57cec5SDimitry Andric } 770b57cec5SDimitry Andric 780b57cec5SDimitry Andric static Expected<std::vector<Profile::FuncID>> readPath(DataExtractor &Extractor, 798bcb0991SDimitry Andric uint64_t &Offset) { 800b57cec5SDimitry Andric // We're reading a sequence of int32_t's until we find a 0. 810b57cec5SDimitry Andric std::vector<Profile::FuncID> Path; 820b57cec5SDimitry Andric auto CurrentOffset = Offset; 830b57cec5SDimitry Andric int32_t FuncId; 840b57cec5SDimitry Andric do { 850b57cec5SDimitry Andric FuncId = Extractor.getSigned(&Offset, 4); 860b57cec5SDimitry Andric if (CurrentOffset == Offset) 870b57cec5SDimitry Andric return make_error<StringError>( 880b57cec5SDimitry Andric Twine("Error parsing path at offset '") + Twine(CurrentOffset) + "'", 890b57cec5SDimitry Andric std::make_error_code(std::errc::invalid_argument)); 900b57cec5SDimitry Andric CurrentOffset = Offset; 910b57cec5SDimitry Andric Path.push_back(FuncId); 920b57cec5SDimitry Andric } while (FuncId != 0); 930b57cec5SDimitry Andric return std::move(Path); 940b57cec5SDimitry Andric } 950b57cec5SDimitry Andric 960b57cec5SDimitry Andric static Expected<Profile::Data> readData(DataExtractor &Extractor, 978bcb0991SDimitry Andric uint64_t &Offset) { 980b57cec5SDimitry Andric // We expect a certain number of elements for Data: 990b57cec5SDimitry Andric // - A 64-bit CallCount 1000b57cec5SDimitry Andric // - A 64-bit CumulativeLocalTime counter 1010b57cec5SDimitry Andric Profile::Data D; 1020b57cec5SDimitry Andric auto CurrentOffset = Offset; 1030b57cec5SDimitry Andric D.CallCount = Extractor.getU64(&Offset); 1040b57cec5SDimitry Andric if (CurrentOffset == Offset) 1050b57cec5SDimitry Andric return make_error<StringError>( 1060b57cec5SDimitry Andric Twine("Error parsing call counts at offset '") + Twine(CurrentOffset) + 1070b57cec5SDimitry Andric "'", 1080b57cec5SDimitry Andric std::make_error_code(std::errc::invalid_argument)); 1090b57cec5SDimitry Andric CurrentOffset = Offset; 1100b57cec5SDimitry Andric D.CumulativeLocalTime = Extractor.getU64(&Offset); 1110b57cec5SDimitry Andric if (CurrentOffset == Offset) 1120b57cec5SDimitry Andric return make_error<StringError>( 1130b57cec5SDimitry Andric Twine("Error parsing cumulative local time at offset '") + 1140b57cec5SDimitry Andric Twine(CurrentOffset) + "'", 1150b57cec5SDimitry Andric std::make_error_code(std::errc::invalid_argument)); 1160b57cec5SDimitry Andric return D; 1170b57cec5SDimitry Andric } 1180b57cec5SDimitry Andric 1190b57cec5SDimitry Andric } // namespace 1200b57cec5SDimitry Andric 1210b57cec5SDimitry Andric Error Profile::addBlock(Block &&B) { 1220b57cec5SDimitry Andric if (B.PathData.empty()) 1230b57cec5SDimitry Andric return make_error<StringError>( 1240b57cec5SDimitry Andric "Block may not have empty path data.", 1250b57cec5SDimitry Andric std::make_error_code(std::errc::invalid_argument)); 1260b57cec5SDimitry Andric 1270b57cec5SDimitry Andric Blocks.emplace_back(std::move(B)); 1280b57cec5SDimitry Andric return Error::success(); 1290b57cec5SDimitry Andric } 1300b57cec5SDimitry Andric 1310b57cec5SDimitry Andric Expected<std::vector<Profile::FuncID>> Profile::expandPath(PathID P) const { 1320b57cec5SDimitry Andric auto It = PathIDMap.find(P); 1330b57cec5SDimitry Andric if (It == PathIDMap.end()) 1340b57cec5SDimitry Andric return make_error<StringError>( 1350b57cec5SDimitry Andric Twine("PathID not found: ") + Twine(P), 1360b57cec5SDimitry Andric std::make_error_code(std::errc::invalid_argument)); 1370b57cec5SDimitry Andric std::vector<Profile::FuncID> Path; 1380b57cec5SDimitry Andric for (auto Node = It->second; Node; Node = Node->Caller) 1390b57cec5SDimitry Andric Path.push_back(Node->Func); 1400b57cec5SDimitry Andric return std::move(Path); 1410b57cec5SDimitry Andric } 1420b57cec5SDimitry Andric 1430b57cec5SDimitry Andric Profile::PathID Profile::internPath(ArrayRef<FuncID> P) { 1440b57cec5SDimitry Andric if (P.empty()) 1450b57cec5SDimitry Andric return 0; 1460b57cec5SDimitry Andric 1470b57cec5SDimitry Andric auto RootToLeafPath = reverse(P); 1480b57cec5SDimitry Andric 1490b57cec5SDimitry Andric // Find the root. 1500b57cec5SDimitry Andric auto It = RootToLeafPath.begin(); 1510b57cec5SDimitry Andric auto PathRoot = *It++; 1520b57cec5SDimitry Andric auto RootIt = 1530b57cec5SDimitry Andric find_if(Roots, [PathRoot](TrieNode *N) { return N->Func == PathRoot; }); 1540b57cec5SDimitry Andric 1550b57cec5SDimitry Andric // If we've not seen this root before, remember it. 1560b57cec5SDimitry Andric TrieNode *Node = nullptr; 1570b57cec5SDimitry Andric if (RootIt == Roots.end()) { 1580b57cec5SDimitry Andric NodeStorage.emplace_back(); 1590b57cec5SDimitry Andric Node = &NodeStorage.back(); 1600b57cec5SDimitry Andric Node->Func = PathRoot; 1610b57cec5SDimitry Andric Roots.push_back(Node); 1620b57cec5SDimitry Andric } else { 1630b57cec5SDimitry Andric Node = *RootIt; 1640b57cec5SDimitry Andric } 1650b57cec5SDimitry Andric 1660b57cec5SDimitry Andric // Now traverse the path, re-creating if necessary. 1670b57cec5SDimitry Andric while (It != RootToLeafPath.end()) { 1680b57cec5SDimitry Andric auto NodeFuncID = *It++; 1690b57cec5SDimitry Andric auto CalleeIt = find_if(Node->Callees, [NodeFuncID](TrieNode *N) { 1700b57cec5SDimitry Andric return N->Func == NodeFuncID; 1710b57cec5SDimitry Andric }); 1720b57cec5SDimitry Andric if (CalleeIt == Node->Callees.end()) { 1730b57cec5SDimitry Andric NodeStorage.emplace_back(); 1740b57cec5SDimitry Andric auto NewNode = &NodeStorage.back(); 1750b57cec5SDimitry Andric NewNode->Func = NodeFuncID; 1760b57cec5SDimitry Andric NewNode->Caller = Node; 1770b57cec5SDimitry Andric Node->Callees.push_back(NewNode); 1780b57cec5SDimitry Andric Node = NewNode; 1790b57cec5SDimitry Andric } else { 1800b57cec5SDimitry Andric Node = *CalleeIt; 1810b57cec5SDimitry Andric } 1820b57cec5SDimitry Andric } 1830b57cec5SDimitry Andric 1840b57cec5SDimitry Andric // At this point, Node *must* be pointing at the leaf. 1850b57cec5SDimitry Andric assert(Node->Func == P.front()); 1860b57cec5SDimitry Andric if (Node->ID == 0) { 1870b57cec5SDimitry Andric Node->ID = NextID++; 1880b57cec5SDimitry Andric PathIDMap.insert({Node->ID, Node}); 1890b57cec5SDimitry Andric } 1900b57cec5SDimitry Andric return Node->ID; 1910b57cec5SDimitry Andric } 1920b57cec5SDimitry Andric 1930b57cec5SDimitry Andric Profile mergeProfilesByThread(const Profile &L, const Profile &R) { 1940b57cec5SDimitry Andric Profile Merged; 1950b57cec5SDimitry Andric using PathDataMap = DenseMap<Profile::PathID, Profile::Data>; 1960b57cec5SDimitry Andric using PathDataMapPtr = std::unique_ptr<PathDataMap>; 1970b57cec5SDimitry Andric using PathDataVector = decltype(Profile::Block::PathData); 1980b57cec5SDimitry Andric using ThreadProfileIndexMap = DenseMap<Profile::ThreadID, PathDataMapPtr>; 1990b57cec5SDimitry Andric ThreadProfileIndexMap ThreadProfileIndex; 2000b57cec5SDimitry Andric 2010b57cec5SDimitry Andric for (const auto &P : {std::ref(L), std::ref(R)}) 2020b57cec5SDimitry Andric for (const auto &Block : P.get()) { 2030b57cec5SDimitry Andric ThreadProfileIndexMap::iterator It; 2040b57cec5SDimitry Andric std::tie(It, std::ignore) = ThreadProfileIndex.insert( 205*0fca6ea1SDimitry Andric {Block.Thread, std::make_unique<PathDataMap>()}); 2060b57cec5SDimitry Andric for (const auto &PathAndData : Block.PathData) { 2070b57cec5SDimitry Andric auto &PathID = PathAndData.first; 2080b57cec5SDimitry Andric auto &Data = PathAndData.second; 2090b57cec5SDimitry Andric auto NewPathID = 2100b57cec5SDimitry Andric Merged.internPath(cantFail(P.get().expandPath(PathID))); 2110b57cec5SDimitry Andric PathDataMap::iterator PathDataIt; 2120b57cec5SDimitry Andric bool Inserted; 2130b57cec5SDimitry Andric std::tie(PathDataIt, Inserted) = It->second->insert({NewPathID, Data}); 2140b57cec5SDimitry Andric if (!Inserted) { 2150b57cec5SDimitry Andric auto &ExistingData = PathDataIt->second; 2160b57cec5SDimitry Andric ExistingData.CallCount += Data.CallCount; 2170b57cec5SDimitry Andric ExistingData.CumulativeLocalTime += Data.CumulativeLocalTime; 2180b57cec5SDimitry Andric } 2190b57cec5SDimitry Andric } 2200b57cec5SDimitry Andric } 2210b57cec5SDimitry Andric 2220b57cec5SDimitry Andric for (const auto &IndexedThreadBlock : ThreadProfileIndex) { 2230b57cec5SDimitry Andric PathDataVector PathAndData; 2240b57cec5SDimitry Andric PathAndData.reserve(IndexedThreadBlock.second->size()); 2250b57cec5SDimitry Andric copy(*IndexedThreadBlock.second, std::back_inserter(PathAndData)); 2260b57cec5SDimitry Andric cantFail( 2270b57cec5SDimitry Andric Merged.addBlock({IndexedThreadBlock.first, std::move(PathAndData)})); 2280b57cec5SDimitry Andric } 2290b57cec5SDimitry Andric return Merged; 2300b57cec5SDimitry Andric } 2310b57cec5SDimitry Andric 2320b57cec5SDimitry Andric Profile mergeProfilesByStack(const Profile &L, const Profile &R) { 2330b57cec5SDimitry Andric Profile Merged; 2340b57cec5SDimitry Andric using PathDataMap = DenseMap<Profile::PathID, Profile::Data>; 2350b57cec5SDimitry Andric PathDataMap PathData; 2360b57cec5SDimitry Andric using PathDataVector = decltype(Profile::Block::PathData); 2370b57cec5SDimitry Andric for (const auto &P : {std::ref(L), std::ref(R)}) 2380b57cec5SDimitry Andric for (const auto &Block : P.get()) 2390b57cec5SDimitry Andric for (const auto &PathAndData : Block.PathData) { 2400b57cec5SDimitry Andric auto &PathId = PathAndData.first; 2410b57cec5SDimitry Andric auto &Data = PathAndData.second; 2420b57cec5SDimitry Andric auto NewPathID = 2430b57cec5SDimitry Andric Merged.internPath(cantFail(P.get().expandPath(PathId))); 2440b57cec5SDimitry Andric PathDataMap::iterator PathDataIt; 2450b57cec5SDimitry Andric bool Inserted; 2460b57cec5SDimitry Andric std::tie(PathDataIt, Inserted) = PathData.insert({NewPathID, Data}); 2470b57cec5SDimitry Andric if (!Inserted) { 2480b57cec5SDimitry Andric auto &ExistingData = PathDataIt->second; 2490b57cec5SDimitry Andric ExistingData.CallCount += Data.CallCount; 2500b57cec5SDimitry Andric ExistingData.CumulativeLocalTime += Data.CumulativeLocalTime; 2510b57cec5SDimitry Andric } 2520b57cec5SDimitry Andric } 2530b57cec5SDimitry Andric 2540b57cec5SDimitry Andric // In the end there's a single Block, for thread 0. 2550b57cec5SDimitry Andric PathDataVector Block; 2560b57cec5SDimitry Andric Block.reserve(PathData.size()); 2570b57cec5SDimitry Andric copy(PathData, std::back_inserter(Block)); 2580b57cec5SDimitry Andric cantFail(Merged.addBlock({0, std::move(Block)})); 2590b57cec5SDimitry Andric return Merged; 2600b57cec5SDimitry Andric } 2610b57cec5SDimitry Andric 2620b57cec5SDimitry Andric Expected<Profile> loadProfile(StringRef Filename) { 2630b57cec5SDimitry Andric Expected<sys::fs::file_t> FdOrErr = sys::fs::openNativeFileForRead(Filename); 2640b57cec5SDimitry Andric if (!FdOrErr) 2650b57cec5SDimitry Andric return FdOrErr.takeError(); 2660b57cec5SDimitry Andric 2670b57cec5SDimitry Andric uint64_t FileSize; 2680b57cec5SDimitry Andric if (auto EC = sys::fs::file_size(Filename, FileSize)) 2690b57cec5SDimitry Andric return make_error<StringError>( 2700b57cec5SDimitry Andric Twine("Cannot get filesize of '") + Filename + "'", EC); 2710b57cec5SDimitry Andric 2720b57cec5SDimitry Andric std::error_code EC; 2730b57cec5SDimitry Andric sys::fs::mapped_file_region MappedFile( 2740b57cec5SDimitry Andric *FdOrErr, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0, 2750b57cec5SDimitry Andric EC); 2760b57cec5SDimitry Andric sys::fs::closeFile(*FdOrErr); 2770b57cec5SDimitry Andric if (EC) 2780b57cec5SDimitry Andric return make_error<StringError>( 2790b57cec5SDimitry Andric Twine("Cannot mmap profile '") + Filename + "'", EC); 2800b57cec5SDimitry Andric StringRef Data(MappedFile.data(), MappedFile.size()); 2810b57cec5SDimitry Andric 2820b57cec5SDimitry Andric Profile P; 2838bcb0991SDimitry Andric uint64_t Offset = 0; 2840b57cec5SDimitry Andric DataExtractor Extractor(Data, true, 8); 2850b57cec5SDimitry Andric 2860b57cec5SDimitry Andric // For each block we get from the file: 2870b57cec5SDimitry Andric while (Offset != MappedFile.size()) { 2880b57cec5SDimitry Andric auto HeaderOrError = readBlockHeader(Extractor, Offset); 2890b57cec5SDimitry Andric if (!HeaderOrError) 2900b57cec5SDimitry Andric return HeaderOrError.takeError(); 2910b57cec5SDimitry Andric 2920b57cec5SDimitry Andric // TODO: Maybe store this header information for each block, even just for 2930b57cec5SDimitry Andric // debugging? 2940b57cec5SDimitry Andric const auto &Header = HeaderOrError.get(); 2950b57cec5SDimitry Andric 2960b57cec5SDimitry Andric // Read in the path data. 2970b57cec5SDimitry Andric auto PathOrError = readPath(Extractor, Offset); 2980b57cec5SDimitry Andric if (!PathOrError) 2990b57cec5SDimitry Andric return PathOrError.takeError(); 3000b57cec5SDimitry Andric const auto &Path = PathOrError.get(); 3010b57cec5SDimitry Andric 3020b57cec5SDimitry Andric // For each path we encounter, we should intern it to get a PathID. 3030b57cec5SDimitry Andric auto DataOrError = readData(Extractor, Offset); 3040b57cec5SDimitry Andric if (!DataOrError) 3050b57cec5SDimitry Andric return DataOrError.takeError(); 3060b57cec5SDimitry Andric auto &Data = DataOrError.get(); 3070b57cec5SDimitry Andric 3080b57cec5SDimitry Andric if (auto E = 3090b57cec5SDimitry Andric P.addBlock(Profile::Block{Profile::ThreadID{Header.Thread}, 3100b57cec5SDimitry Andric {{P.internPath(Path), std::move(Data)}}})) 3110b57cec5SDimitry Andric return std::move(E); 3120b57cec5SDimitry Andric } 3130b57cec5SDimitry Andric 3140b57cec5SDimitry Andric return P; 3150b57cec5SDimitry Andric } 3160b57cec5SDimitry Andric 3170b57cec5SDimitry Andric namespace { 3180b57cec5SDimitry Andric 3190b57cec5SDimitry Andric struct StackEntry { 3200b57cec5SDimitry Andric uint64_t Timestamp; 3210b57cec5SDimitry Andric Profile::FuncID FuncId; 3220b57cec5SDimitry Andric }; 3230b57cec5SDimitry Andric 3240b57cec5SDimitry Andric } // namespace 3250b57cec5SDimitry Andric 3260b57cec5SDimitry Andric Expected<Profile> profileFromTrace(const Trace &T) { 3270b57cec5SDimitry Andric Profile P; 3280b57cec5SDimitry Andric 3290b57cec5SDimitry Andric // The implementation of the algorithm re-creates the execution of 3300b57cec5SDimitry Andric // the functions based on the trace data. To do this, we set up a number of 3310b57cec5SDimitry Andric // data structures to track the execution context of every thread in the 3320b57cec5SDimitry Andric // Trace. 3330b57cec5SDimitry Andric DenseMap<Profile::ThreadID, std::vector<StackEntry>> ThreadStacks; 3340b57cec5SDimitry Andric DenseMap<Profile::ThreadID, DenseMap<Profile::PathID, Profile::Data>> 3350b57cec5SDimitry Andric ThreadPathData; 3360b57cec5SDimitry Andric 3370b57cec5SDimitry Andric // We then do a pass through the Trace to account data on a per-thread-basis. 3380b57cec5SDimitry Andric for (const auto &E : T) { 3390b57cec5SDimitry Andric auto &TSD = ThreadStacks[E.TId]; 3400b57cec5SDimitry Andric switch (E.Type) { 3410b57cec5SDimitry Andric case RecordTypes::ENTER: 3420b57cec5SDimitry Andric case RecordTypes::ENTER_ARG: 3430b57cec5SDimitry Andric 3440b57cec5SDimitry Andric // Push entries into the function call stack. 3450b57cec5SDimitry Andric TSD.push_back({E.TSC, E.FuncId}); 3460b57cec5SDimitry Andric break; 3470b57cec5SDimitry Andric 3480b57cec5SDimitry Andric case RecordTypes::EXIT: 3490b57cec5SDimitry Andric case RecordTypes::TAIL_EXIT: 3500b57cec5SDimitry Andric 3510b57cec5SDimitry Andric // Exits cause some accounting to happen, based on the state of the stack. 3520b57cec5SDimitry Andric // For each function we pop off the stack, we take note of the path and 3530b57cec5SDimitry Andric // record the cumulative state for this path. As we're doing this, we 3540b57cec5SDimitry Andric // intern the path into the Profile. 3550b57cec5SDimitry Andric while (!TSD.empty()) { 3560b57cec5SDimitry Andric auto Top = TSD.back(); 3570b57cec5SDimitry Andric auto FunctionLocalTime = AbsoluteDifference(Top.Timestamp, E.TSC); 3580b57cec5SDimitry Andric SmallVector<Profile::FuncID, 16> Path; 3590b57cec5SDimitry Andric transform(reverse(TSD), std::back_inserter(Path), 3600b57cec5SDimitry Andric std::mem_fn(&StackEntry::FuncId)); 3610b57cec5SDimitry Andric auto InternedPath = P.internPath(Path); 3620b57cec5SDimitry Andric auto &TPD = ThreadPathData[E.TId][InternedPath]; 3630b57cec5SDimitry Andric ++TPD.CallCount; 3640b57cec5SDimitry Andric TPD.CumulativeLocalTime += FunctionLocalTime; 3650b57cec5SDimitry Andric TSD.pop_back(); 3660b57cec5SDimitry Andric 3670b57cec5SDimitry Andric // If we've matched the corresponding entry event for this function, 3680b57cec5SDimitry Andric // then we exit the loop. 3690b57cec5SDimitry Andric if (Top.FuncId == E.FuncId) 3700b57cec5SDimitry Andric break; 3710b57cec5SDimitry Andric 3720b57cec5SDimitry Andric // FIXME: Consider the intermediate times and the cumulative tree time 3730b57cec5SDimitry Andric // as well. 3740b57cec5SDimitry Andric } 3750b57cec5SDimitry Andric 3760b57cec5SDimitry Andric break; 3770b57cec5SDimitry Andric 3780b57cec5SDimitry Andric case RecordTypes::CUSTOM_EVENT: 3790b57cec5SDimitry Andric case RecordTypes::TYPED_EVENT: 3800b57cec5SDimitry Andric // TODO: Support an extension point to allow handling of custom and typed 3810b57cec5SDimitry Andric // events in profiles. 3820b57cec5SDimitry Andric break; 3830b57cec5SDimitry Andric } 3840b57cec5SDimitry Andric } 3850b57cec5SDimitry Andric 3860b57cec5SDimitry Andric // Once we've gone through the Trace, we now create one Block per thread in 3870b57cec5SDimitry Andric // the Profile. 3880b57cec5SDimitry Andric for (const auto &ThreadPaths : ThreadPathData) { 3890b57cec5SDimitry Andric const auto &TID = ThreadPaths.first; 3900b57cec5SDimitry Andric const auto &PathsData = ThreadPaths.second; 3910b57cec5SDimitry Andric if (auto E = P.addBlock({ 3920b57cec5SDimitry Andric TID, 3930b57cec5SDimitry Andric std::vector<std::pair<Profile::PathID, Profile::Data>>( 3940b57cec5SDimitry Andric PathsData.begin(), PathsData.end()), 3950b57cec5SDimitry Andric })) 3960b57cec5SDimitry Andric return std::move(E); 3970b57cec5SDimitry Andric } 3980b57cec5SDimitry Andric 3990b57cec5SDimitry Andric return P; 4000b57cec5SDimitry Andric } 4010b57cec5SDimitry Andric 4020b57cec5SDimitry Andric } // namespace xray 4030b57cec5SDimitry Andric } // namespace llvm 404