xref: /llvm-project/llvm/lib/XRay/Profile.cpp (revision ddaa93b095288ce459be4096193c40a4ea247b11)
1f6c87eb9SDean Michael Berris //===- Profile.cpp - XRay Profile Abstraction -----------------------------===//
2f6c87eb9SDean Michael Berris //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6f6c87eb9SDean Michael Berris //
7f6c87eb9SDean Michael Berris //===----------------------------------------------------------------------===//
8f6c87eb9SDean Michael Berris //
9f6c87eb9SDean Michael Berris // Defines the XRay Profile class representing the latency profile generated by
10f6c87eb9SDean Michael Berris // XRay's profiling mode.
11f6c87eb9SDean Michael Berris //
12f6c87eb9SDean Michael Berris //===----------------------------------------------------------------------===//
13f6c87eb9SDean Michael Berris #include "llvm/XRay/Profile.h"
14f6c87eb9SDean Michael Berris 
15f6c87eb9SDean Michael Berris #include "llvm/Support/DataExtractor.h"
16f6c87eb9SDean Michael Berris #include "llvm/Support/Error.h"
17f6c87eb9SDean Michael Berris #include "llvm/Support/FileSystem.h"
18f6c87eb9SDean Michael Berris #include "llvm/XRay/Trace.h"
19f6c87eb9SDean Michael Berris #include <deque>
20f6c87eb9SDean Michael Berris #include <memory>
21f6c87eb9SDean Michael Berris 
22f6c87eb9SDean Michael Berris namespace llvm {
23f6c87eb9SDean Michael Berris namespace xray {
24f6c87eb9SDean Michael Berris 
Profile(const Profile & O)25f6c87eb9SDean Michael Berris Profile::Profile(const Profile &O) {
26f6c87eb9SDean Michael Berris   // We need to re-create all the tries from the original (O), into the current
27f6c87eb9SDean Michael Berris   // Profile being initialized, through the Block instances we see.
28f6c87eb9SDean Michael Berris   for (const auto &Block : O) {
29f6c87eb9SDean Michael Berris     Blocks.push_back({Block.Thread, {}});
30f6c87eb9SDean Michael Berris     auto &B = Blocks.back();
31f6c87eb9SDean Michael Berris     for (const auto &PathData : Block.PathData)
32f6c87eb9SDean Michael Berris       B.PathData.push_back({internPath(cantFail(O.expandPath(PathData.first))),
33f6c87eb9SDean Michael Berris                             PathData.second});
34f6c87eb9SDean Michael Berris   }
35f6c87eb9SDean Michael Berris }
36f6c87eb9SDean Michael Berris 
operator =(const Profile & O)37f6c87eb9SDean Michael Berris Profile &Profile::operator=(const Profile &O) {
38f6c87eb9SDean Michael Berris   Profile P = O;
39f6c87eb9SDean Michael Berris   *this = std::move(P);
40f6c87eb9SDean Michael Berris   return *this;
41f6c87eb9SDean Michael Berris }
42f6c87eb9SDean Michael Berris 
43f6c87eb9SDean Michael Berris namespace {
44f6c87eb9SDean Michael Berris 
45f6c87eb9SDean Michael Berris struct BlockHeader {
46f6c87eb9SDean Michael Berris   uint32_t Size;
47f6c87eb9SDean Michael Berris   uint32_t Number;
48f6c87eb9SDean Michael Berris   uint64_t Thread;
49f6c87eb9SDean Michael Berris };
50f6c87eb9SDean Michael Berris 
readBlockHeader(DataExtractor & Extractor,uint64_t & Offset)51f6c87eb9SDean Michael Berris static Expected<BlockHeader> readBlockHeader(DataExtractor &Extractor,
52f26a70a5SIgor Kudrin                                              uint64_t &Offset) {
53f6c87eb9SDean Michael Berris   BlockHeader H;
54f26a70a5SIgor Kudrin   uint64_t CurrentOffset = Offset;
55f6c87eb9SDean Michael Berris   H.Size = Extractor.getU32(&Offset);
56f6c87eb9SDean Michael Berris   if (Offset == CurrentOffset)
57f6c87eb9SDean Michael Berris     return make_error<StringError>(
58f6c87eb9SDean Michael Berris         Twine("Error parsing block header size at offset '") +
59f6c87eb9SDean Michael Berris             Twine(CurrentOffset) + "'",
60f6c87eb9SDean Michael Berris         std::make_error_code(std::errc::invalid_argument));
61f6c87eb9SDean Michael Berris   CurrentOffset = Offset;
62f6c87eb9SDean Michael Berris   H.Number = Extractor.getU32(&Offset);
63f6c87eb9SDean Michael Berris   if (Offset == CurrentOffset)
64f6c87eb9SDean Michael Berris     return make_error<StringError>(
65f6c87eb9SDean Michael Berris         Twine("Error parsing block header number at offset '") +
66f6c87eb9SDean Michael Berris             Twine(CurrentOffset) + "'",
67f6c87eb9SDean Michael Berris         std::make_error_code(std::errc::invalid_argument));
68f6c87eb9SDean Michael Berris   CurrentOffset = Offset;
69f6c87eb9SDean Michael Berris   H.Thread = Extractor.getU64(&Offset);
70f6c87eb9SDean Michael Berris   if (Offset == CurrentOffset)
71f6c87eb9SDean Michael Berris     return make_error<StringError>(
72f6c87eb9SDean Michael Berris         Twine("Error parsing block header thread id at offset '") +
73f6c87eb9SDean Michael Berris             Twine(CurrentOffset) + "'",
74f6c87eb9SDean Michael Berris         std::make_error_code(std::errc::invalid_argument));
75f6c87eb9SDean Michael Berris   return H;
76f6c87eb9SDean Michael Berris }
77f6c87eb9SDean Michael Berris 
readPath(DataExtractor & Extractor,uint64_t & Offset)78f6c87eb9SDean Michael Berris static Expected<std::vector<Profile::FuncID>> readPath(DataExtractor &Extractor,
79f26a70a5SIgor Kudrin                                                        uint64_t &Offset) {
80f6c87eb9SDean Michael Berris   // We're reading a sequence of int32_t's until we find a 0.
81f6c87eb9SDean Michael Berris   std::vector<Profile::FuncID> Path;
82f6c87eb9SDean Michael Berris   auto CurrentOffset = Offset;
83f6c87eb9SDean Michael Berris   int32_t FuncId;
84f6c87eb9SDean Michael Berris   do {
85f6c87eb9SDean Michael Berris     FuncId = Extractor.getSigned(&Offset, 4);
86f6c87eb9SDean Michael Berris     if (CurrentOffset == Offset)
87f6c87eb9SDean Michael Berris       return make_error<StringError>(
88f6c87eb9SDean Michael Berris           Twine("Error parsing path at offset '") + Twine(CurrentOffset) + "'",
89f6c87eb9SDean Michael Berris           std::make_error_code(std::errc::invalid_argument));
90f6c87eb9SDean Michael Berris     CurrentOffset = Offset;
91f6c87eb9SDean Michael Berris     Path.push_back(FuncId);
92f6c87eb9SDean Michael Berris   } while (FuncId != 0);
93c55cf4afSBill Wendling   return std::move(Path);
94f6c87eb9SDean Michael Berris }
95f6c87eb9SDean Michael Berris 
readData(DataExtractor & Extractor,uint64_t & Offset)96f6c87eb9SDean Michael Berris static Expected<Profile::Data> readData(DataExtractor &Extractor,
97f26a70a5SIgor Kudrin                                         uint64_t &Offset) {
98f6c87eb9SDean Michael Berris   // We expect a certain number of elements for Data:
99f6c87eb9SDean Michael Berris   //   - A 64-bit CallCount
100f6c87eb9SDean Michael Berris   //   - A 64-bit CumulativeLocalTime counter
101f6c87eb9SDean Michael Berris   Profile::Data D;
102f6c87eb9SDean Michael Berris   auto CurrentOffset = Offset;
103f6c87eb9SDean Michael Berris   D.CallCount = Extractor.getU64(&Offset);
104f6c87eb9SDean Michael Berris   if (CurrentOffset == Offset)
105f6c87eb9SDean Michael Berris     return make_error<StringError>(
106f6c87eb9SDean Michael Berris         Twine("Error parsing call counts at offset '") + Twine(CurrentOffset) +
107f6c87eb9SDean Michael Berris             "'",
108f6c87eb9SDean Michael Berris         std::make_error_code(std::errc::invalid_argument));
109f6c87eb9SDean Michael Berris   CurrentOffset = Offset;
110f6c87eb9SDean Michael Berris   D.CumulativeLocalTime = Extractor.getU64(&Offset);
111f6c87eb9SDean Michael Berris   if (CurrentOffset == Offset)
112f6c87eb9SDean Michael Berris     return make_error<StringError>(
113f6c87eb9SDean Michael Berris         Twine("Error parsing cumulative local time at offset '") +
114f6c87eb9SDean Michael Berris             Twine(CurrentOffset) + "'",
115f6c87eb9SDean Michael Berris         std::make_error_code(std::errc::invalid_argument));
116f6c87eb9SDean Michael Berris   return D;
117f6c87eb9SDean Michael Berris }
118f6c87eb9SDean Michael Berris 
119f6c87eb9SDean Michael Berris } // namespace
120f6c87eb9SDean Michael Berris 
addBlock(Block && B)121f6c87eb9SDean Michael Berris Error Profile::addBlock(Block &&B) {
122f6c87eb9SDean Michael Berris   if (B.PathData.empty())
123f6c87eb9SDean Michael Berris     return make_error<StringError>(
124f6c87eb9SDean Michael Berris         "Block may not have empty path data.",
125f6c87eb9SDean Michael Berris         std::make_error_code(std::errc::invalid_argument));
126f6c87eb9SDean Michael Berris 
127f6c87eb9SDean Michael Berris   Blocks.emplace_back(std::move(B));
128f6c87eb9SDean Michael Berris   return Error::success();
129f6c87eb9SDean Michael Berris }
130f6c87eb9SDean Michael Berris 
expandPath(PathID P) const131f6c87eb9SDean Michael Berris Expected<std::vector<Profile::FuncID>> Profile::expandPath(PathID P) const {
132f6c87eb9SDean Michael Berris   auto It = PathIDMap.find(P);
133f6c87eb9SDean Michael Berris   if (It == PathIDMap.end())
134f6c87eb9SDean Michael Berris     return make_error<StringError>(
135f6c87eb9SDean Michael Berris         Twine("PathID not found: ") + Twine(P),
136f6c87eb9SDean Michael Berris         std::make_error_code(std::errc::invalid_argument));
137f6c87eb9SDean Michael Berris   std::vector<Profile::FuncID> Path;
138f6c87eb9SDean Michael Berris   for (auto Node = It->second; Node; Node = Node->Caller)
139f6c87eb9SDean Michael Berris     Path.push_back(Node->Func);
140c55cf4afSBill Wendling   return std::move(Path);
141f6c87eb9SDean Michael Berris }
142f6c87eb9SDean Michael Berris 
internPath(ArrayRef<FuncID> P)143f6c87eb9SDean Michael Berris Profile::PathID Profile::internPath(ArrayRef<FuncID> P) {
144f6c87eb9SDean Michael Berris   if (P.empty())
145f6c87eb9SDean Michael Berris     return 0;
146f6c87eb9SDean Michael Berris 
147f6c87eb9SDean Michael Berris   auto RootToLeafPath = reverse(P);
148f6c87eb9SDean Michael Berris 
149f6c87eb9SDean Michael Berris   // Find the root.
150f6c87eb9SDean Michael Berris   auto It = RootToLeafPath.begin();
151f6c87eb9SDean Michael Berris   auto PathRoot = *It++;
152f6c87eb9SDean Michael Berris   auto RootIt =
153f6c87eb9SDean Michael Berris       find_if(Roots, [PathRoot](TrieNode *N) { return N->Func == PathRoot; });
154f6c87eb9SDean Michael Berris 
155f6c87eb9SDean Michael Berris   // If we've not seen this root before, remember it.
156f6c87eb9SDean Michael Berris   TrieNode *Node = nullptr;
157f6c87eb9SDean Michael Berris   if (RootIt == Roots.end()) {
158f6c87eb9SDean Michael Berris     NodeStorage.emplace_back();
159f6c87eb9SDean Michael Berris     Node = &NodeStorage.back();
160f6c87eb9SDean Michael Berris     Node->Func = PathRoot;
161f6c87eb9SDean Michael Berris     Roots.push_back(Node);
162f6c87eb9SDean Michael Berris   } else {
163f6c87eb9SDean Michael Berris     Node = *RootIt;
164f6c87eb9SDean Michael Berris   }
165f6c87eb9SDean Michael Berris 
166f6c87eb9SDean Michael Berris   // Now traverse the path, re-creating if necessary.
167f6c87eb9SDean Michael Berris   while (It != RootToLeafPath.end()) {
168f6c87eb9SDean Michael Berris     auto NodeFuncID = *It++;
169f6c87eb9SDean Michael Berris     auto CalleeIt = find_if(Node->Callees, [NodeFuncID](TrieNode *N) {
170f6c87eb9SDean Michael Berris       return N->Func == NodeFuncID;
171f6c87eb9SDean Michael Berris     });
172f6c87eb9SDean Michael Berris     if (CalleeIt == Node->Callees.end()) {
173f6c87eb9SDean Michael Berris       NodeStorage.emplace_back();
174f6c87eb9SDean Michael Berris       auto NewNode = &NodeStorage.back();
175f6c87eb9SDean Michael Berris       NewNode->Func = NodeFuncID;
176f6c87eb9SDean Michael Berris       NewNode->Caller = Node;
177f6c87eb9SDean Michael Berris       Node->Callees.push_back(NewNode);
178f6c87eb9SDean Michael Berris       Node = NewNode;
179f6c87eb9SDean Michael Berris     } else {
180f6c87eb9SDean Michael Berris       Node = *CalleeIt;
181f6c87eb9SDean Michael Berris     }
182f6c87eb9SDean Michael Berris   }
183f6c87eb9SDean Michael Berris 
184f6c87eb9SDean Michael Berris   // At this point, Node *must* be pointing at the leaf.
185f6c87eb9SDean Michael Berris   assert(Node->Func == P.front());
186f6c87eb9SDean Michael Berris   if (Node->ID == 0) {
187f6c87eb9SDean Michael Berris     Node->ID = NextID++;
188f6c87eb9SDean Michael Berris     PathIDMap.insert({Node->ID, Node});
189f6c87eb9SDean Michael Berris   }
190f6c87eb9SDean Michael Berris   return Node->ID;
191f6c87eb9SDean Michael Berris }
192f6c87eb9SDean Michael Berris 
mergeProfilesByThread(const Profile & L,const Profile & R)193f6c87eb9SDean Michael Berris Profile mergeProfilesByThread(const Profile &L, const Profile &R) {
194f6c87eb9SDean Michael Berris   Profile Merged;
195f6c87eb9SDean Michael Berris   using PathDataMap = DenseMap<Profile::PathID, Profile::Data>;
196f6c87eb9SDean Michael Berris   using PathDataMapPtr = std::unique_ptr<PathDataMap>;
197f6c87eb9SDean Michael Berris   using PathDataVector = decltype(Profile::Block::PathData);
198f6c87eb9SDean Michael Berris   using ThreadProfileIndexMap = DenseMap<Profile::ThreadID, PathDataMapPtr>;
199f6c87eb9SDean Michael Berris   ThreadProfileIndexMap ThreadProfileIndex;
200f6c87eb9SDean Michael Berris 
201f6c87eb9SDean Michael Berris   for (const auto &P : {std::ref(L), std::ref(R)})
202f6c87eb9SDean Michael Berris     for (const auto &Block : P.get()) {
203f6c87eb9SDean Michael Berris       ThreadProfileIndexMap::iterator It;
204f6c87eb9SDean Michael Berris       std::tie(It, std::ignore) = ThreadProfileIndex.insert(
205*ddaa93b0SKazu Hirata           {Block.Thread, std::make_unique<PathDataMap>()});
206f6c87eb9SDean Michael Berris       for (const auto &PathAndData : Block.PathData) {
207f6c87eb9SDean Michael Berris         auto &PathID = PathAndData.first;
208f6c87eb9SDean Michael Berris         auto &Data = PathAndData.second;
209f6c87eb9SDean Michael Berris         auto NewPathID =
210f6c87eb9SDean Michael Berris             Merged.internPath(cantFail(P.get().expandPath(PathID)));
211f6c87eb9SDean Michael Berris         PathDataMap::iterator PathDataIt;
212f6c87eb9SDean Michael Berris         bool Inserted;
213f6c87eb9SDean Michael Berris         std::tie(PathDataIt, Inserted) = It->second->insert({NewPathID, Data});
214f6c87eb9SDean Michael Berris         if (!Inserted) {
215f6c87eb9SDean Michael Berris           auto &ExistingData = PathDataIt->second;
216f6c87eb9SDean Michael Berris           ExistingData.CallCount += Data.CallCount;
217f6c87eb9SDean Michael Berris           ExistingData.CumulativeLocalTime += Data.CumulativeLocalTime;
218f6c87eb9SDean Michael Berris         }
219f6c87eb9SDean Michael Berris       }
220f6c87eb9SDean Michael Berris     }
221f6c87eb9SDean Michael Berris 
222f6c87eb9SDean Michael Berris   for (const auto &IndexedThreadBlock : ThreadProfileIndex) {
223f6c87eb9SDean Michael Berris     PathDataVector PathAndData;
224f6c87eb9SDean Michael Berris     PathAndData.reserve(IndexedThreadBlock.second->size());
225f6c87eb9SDean Michael Berris     copy(*IndexedThreadBlock.second, std::back_inserter(PathAndData));
226f6c87eb9SDean Michael Berris     cantFail(
227f6c87eb9SDean Michael Berris         Merged.addBlock({IndexedThreadBlock.first, std::move(PathAndData)}));
228f6c87eb9SDean Michael Berris   }
229f6c87eb9SDean Michael Berris   return Merged;
230f6c87eb9SDean Michael Berris }
231f6c87eb9SDean Michael Berris 
mergeProfilesByStack(const Profile & L,const Profile & R)232f6c87eb9SDean Michael Berris Profile mergeProfilesByStack(const Profile &L, const Profile &R) {
233f6c87eb9SDean Michael Berris   Profile Merged;
234f6c87eb9SDean Michael Berris   using PathDataMap = DenseMap<Profile::PathID, Profile::Data>;
235f6c87eb9SDean Michael Berris   PathDataMap PathData;
236f6c87eb9SDean Michael Berris   using PathDataVector = decltype(Profile::Block::PathData);
237f6c87eb9SDean Michael Berris   for (const auto &P : {std::ref(L), std::ref(R)})
238f6c87eb9SDean Michael Berris     for (const auto &Block : P.get())
239f6c87eb9SDean Michael Berris       for (const auto &PathAndData : Block.PathData) {
240f6c87eb9SDean Michael Berris         auto &PathId = PathAndData.first;
241f6c87eb9SDean Michael Berris         auto &Data = PathAndData.second;
242f6c87eb9SDean Michael Berris         auto NewPathID =
243f6c87eb9SDean Michael Berris             Merged.internPath(cantFail(P.get().expandPath(PathId)));
244f6c87eb9SDean Michael Berris         PathDataMap::iterator PathDataIt;
245f6c87eb9SDean Michael Berris         bool Inserted;
246f6c87eb9SDean Michael Berris         std::tie(PathDataIt, Inserted) = PathData.insert({NewPathID, Data});
247f6c87eb9SDean Michael Berris         if (!Inserted) {
248f6c87eb9SDean Michael Berris           auto &ExistingData = PathDataIt->second;
249f6c87eb9SDean Michael Berris           ExistingData.CallCount += Data.CallCount;
250f6c87eb9SDean Michael Berris           ExistingData.CumulativeLocalTime += Data.CumulativeLocalTime;
251f6c87eb9SDean Michael Berris         }
252f6c87eb9SDean Michael Berris       }
253f6c87eb9SDean Michael Berris 
254f6c87eb9SDean Michael Berris   // In the end there's a single Block, for thread 0.
255f6c87eb9SDean Michael Berris   PathDataVector Block;
256f6c87eb9SDean Michael Berris   Block.reserve(PathData.size());
257f6c87eb9SDean Michael Berris   copy(PathData, std::back_inserter(Block));
258f6c87eb9SDean Michael Berris   cantFail(Merged.addBlock({0, std::move(Block)}));
259f6c87eb9SDean Michael Berris   return Merged;
260f6c87eb9SDean Michael Berris }
261f6c87eb9SDean Michael Berris 
loadProfile(StringRef Filename)262f6c87eb9SDean Michael Berris Expected<Profile> loadProfile(StringRef Filename) {
263f002fcb2SReid Kleckner   Expected<sys::fs::file_t> FdOrErr = sys::fs::openNativeFileForRead(Filename);
264f002fcb2SReid Kleckner   if (!FdOrErr)
265f002fcb2SReid Kleckner     return FdOrErr.takeError();
266f6c87eb9SDean Michael Berris 
267f6c87eb9SDean Michael Berris   uint64_t FileSize;
268f6c87eb9SDean Michael Berris   if (auto EC = sys::fs::file_size(Filename, FileSize))
269f6c87eb9SDean Michael Berris     return make_error<StringError>(
270f6c87eb9SDean Michael Berris         Twine("Cannot get filesize of '") + Filename + "'", EC);
271f6c87eb9SDean Michael Berris 
272f6c87eb9SDean Michael Berris   std::error_code EC;
273f6c87eb9SDean Michael Berris   sys::fs::mapped_file_region MappedFile(
274f002fcb2SReid Kleckner       *FdOrErr, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0,
275f002fcb2SReid Kleckner       EC);
276f002fcb2SReid Kleckner   sys::fs::closeFile(*FdOrErr);
277f6c87eb9SDean Michael Berris   if (EC)
278f6c87eb9SDean Michael Berris     return make_error<StringError>(
279f6c87eb9SDean Michael Berris         Twine("Cannot mmap profile '") + Filename + "'", EC);
280f6c87eb9SDean Michael Berris   StringRef Data(MappedFile.data(), MappedFile.size());
281f6c87eb9SDean Michael Berris 
282f6c87eb9SDean Michael Berris   Profile P;
283f26a70a5SIgor Kudrin   uint64_t Offset = 0;
284f6c87eb9SDean Michael Berris   DataExtractor Extractor(Data, true, 8);
285f6c87eb9SDean Michael Berris 
286f6c87eb9SDean Michael Berris   // For each block we get from the file:
287f6c87eb9SDean Michael Berris   while (Offset != MappedFile.size()) {
288f6c87eb9SDean Michael Berris     auto HeaderOrError = readBlockHeader(Extractor, Offset);
289f6c87eb9SDean Michael Berris     if (!HeaderOrError)
290f6c87eb9SDean Michael Berris       return HeaderOrError.takeError();
291f6c87eb9SDean Michael Berris 
292f6c87eb9SDean Michael Berris     // TODO: Maybe store this header information for each block, even just for
293f6c87eb9SDean Michael Berris     // debugging?
294f6c87eb9SDean Michael Berris     const auto &Header = HeaderOrError.get();
295f6c87eb9SDean Michael Berris 
296f6c87eb9SDean Michael Berris     // Read in the path data.
297f6c87eb9SDean Michael Berris     auto PathOrError = readPath(Extractor, Offset);
298f6c87eb9SDean Michael Berris     if (!PathOrError)
299f6c87eb9SDean Michael Berris       return PathOrError.takeError();
300f6c87eb9SDean Michael Berris     const auto &Path = PathOrError.get();
301f6c87eb9SDean Michael Berris 
302f6c87eb9SDean Michael Berris     // For each path we encounter, we should intern it to get a PathID.
303f6c87eb9SDean Michael Berris     auto DataOrError = readData(Extractor, Offset);
304f6c87eb9SDean Michael Berris     if (!DataOrError)
305f6c87eb9SDean Michael Berris       return DataOrError.takeError();
306f6c87eb9SDean Michael Berris     auto &Data = DataOrError.get();
307f6c87eb9SDean Michael Berris 
308f6c87eb9SDean Michael Berris     if (auto E =
309f6c87eb9SDean Michael Berris             P.addBlock(Profile::Block{Profile::ThreadID{Header.Thread},
310f6c87eb9SDean Michael Berris                                       {{P.internPath(Path), std::move(Data)}}}))
311c55cf4afSBill Wendling       return std::move(E);
312f6c87eb9SDean Michael Berris   }
313f6c87eb9SDean Michael Berris 
314f6c87eb9SDean Michael Berris   return P;
315f6c87eb9SDean Michael Berris }
316f6c87eb9SDean Michael Berris 
317f6c87eb9SDean Michael Berris namespace {
318f6c87eb9SDean Michael Berris 
319f6c87eb9SDean Michael Berris struct StackEntry {
320f6c87eb9SDean Michael Berris   uint64_t Timestamp;
321f6c87eb9SDean Michael Berris   Profile::FuncID FuncId;
322f6c87eb9SDean Michael Berris };
323f6c87eb9SDean Michael Berris 
324f6c87eb9SDean Michael Berris } // namespace
325f6c87eb9SDean Michael Berris 
profileFromTrace(const Trace & T)326f6c87eb9SDean Michael Berris Expected<Profile> profileFromTrace(const Trace &T) {
327f6c87eb9SDean Michael Berris   Profile P;
328f6c87eb9SDean Michael Berris 
329f6c87eb9SDean Michael Berris   // The implementation of the algorithm re-creates the execution of
330f6c87eb9SDean Michael Berris   // the functions based on the trace data. To do this, we set up a number of
331f6c87eb9SDean Michael Berris   // data structures to track the execution context of every thread in the
332f6c87eb9SDean Michael Berris   // Trace.
333f6c87eb9SDean Michael Berris   DenseMap<Profile::ThreadID, std::vector<StackEntry>> ThreadStacks;
334f6c87eb9SDean Michael Berris   DenseMap<Profile::ThreadID, DenseMap<Profile::PathID, Profile::Data>>
335f6c87eb9SDean Michael Berris       ThreadPathData;
336f6c87eb9SDean Michael Berris 
337f6c87eb9SDean Michael Berris   //  We then do a pass through the Trace to account data on a per-thread-basis.
338f6c87eb9SDean Michael Berris   for (const auto &E : T) {
339f6c87eb9SDean Michael Berris     auto &TSD = ThreadStacks[E.TId];
340f6c87eb9SDean Michael Berris     switch (E.Type) {
341f6c87eb9SDean Michael Berris     case RecordTypes::ENTER:
342f6c87eb9SDean Michael Berris     case RecordTypes::ENTER_ARG:
343f6c87eb9SDean Michael Berris 
344f6c87eb9SDean Michael Berris       // Push entries into the function call stack.
345f6c87eb9SDean Michael Berris       TSD.push_back({E.TSC, E.FuncId});
346f6c87eb9SDean Michael Berris       break;
347f6c87eb9SDean Michael Berris 
348f6c87eb9SDean Michael Berris     case RecordTypes::EXIT:
349f6c87eb9SDean Michael Berris     case RecordTypes::TAIL_EXIT:
350f6c87eb9SDean Michael Berris 
351f6c87eb9SDean Michael Berris       // Exits cause some accounting to happen, based on the state of the stack.
352f6c87eb9SDean Michael Berris       // For each function we pop off the stack, we take note of the path and
353f6c87eb9SDean Michael Berris       // record the cumulative state for this path. As we're doing this, we
354f6c87eb9SDean Michael Berris       // intern the path into the Profile.
355f6c87eb9SDean Michael Berris       while (!TSD.empty()) {
356f6c87eb9SDean Michael Berris         auto Top = TSD.back();
357f6c87eb9SDean Michael Berris         auto FunctionLocalTime = AbsoluteDifference(Top.Timestamp, E.TSC);
358f6c87eb9SDean Michael Berris         SmallVector<Profile::FuncID, 16> Path;
359f6c87eb9SDean Michael Berris         transform(reverse(TSD), std::back_inserter(Path),
360f6c87eb9SDean Michael Berris                   std::mem_fn(&StackEntry::FuncId));
361f6c87eb9SDean Michael Berris         auto InternedPath = P.internPath(Path);
362f6c87eb9SDean Michael Berris         auto &TPD = ThreadPathData[E.TId][InternedPath];
363f6c87eb9SDean Michael Berris         ++TPD.CallCount;
364f6c87eb9SDean Michael Berris         TPD.CumulativeLocalTime += FunctionLocalTime;
365f6c87eb9SDean Michael Berris         TSD.pop_back();
366f6c87eb9SDean Michael Berris 
367f6c87eb9SDean Michael Berris         // If we've matched the corresponding entry event for this function,
368f6c87eb9SDean Michael Berris         // then we exit the loop.
369f6c87eb9SDean Michael Berris         if (Top.FuncId == E.FuncId)
370f6c87eb9SDean Michael Berris           break;
371f6c87eb9SDean Michael Berris 
372f6c87eb9SDean Michael Berris         // FIXME: Consider the intermediate times and the cumulative tree time
373f6c87eb9SDean Michael Berris         // as well.
374f6c87eb9SDean Michael Berris       }
375f6c87eb9SDean Michael Berris 
376f6c87eb9SDean Michael Berris       break;
37725f8d204SDean Michael Berris 
37825f8d204SDean Michael Berris     case RecordTypes::CUSTOM_EVENT:
37925f8d204SDean Michael Berris     case RecordTypes::TYPED_EVENT:
38025f8d204SDean Michael Berris       // TODO: Support an extension point to allow handling of custom and typed
38125f8d204SDean Michael Berris       // events in profiles.
38225f8d204SDean Michael Berris       break;
383f6c87eb9SDean Michael Berris     }
384f6c87eb9SDean Michael Berris   }
385f6c87eb9SDean Michael Berris 
386f6c87eb9SDean Michael Berris   // Once we've gone through the Trace, we now create one Block per thread in
387f6c87eb9SDean Michael Berris   // the Profile.
388f6c87eb9SDean Michael Berris   for (const auto &ThreadPaths : ThreadPathData) {
389f6c87eb9SDean Michael Berris     const auto &TID = ThreadPaths.first;
390f6c87eb9SDean Michael Berris     const auto &PathsData = ThreadPaths.second;
391f6c87eb9SDean Michael Berris     if (auto E = P.addBlock({
392f6c87eb9SDean Michael Berris             TID,
393f6c87eb9SDean Michael Berris             std::vector<std::pair<Profile::PathID, Profile::Data>>(
394f6c87eb9SDean Michael Berris                 PathsData.begin(), PathsData.end()),
395f6c87eb9SDean Michael Berris         }))
396c55cf4afSBill Wendling       return std::move(E);
397f6c87eb9SDean Michael Berris   }
398f6c87eb9SDean Michael Berris 
399f6c87eb9SDean Michael Berris   return P;
400f6c87eb9SDean Michael Berris }
401f6c87eb9SDean Michael Berris 
402f6c87eb9SDean Michael Berris } // namespace xray
403f6c87eb9SDean Michael Berris } // namespace llvm
404