xref: /freebsd-src/contrib/llvm-project/llvm/lib/XRay/Profile.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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