1 | //===-- TraceHTR.h --------------------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #ifndef LLDB_TARGET_TRACE_HTR_H |
10 | #define LLDB_TARGET_TRACE_HTR_H |
11 | |
12 | #include "lldb/Target/Thread.h" |
13 | #include "lldb/Target/Trace.h" |
14 | |
15 | #include <optional> |
16 | #include <unordered_map> |
17 | #include <unordered_set> |
18 | |
19 | namespace lldb_private { |
20 | |
21 | /// Metadata associated with an HTR block |
22 | /// See lldb/docs/htr.rst for comprehensive HTR documentation |
23 | class HTRBlockMetadata { |
24 | public: |
25 | /// Constructor for a block's metadata. |
26 | /// |
27 | /// \param[in] first_instruction_load_address |
28 | /// The load address of the block's first instruction. |
29 | /// |
30 | /// \param[in] num_instructions |
31 | /// The total number of instructions in the block. |
32 | /// |
33 | /// \param[in] func_calls |
34 | /// The map of a function name to the number of times it is called from |
35 | /// the block. |
36 | HTRBlockMetadata(lldb::addr_t first_instruction_load_address, |
37 | size_t num_instructions, |
38 | llvm::DenseMap<ConstString, size_t> &&func_calls) |
39 | : m_first_instruction_load_address(first_instruction_load_address), |
40 | m_num_instructions(num_instructions), m_func_calls(func_calls) {} |
41 | |
42 | /// Merge two \a HTRBlockMetadata in place. |
43 | /// |
44 | /// \param[in][out] merged_metadata |
45 | /// Metadata that metadata_to_merge will be merged into. |
46 | /// |
47 | /// \param[in] metadata_to_merge |
48 | /// Metadata to merge into merged_metadata. |
49 | static void MergeMetadata(HTRBlockMetadata &merged_metadata, |
50 | HTRBlockMetadata const &metadata_to_merge); |
51 | /// Get the number of instructions in the block. |
52 | /// |
53 | /// \return |
54 | /// The number of instructions in the block. |
55 | size_t GetNumInstructions() const; |
56 | |
57 | /// Get the name of the most frequently called function from the block. |
58 | /// |
59 | /// \return |
60 | /// The name of the function that is called the most from this block or |
61 | /// std::nullopt if no function is called from this block. |
62 | std::optional<llvm::StringRef> GetMostFrequentlyCalledFunction() const; |
63 | |
64 | /// Get the load address of the first instruction in the block. |
65 | /// |
66 | /// \return |
67 | /// The load address of the first instruction in the block. |
68 | lldb::addr_t GetFirstInstructionLoadAddress() const; |
69 | |
70 | /// Get the function calls map for the block. |
71 | /// Function calls are identified in the instruction layer by finding 'call' |
72 | /// instructions and determining the function they are calling. As these |
73 | /// instructions are merged into blocks, we merge these different function |
74 | /// calls into a single map containing the function names to the number of |
75 | /// times it is called from this block. |
76 | /// |
77 | /// \return |
78 | /// The mapping of function name to the number of times it is called from |
79 | /// this block. |
80 | llvm::DenseMap<ConstString, size_t> const &GetFunctionCalls() const; |
81 | |
82 | private: |
83 | lldb::addr_t m_first_instruction_load_address; |
84 | size_t m_num_instructions; |
85 | llvm::DenseMap<ConstString, size_t> m_func_calls; |
86 | }; |
87 | |
88 | /// Block structure representing a sequence of trace "units" (ie instructions). |
89 | /// Sequences of blocks are merged to create a new, single block |
90 | /// See lldb/docs/htr.rst for comprehensive HTR documentation |
91 | class HTRBlock { |
92 | public: |
93 | /// Constructor for a block of an HTR layer. |
94 | /// |
95 | /// \param[in] offset |
96 | /// The offset of the start of this block in the previous layer. |
97 | /// |
98 | /// \param[in] size |
99 | /// Number of blocks/instructions that make up this block in the previous |
100 | /// layer. |
101 | /// |
102 | /// \param[in] metadata |
103 | /// General metadata for this block. |
104 | HTRBlock(size_t offset, size_t size, HTRBlockMetadata metadata) |
105 | : m_offset(offset), m_size(size), m_metadata(metadata) {} |
106 | |
107 | /// Get the offset of the start of this block in the previous layer. |
108 | /// |
109 | /// \return |
110 | /// The offset of the block. |
111 | size_t GetOffset() const; |
112 | |
113 | /// Get the number of blocks/instructions that make up this block in the |
114 | /// previous layer. |
115 | /// |
116 | /// \return |
117 | /// The size of the block. |
118 | size_t GetSize() const; |
119 | |
120 | /// Get the metadata for this block. |
121 | /// |
122 | /// \return |
123 | /// The metadata of the block. |
124 | HTRBlockMetadata const &GetMetadata() const; |
125 | |
126 | private: |
127 | /// Offset in the previous layer |
128 | size_t m_offset; |
129 | /// Number of blocks/instructions that make up this block in the previous |
130 | /// layer |
131 | size_t m_size; |
132 | /// General metadata for this block |
133 | HTRBlockMetadata m_metadata; |
134 | }; |
135 | |
136 | /// HTR layer interface |
137 | /// See lldb/docs/htr.rst for comprehensive HTR documentation |
138 | class IHTRLayer { |
139 | public: |
140 | /// Construct new HTR layer. |
141 | // |
142 | /// \param[in] id |
143 | /// The layer's id. |
144 | IHTRLayer(size_t id) : m_layer_id(id) {} |
145 | |
146 | /// Get the ID of the layer. |
147 | /// |
148 | /// \return |
149 | /// The layer ID of this layer. |
150 | size_t GetLayerId() const; |
151 | |
152 | /// Get the metadata of a unit (instruction or block) in the layer. |
153 | /// |
154 | /// \param[in] index |
155 | /// The position of the unit in the layer. |
156 | /// |
157 | /// \return |
158 | /// The metadata of the unit in the layer. |
159 | virtual HTRBlockMetadata GetMetadataByIndex(size_t index) const = 0; |
160 | |
161 | /// Get the total number of units (instruction or block) in this layer. |
162 | /// |
163 | /// \return |
164 | /// The total number of units in the layer. |
165 | virtual size_t GetNumUnits() const = 0; |
166 | |
167 | /// Creates a new block from the result of merging a contiguous sequence of |
168 | /// "units" (instructions or blocks depending on layer type) in this layer |
169 | /// This allows the implementation class to decide how to store/generate this |
170 | /// metadata. For example, in the case of the instruction layer we want to |
171 | /// lazily generate this metadata instead of storing it for each instruction. |
172 | /// |
173 | /// \param[in] start_unit_index |
174 | /// The index of the first unit to be merged. |
175 | /// |
176 | /// \param[in] num_units |
177 | /// The number of units to be merged. Must be >= 1, since merging 0 blocks |
178 | /// does not make sense. |
179 | /// |
180 | /// \return |
181 | /// A new block instance representing the merge of the specified units. |
182 | HTRBlock MergeUnits(size_t start_unit_index, size_t num_units); |
183 | |
184 | virtual ~IHTRLayer() = default; |
185 | |
186 | protected: |
187 | /// ID of the layer. |
188 | size_t m_layer_id; |
189 | }; |
190 | |
191 | /// "Base" layer of HTR representing the dynamic instructions of the trace. |
192 | /// See lldb/docs/htr.rst for comprehensive HTR documentation |
193 | class HTRInstructionLayer : public IHTRLayer { |
194 | public: |
195 | /// Construct new instruction layer. |
196 | // |
197 | /// \param[in] id |
198 | /// The layer's id. |
199 | HTRInstructionLayer(size_t id) : IHTRLayer(id) {} |
200 | |
201 | size_t GetNumUnits() const override; |
202 | |
203 | HTRBlockMetadata GetMetadataByIndex(size_t index) const override; |
204 | |
205 | /// Get the dynamic instruction trace. |
206 | /// |
207 | /// \return |
208 | /// The dynamic instruction trace. |
209 | llvm::ArrayRef<lldb::addr_t> GetInstructionTrace() const; |
210 | |
211 | /// Add metadata for a 'call' instruction of the trace. |
212 | /// |
213 | /// \param[in] load_addr |
214 | /// The load address of the 'call' instruction. |
215 | /// |
216 | /// \param[in] func_name |
217 | /// The name of the function the 'call' instruction is calling if it can |
218 | /// be determined, std::nullopt otherwise. |
219 | void AddCallInstructionMetadata(lldb::addr_t load_addr, |
220 | std::optional<ConstString> func_name); |
221 | |
222 | /// Append the load address of an instruction to the dynamic instruction |
223 | /// trace. |
224 | /// |
225 | /// \param[in] load_addr |
226 | /// The load address of the instruction. |
227 | void AppendInstruction(lldb::addr_t load_addr); |
228 | |
229 | private: |
230 | // Dynamic instructions of trace are stored in chronological order. |
231 | std::vector<lldb::addr_t> m_instruction_trace; |
232 | // Only store metadata for instructions of interest (call instructions) |
233 | // If we stored metadata for each instruction this would be wasteful since |
234 | // most instructions don't contain useful metadata |
235 | |
236 | // This map contains the load address of all the call instructions. |
237 | // load address maps to the name of the function it calls (std::nullopt if |
238 | // function name can't be determined) |
239 | std::unordered_map<lldb::addr_t, std::optional<ConstString>> m_call_isns; |
240 | }; |
241 | |
242 | /// HTR layer composed of blocks of the trace. |
243 | /// See lldb/docs/htr.rst for comprehensive HTR documentation |
244 | class HTRBlockLayer : public IHTRLayer { |
245 | public: |
246 | /// Construct new block layer. |
247 | // |
248 | /// \param[in] id |
249 | /// The layer's id. |
250 | HTRBlockLayer(size_t id) : IHTRLayer(id) {} |
251 | |
252 | size_t GetNumUnits() const override; |
253 | |
254 | HTRBlockMetadata GetMetadataByIndex(size_t index) const override; |
255 | |
256 | /// Get an \a HTRBlock from its block id. |
257 | /// |
258 | /// \param[in] block_id |
259 | /// The id of the block to retrieve. |
260 | /// |
261 | /// \return |
262 | /// The \a HTRBlock with the specified id, nullptr if no there is no block |
263 | /// in the layer with the specified block id. |
264 | HTRBlock const *GetBlockById(size_t block_id) const; |
265 | |
266 | /// Get the block ID trace for this layer. |
267 | /// This block ID trace stores the block ID of each block that occured in the |
268 | /// trace and the block defs map maps block ID to the corresponding \a |
269 | /// HTRBlock. |
270 | /// |
271 | /// \return |
272 | /// The block ID trace for this layer. |
273 | llvm::ArrayRef<size_t> GetBlockIdTrace() const; |
274 | |
275 | /// Appends a new block to the layer. |
276 | /// |
277 | /// \param[in] block_id |
278 | /// The block id of the new block. |
279 | /// |
280 | /// \param[in] block |
281 | /// The new \a HTRBlock to be appended to the layer. This block is moved |
282 | /// into the layer. |
283 | void AppendNewBlock(size_t block_id, HTRBlock &&block); |
284 | |
285 | /// Appends a repeated block to the layer. |
286 | /// |
287 | /// \param[in] block_id |
288 | /// The block id of the repeated block. |
289 | void AppendRepeatedBlock(size_t block_id); |
290 | |
291 | private: |
292 | /// Maps a unique Block ID to the corresponding HTRBlock |
293 | std::unordered_map<size_t, HTRBlock> m_block_defs; |
294 | /// Reduce memory footprint by just storing a trace of block IDs and use |
295 | /// m_block_defs to map a block_id to its corresponding HTRBlock |
296 | std::vector<size_t> m_block_id_trace; |
297 | }; |
298 | |
299 | typedef std::unique_ptr<lldb_private::HTRBlockLayer> HTRBlockLayerUP; |
300 | typedef std::unique_ptr<lldb_private::HTRInstructionLayer> |
301 | HTRInstructionLayerUP; |
302 | |
303 | /// Top-level HTR class |
304 | /// See lldb/docs/htr.rst for comprehensive HTR documentation |
305 | class TraceHTR { |
306 | |
307 | public: |
308 | /// Constructor for a trace's HTR. |
309 | /// |
310 | /// \param[in] thread |
311 | /// The thread the trace belongs to. |
312 | /// |
313 | /// \param[in] cursor |
314 | /// The trace cursor that gives access to the trace's contents. |
315 | TraceHTR(Thread &thread, TraceCursor &cursor); |
316 | |
317 | /// Executes passes on the HTR layers until no further |
318 | /// summarization/compression is achieved |
319 | void ExecutePasses(); |
320 | |
321 | /// Export HTR layers to the specified format and outfile. |
322 | /// |
323 | /// \param[in] outfile |
324 | /// The file that the exported HTR data will be written to. |
325 | /// |
326 | /// \return |
327 | /// Success if the export is successful, Error otherwise. |
328 | llvm::Error Export(std::string outfile); |
329 | |
330 | /// Get the block layers of this HTR. |
331 | /// |
332 | /// \return |
333 | /// The block layers of this HTR. |
334 | llvm::ArrayRef<HTRBlockLayerUP> GetBlockLayers() const; |
335 | |
336 | /// Get the instruction layer of this HTR. |
337 | /// |
338 | /// \return |
339 | /// The instruction layer of this HTR. |
340 | HTRInstructionLayer const &GetInstructionLayer() const; |
341 | |
342 | /// Add a new block layer to this HTR. |
343 | /// |
344 | /// \param[in] |
345 | /// The new block layer to be added. |
346 | void AddNewBlockLayer(HTRBlockLayerUP &&block_layer); |
347 | |
348 | private: |
349 | // There is a single instruction layer per HTR |
350 | HTRInstructionLayerUP m_instruction_layer_up; |
351 | // There are one or more block layers per HTR |
352 | std::vector<HTRBlockLayerUP> m_block_layer_ups; |
353 | }; |
354 | |
355 | // Serialization functions for exporting HTR to Chrome Trace Format |
356 | llvm::json::Value toJSON(const TraceHTR &htr); |
357 | llvm::json::Value toJSON(const HTRBlock &block); |
358 | llvm::json::Value toJSON(const HTRBlockMetadata &metadata); |
359 | |
360 | /// The HTR passes are defined below: |
361 | |
362 | /// Creates a new layer by merging the "basic super blocks" in the current layer |
363 | /// |
364 | /// A "basic super block" is the longest sequence of blocks that always occur in |
365 | /// the same order. (The concept is akin to “Basic Block" in compiler theory, |
366 | /// but refers to dynamic occurrences rather than CFG nodes) |
367 | /// |
368 | /// Procedure to find all basic super blocks: |
369 | // |
370 | /// - For each block, compute the number of distinct predecessor and |
371 | /// successor blocks. |
372 | /// Predecessor - the block that occurs directly before (to the left of) |
373 | /// the current block Successor - the block that occurs directly after |
374 | /// (to the right of) the current block |
375 | /// - A block with more than one distinct successor is always the start of a |
376 | /// super block, the super block will continue until the next block with |
377 | /// more than one distinct predecessor or successor. |
378 | /// |
379 | /// The implementation makes use of two terms - 'heads' and 'tails' known as |
380 | /// the 'endpoints' of a basic super block: |
381 | /// A 'head' is defined to be a block in the trace that doesn't have a |
382 | /// unique predecessor |
383 | /// A 'tail' is defined to be a block in the trace that doesn't have a |
384 | /// unique successor |
385 | /// |
386 | /// A basic super block is defined to be a sequence of blocks between two |
387 | /// endpoints |
388 | /// |
389 | /// A head represents the start of the next group, so the current group |
390 | /// ends at the block preceding the head and the next group begins with |
391 | /// this head block |
392 | /// |
393 | /// A tail represents the end of the current group, so the current group |
394 | /// ends with the tail block and the next group begins with the |
395 | /// following block. |
396 | /// |
397 | /// See lldb/docs/htr.rst for comprehensive HTR documentation |
398 | /// |
399 | /// \param[in] layer |
400 | /// The layer to execute the pass on. |
401 | /// |
402 | /// \return |
403 | /// A new layer instance representing the merge of blocks in the |
404 | /// previous layer |
405 | HTRBlockLayerUP BasicSuperBlockMerge(IHTRLayer &layer); |
406 | |
407 | } // namespace lldb_private |
408 | |
409 | #endif // LLDB_TARGET_TRACE_HTR_H |
410 | |