1 //===-- TraceHTR.cpp ------------------------------------------------------===//
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 #include "TraceHTR.h"
10 
11 #include "lldb/Symbol/Function.h"
12 #include "lldb/Target/Process.h"
13 #include "lldb/Target/Target.h"
14 #include "llvm/Support/JSON.h"
15 #include <sstream>
16 #include <string>
17 
18 using namespace lldb_private;
19 using namespace lldb;
20 
21 size_t HTRBlockMetadata::GetNumInstructions() const {
22   return m_num_instructions;
23 }
24 
25 llvm::Optional<llvm::StringRef>
26 HTRBlockMetadata::GetMostFrequentlyCalledFunction() const {
27   size_t max_ncalls = 0;
28   llvm::Optional<llvm::StringRef> max_name = llvm::None;
29   for (const auto &it : m_func_calls) {
30     ConstString name = it.first;
31     size_t ncalls = it.second;
32     if (ncalls > max_ncalls) {
33       max_ncalls = ncalls;
34       max_name = name.GetStringRef();
35     }
36   }
37   return max_name;
38 }
39 
40 llvm::DenseMap<ConstString, size_t> const &
41 HTRBlockMetadata::GetFunctionCalls() const {
42   return m_func_calls;
43 }
44 
45 lldb::addr_t HTRBlockMetadata::GetFirstInstructionLoadAddress() const {
46   return m_first_instruction_load_address;
47 }
48 
49 size_t HTRBlock::GetOffset() const { return m_offset; }
50 
51 size_t HTRBlock::GetSize() const { return m_size; }
52 
53 HTRBlockMetadata const &HTRBlock::GetMetadata() const { return m_metadata; }
54 
55 llvm::ArrayRef<HTRBlockLayerUP> TraceHTR::GetBlockLayers() const {
56   return m_block_layer_ups;
57 }
58 
59 HTRInstructionLayer const &TraceHTR::GetInstructionLayer() const {
60   return *m_instruction_layer_up;
61 }
62 
63 void TraceHTR::AddNewBlockLayer(HTRBlockLayerUP &&block_layer) {
64   m_block_layer_ups.emplace_back(std::move(block_layer));
65 }
66 
67 size_t IHTRLayer::GetLayerId() const { return m_layer_id; }
68 
69 void HTRBlockLayer::AppendNewBlock(size_t block_id, HTRBlock &&block) {
70   m_block_id_trace.emplace_back(block_id);
71   m_block_defs.emplace(block_id, block);
72 }
73 
74 void HTRBlockLayer::AppendRepeatedBlock(size_t block_id) {
75   m_block_id_trace.emplace_back(block_id);
76 }
77 
78 llvm::ArrayRef<lldb::addr_t> HTRInstructionLayer::GetInstructionTrace() const {
79   return m_instruction_trace;
80 }
81 
82 void HTRInstructionLayer::AddCallInstructionMetadata(
83     lldb::addr_t load_addr, llvm::Optional<ConstString> func_name) {
84   m_call_isns.emplace(load_addr, func_name);
85 }
86 
87 void HTRInstructionLayer::AppendInstruction(lldb::addr_t load_addr) {
88   m_instruction_trace.emplace_back(load_addr);
89 }
90 
91 HTRBlock const *HTRBlockLayer::GetBlockById(size_t block_id) const {
92   auto block_it = m_block_defs.find(block_id);
93   if (block_it == m_block_defs.end())
94     return nullptr;
95   else
96     return &block_it->second;
97 }
98 
99 llvm::ArrayRef<size_t> HTRBlockLayer::GetBlockIdTrace() const {
100   return m_block_id_trace;
101 }
102 
103 size_t HTRBlockLayer::GetNumUnits() const { return m_block_id_trace.size(); }
104 
105 HTRBlockMetadata HTRInstructionLayer::GetMetadataByIndex(size_t index) const {
106   lldb::addr_t instruction_load_address = m_instruction_trace[index];
107   llvm::DenseMap<ConstString, size_t> func_calls;
108 
109   auto func_name_it = m_call_isns.find(instruction_load_address);
110   if (func_name_it != m_call_isns.end()) {
111     if (llvm::Optional<ConstString> func_name = func_name_it->second) {
112       func_calls[*func_name] = 1;
113     }
114   }
115   return {instruction_load_address, 1, std::move(func_calls)};
116 }
117 
118 size_t HTRInstructionLayer::GetNumUnits() const {
119   return m_instruction_trace.size();
120 }
121 
122 HTRBlockMetadata HTRBlockLayer::GetMetadataByIndex(size_t index) const {
123   size_t block_id = m_block_id_trace[index];
124   HTRBlock block = m_block_defs.find(block_id)->second;
125   return block.GetMetadata();
126 }
127 
128 TraceHTR::TraceHTR(Thread &thread, TraceCursor &cursor)
129     : m_instruction_layer_up(std::make_unique<HTRInstructionLayer>(0)) {
130 
131   // Move cursor to the first instruction in the trace
132   cursor.SetForwards(true);
133   cursor.Seek(0, TraceCursor::SeekType::Beginning);
134 
135   Target &target = thread.GetProcess()->GetTarget();
136   auto function_name_from_load_address =
137       [&](lldb::addr_t load_address) -> llvm::Optional<ConstString> {
138     lldb_private::Address pc_addr;
139     SymbolContext sc;
140     if (target.ResolveLoadAddress(load_address, pc_addr) &&
141         pc_addr.CalculateSymbolContext(&sc))
142       return sc.GetFunctionName()
143                  ? llvm::Optional<ConstString>(sc.GetFunctionName())
144                  : llvm::None;
145     else
146       return llvm::None;
147   };
148 
149   /* TODO: fix after persona0220's patch on a new way to access instruction
150   kinds while (cursor.HasValue()) { if (cursor.IsError()) {
151       // Append a load address of 0 for all instructions that an error occured
152       // while decoding.
153       // TODO: Make distinction between errors by storing the error messages.
154       // Currently, all errors are treated the same.
155       m_instruction_layer_up->AppendInstruction(0);
156       cursor.Next();
157     } else if (cursor.IsEvent()) {
158       cursor.Next();
159     } else {
160       lldb::addr_t current_instruction_load_address = cursor.GetLoadAddress();
161       lldb::TraceInstructionControlFlowType current_instruction_type =
162           cursor.GetInstructionControlFlowType();
163 
164       m_instruction_layer_up->AppendInstruction(
165           current_instruction_load_address);
166       cursor.Next();
167       bool more_data_in_trace = cursor.HasValue();
168       if (current_instruction_type &
169           lldb::eTraceInstructionControlFlowTypeCall) {
170         if (more_data_in_trace && !cursor.IsError()) {
171           m_instruction_layer_up->AddCallInstructionMetadata(
172               current_instruction_load_address,
173               function_name_from_load_address(cursor.GetLoadAddress()));
174         } else {
175           // Next instruction is not known - pass None to indicate the name
176           // of the function being called is not known
177           m_instruction_layer_up->AddCallInstructionMetadata(
178               current_instruction_load_address, llvm::None);
179         }
180       }
181     }
182   }
183   */
184 }
185 
186 void HTRBlockMetadata::MergeMetadata(
187     HTRBlockMetadata &merged_metadata,
188     HTRBlockMetadata const &metadata_to_merge) {
189   merged_metadata.m_num_instructions += metadata_to_merge.m_num_instructions;
190   for (const auto &it : metadata_to_merge.m_func_calls) {
191     ConstString name = it.first;
192     size_t num_calls = it.second;
193     merged_metadata.m_func_calls[name] += num_calls;
194   }
195 }
196 
197 HTRBlock IHTRLayer::MergeUnits(size_t start_unit_index, size_t num_units) {
198   // TODO: make this function take `end_unit_index` as a parameter instead of
199   // unit and merge the range [start_unit_indx, end_unit_index] inclusive.
200   HTRBlockMetadata merged_metadata = GetMetadataByIndex(start_unit_index);
201   for (size_t i = start_unit_index + 1; i < start_unit_index + num_units; i++) {
202     // merge the new metadata into merged_metadata
203     HTRBlockMetadata::MergeMetadata(merged_metadata, GetMetadataByIndex(i));
204   }
205   return {start_unit_index, num_units, merged_metadata};
206 }
207 
208 void TraceHTR::ExecutePasses() {
209   auto are_passes_done = [](IHTRLayer &l1, IHTRLayer &l2) {
210     return l1.GetNumUnits() == l2.GetNumUnits();
211   };
212   HTRBlockLayerUP current_block_layer_up =
213       BasicSuperBlockMerge(*m_instruction_layer_up);
214   HTRBlockLayer &current_block_layer = *current_block_layer_up;
215   if (are_passes_done(*m_instruction_layer_up, *current_block_layer_up))
216     return;
217 
218   AddNewBlockLayer(std::move(current_block_layer_up));
219   while (true) {
220     HTRBlockLayerUP new_block_layer_up =
221         BasicSuperBlockMerge(current_block_layer);
222     if (are_passes_done(current_block_layer, *new_block_layer_up))
223       return;
224 
225     current_block_layer = *new_block_layer_up;
226     AddNewBlockLayer(std::move(new_block_layer_up));
227   }
228 }
229 
230 llvm::Error TraceHTR::Export(std::string outfile) {
231   std::error_code ec;
232   llvm::raw_fd_ostream os(outfile, ec, llvm::sys::fs::OF_Text);
233   if (ec) {
234     return llvm::make_error<llvm::StringError>(
235         "unable to open destination file: " + outfile, os.error());
236   } else {
237     os << toJSON(*this);
238     os.close();
239     if (os.has_error()) {
240       return llvm::make_error<llvm::StringError>(
241           "unable to write to destination file: " + outfile, os.error());
242     }
243   }
244   return llvm::Error::success();
245 }
246 
247 HTRBlockLayerUP lldb_private::BasicSuperBlockMerge(IHTRLayer &layer) {
248   std::unique_ptr<HTRBlockLayer> new_block_layer =
249       std::make_unique<HTRBlockLayer>(layer.GetLayerId() + 1);
250 
251   if (layer.GetNumUnits()) {
252     // Future Improvement: split this into two functions - one for finding heads
253     // and tails, one for merging/creating the next layer A 'head' is defined to
254     // be a block whose occurrences in the trace do not have a unique preceding
255     // block.
256     std::unordered_set<size_t> heads;
257 
258     // The load address of the first instruction of a block is the unique ID for
259     // that block (i.e. blocks with the same first instruction load address are
260     // the same block)
261 
262     // Future Improvement: no need to store all its preceding block ids, all we
263     // care about is that there is more than one preceding block id, so an enum
264     // could be used
265     std::unordered_map<lldb::addr_t, std::unordered_set<lldb::addr_t>> head_map;
266     lldb::addr_t prev_id =
267         layer.GetMetadataByIndex(0).GetFirstInstructionLoadAddress();
268     size_t num_units = layer.GetNumUnits();
269     // This excludes the first unit since it has no previous unit
270     for (size_t i = 1; i < num_units; i++) {
271       lldb::addr_t current_id =
272           layer.GetMetadataByIndex(i).GetFirstInstructionLoadAddress();
273       head_map[current_id].insert(prev_id);
274       prev_id = current_id;
275     }
276     for (const auto &it : head_map) {
277       // ID of 0 represents an error - errors can't be heads or tails
278       lldb::addr_t id = it.first;
279       const std::unordered_set<lldb::addr_t> predecessor_set = it.second;
280       if (id && predecessor_set.size() > 1)
281         heads.insert(id);
282     }
283 
284     // Future Improvement: identify heads and tails in the same loop
285     // A 'tail' is defined to be a block whose occurrences in the trace do
286     // not have a unique succeeding block.
287     std::unordered_set<lldb::addr_t> tails;
288     std::unordered_map<lldb::addr_t, std::unordered_set<lldb::addr_t>> tail_map;
289 
290     // This excludes the last unit since it has no next unit
291     for (size_t i = 0; i < num_units - 1; i++) {
292       lldb::addr_t current_id =
293           layer.GetMetadataByIndex(i).GetFirstInstructionLoadAddress();
294       lldb::addr_t next_id =
295           layer.GetMetadataByIndex(i + 1).GetFirstInstructionLoadAddress();
296       tail_map[current_id].insert(next_id);
297     }
298 
299     // Mark last block as tail so the algorithm stops gracefully
300     lldb::addr_t last_id = layer.GetMetadataByIndex(num_units - 1)
301                                .GetFirstInstructionLoadAddress();
302     tails.insert(last_id);
303     for (const auto &it : tail_map) {
304       lldb::addr_t id = it.first;
305       const std::unordered_set<lldb::addr_t> successor_set = it.second;
306       // ID of 0 represents an error - errors can't be heads or tails
307       if (id && successor_set.size() > 1)
308         tails.insert(id);
309     }
310 
311     // Need to keep track of size of string since things we push are variable
312     // length
313     size_t superblock_size = 0;
314     // Each super block always has the same first unit (we call this the
315     // super block head) This gurantee allows us to use the super block head as
316     // the unique key mapping to the super block it begins
317     llvm::Optional<size_t> superblock_head = llvm::None;
318     auto construct_next_layer = [&](size_t merge_start, size_t n) -> void {
319       if (!superblock_head)
320         return;
321       if (new_block_layer->GetBlockById(*superblock_head)) {
322         new_block_layer->AppendRepeatedBlock(*superblock_head);
323       } else {
324         HTRBlock new_block = layer.MergeUnits(merge_start, n);
325         new_block_layer->AppendNewBlock(*superblock_head, std::move(new_block));
326       }
327     };
328 
329     for (size_t i = 0; i < num_units; i++) {
330       lldb::addr_t unit_id =
331           layer.GetMetadataByIndex(i).GetFirstInstructionLoadAddress();
332       auto isHead = heads.count(unit_id) > 0;
333       auto isTail = tails.count(unit_id) > 0;
334 
335       if (isHead && isTail) {
336         // Head logic
337         if (superblock_size) { // this handles (tail, head) adjacency -
338                                // otherwise an empty
339                                // block is created
340           // End previous super block
341           construct_next_layer(i - superblock_size, superblock_size);
342         }
343         // Current id is first in next super block since it's a head
344         superblock_head = unit_id;
345         superblock_size = 1;
346 
347         // Tail logic
348         construct_next_layer(i - superblock_size + 1, superblock_size);
349         // Reset the block_head since the prev super block has come to and end
350         superblock_head = llvm::None;
351         superblock_size = 0;
352       } else if (isHead) {
353         if (superblock_size) { // this handles (tail, head) adjacency -
354                                // otherwise an empty
355                                // block is created
356           // End previous super block
357           construct_next_layer(i - superblock_size, superblock_size);
358         }
359         // Current id is first in next super block since it's a head
360         superblock_head = unit_id;
361         superblock_size = 1;
362       } else if (isTail) {
363         if (!superblock_head)
364           superblock_head = unit_id;
365         superblock_size++;
366 
367         // End previous super block
368         construct_next_layer(i - superblock_size + 1, superblock_size);
369         // Reset the block_head since the prev super block has come to and end
370         superblock_head = llvm::None;
371         superblock_size = 0;
372       } else {
373         if (!superblock_head)
374           superblock_head = unit_id;
375         superblock_size++;
376       }
377     }
378   }
379   return new_block_layer;
380 }
381 
382 llvm::json::Value lldb_private::toJSON(const TraceHTR &htr) {
383   std::vector<llvm::json::Value> layers_as_json;
384   for (size_t i = 0; i < htr.GetInstructionLayer().GetInstructionTrace().size();
385        i++) {
386     size_t layer_id = htr.GetInstructionLayer().GetLayerId();
387     HTRBlockMetadata metadata = htr.GetInstructionLayer().GetMetadataByIndex(i);
388     lldb::addr_t load_address = metadata.GetFirstInstructionLoadAddress();
389 
390     std::string display_name;
391 
392     std::stringstream stream;
393     stream << "0x" << std::hex << load_address;
394     std::string load_address_hex_string(stream.str());
395     display_name.assign(load_address_hex_string);
396 
397     // name: load address of the first instruction of the block and the name
398     // of the most frequently called function from the block (if applicable)
399 
400     // ph: the event type - 'X' for Complete events (see link to documentation
401     // below)
402 
403     // Since trace timestamps aren't yet supported in HTR, the ts (timestamp) is
404     // based on the instruction's offset in the trace and the dur (duration) is
405     // 1 since this layer contains single instructions. Using the instruction
406     // offset and a duration of 1 oversimplifies the true timing information of
407     // the trace, nonetheless, these approximate timestamps/durations provide an
408     // clear visualization of the trace.
409 
410     // ts: offset from the beginning of the trace for the first instruction in
411     // the block
412 
413     // dur: 1 since this layer contains single instructions.
414 
415     // pid: the ID of the HTR layer the blocks belong to
416 
417     // See
418     // https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview#heading=h.j75x71ritcoy
419     // for documentation on the Trace Event Format
420     layers_as_json.emplace_back(llvm::json::Object{
421         {"name", display_name},
422         {"ph", "X"},
423         {"ts", (int64_t)i},
424         {"dur", 1},
425         {"pid", (int64_t)layer_id},
426     });
427   }
428 
429   for (const auto &layer : htr.GetBlockLayers()) {
430     size_t start_ts = 0;
431     std::vector<size_t> block_id_trace = layer->GetBlockIdTrace();
432     for (size_t i = 0; i < block_id_trace.size(); i++) {
433       size_t id = block_id_trace[i];
434       // Guranteed that this ID is valid, so safe to dereference here.
435       HTRBlock block = *layer->GetBlockById(id);
436       llvm::json::Value block_json = toJSON(block);
437       size_t layer_id = layer->GetLayerId();
438 
439       HTRBlockMetadata metadata = block.GetMetadata();
440 
441       llvm::Optional<llvm::StringRef> most_freq_func =
442           metadata.GetMostFrequentlyCalledFunction();
443       std::stringstream stream;
444       stream << "0x" << std::hex << metadata.GetFirstInstructionLoadAddress();
445       std::string offset_hex_string(stream.str());
446       std::string display_name =
447           most_freq_func ? offset_hex_string + ": " + most_freq_func->str()
448                          : offset_hex_string;
449 
450       // Since trace timestamps aren't yet supported in HTR, the ts (timestamp)
451       // and dur (duration) are based on the block's offset in the trace and
452       // number of instructions in the block, respectively. Using the block
453       // offset and the number of instructions oversimplifies the true timing
454       // information of the trace, nonetheless, these approximate
455       // timestamps/durations provide an understandable visualization of the
456       // trace.
457       auto duration = metadata.GetNumInstructions();
458       layers_as_json.emplace_back(llvm::json::Object{
459           {"name", display_name},
460           {"ph", "X"},
461           {"ts", (int64_t)start_ts},
462           {"dur", (int64_t)duration},
463           {"pid", (int64_t)layer_id},
464           {"args", block_json},
465       });
466       start_ts += duration;
467     }
468   }
469   return layers_as_json;
470 }
471 
472 llvm::json::Value lldb_private::toJSON(const HTRBlock &block) {
473   return llvm::json::Value(
474       llvm::json::Object{{"Metadata", block.GetMetadata()}});
475 }
476 
477 llvm::json::Value lldb_private::toJSON(const HTRBlockMetadata &metadata) {
478   std::vector<llvm::json::Value> function_calls;
479   for (const auto &it : metadata.GetFunctionCalls()) {
480     ConstString name = it.first;
481     size_t n_calls = it.second;
482     function_calls.emplace_back(llvm::formatv("({0}: {1})", name, n_calls));
483   }
484 
485   return llvm::json::Value(llvm::json::Object{
486       {"Number of Instructions", (ssize_t)metadata.GetNumInstructions()},
487       {"Functions", function_calls}});
488 }
489