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 ¤t_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