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