Path: blob/main/contrib/llvm-project/lldb/source/Plugins/TraceExporter/common/TraceHTR.cpp
39648 views
//===-- TraceHTR.cpp ------------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "TraceHTR.h"910#include "lldb/Symbol/Function.h"11#include "lldb/Target/Process.h"12#include "lldb/Target/Target.h"13#include "llvm/Support/JSON.h"14#include <optional>15#include <sstream>16#include <string>1718using namespace lldb_private;19using namespace lldb;2021size_t HTRBlockMetadata::GetNumInstructions() const {22return m_num_instructions;23}2425std::optional<llvm::StringRef>26HTRBlockMetadata::GetMostFrequentlyCalledFunction() const {27size_t max_ncalls = 0;28std::optional<llvm::StringRef> max_name;29for (const auto &it : m_func_calls) {30ConstString name = it.first;31size_t ncalls = it.second;32if (ncalls > max_ncalls) {33max_ncalls = ncalls;34max_name = name.GetStringRef();35}36}37return max_name;38}3940llvm::DenseMap<ConstString, size_t> const &41HTRBlockMetadata::GetFunctionCalls() const {42return m_func_calls;43}4445lldb::addr_t HTRBlockMetadata::GetFirstInstructionLoadAddress() const {46return m_first_instruction_load_address;47}4849size_t HTRBlock::GetOffset() const { return m_offset; }5051size_t HTRBlock::GetSize() const { return m_size; }5253HTRBlockMetadata const &HTRBlock::GetMetadata() const { return m_metadata; }5455llvm::ArrayRef<HTRBlockLayerUP> TraceHTR::GetBlockLayers() const {56return m_block_layer_ups;57}5859HTRInstructionLayer const &TraceHTR::GetInstructionLayer() const {60return *m_instruction_layer_up;61}6263void TraceHTR::AddNewBlockLayer(HTRBlockLayerUP &&block_layer) {64m_block_layer_ups.emplace_back(std::move(block_layer));65}6667size_t IHTRLayer::GetLayerId() const { return m_layer_id; }6869void HTRBlockLayer::AppendNewBlock(size_t block_id, HTRBlock &&block) {70m_block_id_trace.emplace_back(block_id);71m_block_defs.emplace(block_id, std::move(block));72}7374void HTRBlockLayer::AppendRepeatedBlock(size_t block_id) {75m_block_id_trace.emplace_back(block_id);76}7778llvm::ArrayRef<lldb::addr_t> HTRInstructionLayer::GetInstructionTrace() const {79return m_instruction_trace;80}8182void HTRInstructionLayer::AddCallInstructionMetadata(83lldb::addr_t load_addr, std::optional<ConstString> func_name) {84m_call_isns.emplace(load_addr, func_name);85}8687void HTRInstructionLayer::AppendInstruction(lldb::addr_t load_addr) {88m_instruction_trace.emplace_back(load_addr);89}9091HTRBlock const *HTRBlockLayer::GetBlockById(size_t block_id) const {92auto block_it = m_block_defs.find(block_id);93if (block_it == m_block_defs.end())94return nullptr;95else96return &block_it->second;97}9899llvm::ArrayRef<size_t> HTRBlockLayer::GetBlockIdTrace() const {100return m_block_id_trace;101}102103size_t HTRBlockLayer::GetNumUnits() const { return m_block_id_trace.size(); }104105HTRBlockMetadata HTRInstructionLayer::GetMetadataByIndex(size_t index) const {106lldb::addr_t instruction_load_address = m_instruction_trace[index];107llvm::DenseMap<ConstString, size_t> func_calls;108109auto func_name_it = m_call_isns.find(instruction_load_address);110if (func_name_it != m_call_isns.end()) {111if (std::optional<ConstString> func_name = func_name_it->second) {112func_calls[*func_name] = 1;113}114}115return {instruction_load_address, 1, std::move(func_calls)};116}117118size_t HTRInstructionLayer::GetNumUnits() const {119return m_instruction_trace.size();120}121122HTRBlockMetadata HTRBlockLayer::GetMetadataByIndex(size_t index) const {123size_t block_id = m_block_id_trace[index];124HTRBlock block = m_block_defs.find(block_id)->second;125return block.GetMetadata();126}127128TraceHTR::TraceHTR(Thread &thread, TraceCursor &cursor)129: m_instruction_layer_up(std::make_unique<HTRInstructionLayer>(0)) {130131// Move cursor to the first instruction in the trace132cursor.SetForwards(true);133cursor.Seek(0, lldb::eTraceCursorSeekTypeBeginning);134135// TODO: fix after persona0220's patch on a new way to access instruction136// kinds137/*138Target &target = thread.GetProcess()->GetTarget();139auto function_name_from_load_address =140[&](lldb::addr_t load_address) -> std::optional<ConstString> {141lldb_private::Address pc_addr;142SymbolContext sc;143if (target.ResolveLoadAddress(load_address, pc_addr) &&144pc_addr.CalculateSymbolContext(&sc))145return sc.GetFunctionName()146? std::optional<ConstString>(sc.GetFunctionName())147: std::nullopt;148else149return std::nullopt;150};151152while (cursor.HasValue()) { if (cursor.IsError()) {153// Append a load address of 0 for all instructions that an error occured154// while decoding.155// TODO: Make distinction between errors by storing the error messages.156// Currently, all errors are treated the same.157m_instruction_layer_up->AppendInstruction(0);158cursor.Next();159} else if (cursor.IsEvent()) {160cursor.Next();161} else {162lldb::addr_t current_instruction_load_address = cursor.GetLoadAddress();163lldb::InstructionControlFlowKind current_instruction_type =164cursor.GetInstructionControlFlowKind();165166m_instruction_layer_up->AppendInstruction(167current_instruction_load_address);168cursor.Next();169bool more_data_in_trace = cursor.HasValue();170if (current_instruction_type &171lldb::eInstructionControlFlowKindCall) {172if (more_data_in_trace && !cursor.IsError()) {173m_instruction_layer_up->AddCallInstructionMetadata(174current_instruction_load_address,175function_name_from_load_address(cursor.GetLoadAddress()));176} else {177// Next instruction is not known - pass None to indicate the name178// of the function being called is not known179m_instruction_layer_up->AddCallInstructionMetadata(180current_instruction_load_address, std::nullopt);181}182}183}184}185*/186}187188void HTRBlockMetadata::MergeMetadata(189HTRBlockMetadata &merged_metadata,190HTRBlockMetadata const &metadata_to_merge) {191merged_metadata.m_num_instructions += metadata_to_merge.m_num_instructions;192for (const auto &it : metadata_to_merge.m_func_calls) {193ConstString name = it.first;194size_t num_calls = it.second;195merged_metadata.m_func_calls[name] += num_calls;196}197}198199HTRBlock IHTRLayer::MergeUnits(size_t start_unit_index, size_t num_units) {200// TODO: make this function take `end_unit_index` as a parameter instead of201// unit and merge the range [start_unit_indx, end_unit_index] inclusive.202HTRBlockMetadata merged_metadata = GetMetadataByIndex(start_unit_index);203for (size_t i = start_unit_index + 1; i < start_unit_index + num_units; i++) {204// merge the new metadata into merged_metadata205HTRBlockMetadata::MergeMetadata(merged_metadata, GetMetadataByIndex(i));206}207return {start_unit_index, num_units, merged_metadata};208}209210void TraceHTR::ExecutePasses() {211auto are_passes_done = [](IHTRLayer &l1, IHTRLayer &l2) {212return l1.GetNumUnits() == l2.GetNumUnits();213};214HTRBlockLayerUP current_block_layer_up =215BasicSuperBlockMerge(*m_instruction_layer_up);216HTRBlockLayer ¤t_block_layer = *current_block_layer_up;217if (are_passes_done(*m_instruction_layer_up, *current_block_layer_up))218return;219220AddNewBlockLayer(std::move(current_block_layer_up));221while (true) {222HTRBlockLayerUP new_block_layer_up =223BasicSuperBlockMerge(current_block_layer);224if (are_passes_done(current_block_layer, *new_block_layer_up))225return;226227current_block_layer = *new_block_layer_up;228AddNewBlockLayer(std::move(new_block_layer_up));229}230}231232llvm::Error TraceHTR::Export(std::string outfile) {233std::error_code ec;234llvm::raw_fd_ostream os(outfile, ec, llvm::sys::fs::OF_Text);235if (ec) {236return llvm::make_error<llvm::StringError>(237"unable to open destination file: " + outfile, os.error());238} else {239os << toJSON(*this);240os.close();241if (os.has_error()) {242return llvm::make_error<llvm::StringError>(243"unable to write to destination file: " + outfile, os.error());244}245}246return llvm::Error::success();247}248249HTRBlockLayerUP lldb_private::BasicSuperBlockMerge(IHTRLayer &layer) {250std::unique_ptr<HTRBlockLayer> new_block_layer =251std::make_unique<HTRBlockLayer>(layer.GetLayerId() + 1);252253if (layer.GetNumUnits()) {254// Future Improvement: split this into two functions - one for finding heads255// and tails, one for merging/creating the next layer A 'head' is defined to256// be a block whose occurrences in the trace do not have a unique preceding257// block.258std::unordered_set<size_t> heads;259260// The load address of the first instruction of a block is the unique ID for261// that block (i.e. blocks with the same first instruction load address are262// the same block)263264// Future Improvement: no need to store all its preceding block ids, all we265// care about is that there is more than one preceding block id, so an enum266// could be used267std::unordered_map<lldb::addr_t, std::unordered_set<lldb::addr_t>> head_map;268lldb::addr_t prev_id =269layer.GetMetadataByIndex(0).GetFirstInstructionLoadAddress();270size_t num_units = layer.GetNumUnits();271// This excludes the first unit since it has no previous unit272for (size_t i = 1; i < num_units; i++) {273lldb::addr_t current_id =274layer.GetMetadataByIndex(i).GetFirstInstructionLoadAddress();275head_map[current_id].insert(prev_id);276prev_id = current_id;277}278for (const auto &it : head_map) {279// ID of 0 represents an error - errors can't be heads or tails280lldb::addr_t id = it.first;281const std::unordered_set<lldb::addr_t> predecessor_set = it.second;282if (id && predecessor_set.size() > 1)283heads.insert(id);284}285286// Future Improvement: identify heads and tails in the same loop287// A 'tail' is defined to be a block whose occurrences in the trace do288// not have a unique succeeding block.289std::unordered_set<lldb::addr_t> tails;290std::unordered_map<lldb::addr_t, std::unordered_set<lldb::addr_t>> tail_map;291292// This excludes the last unit since it has no next unit293for (size_t i = 0; i < num_units - 1; i++) {294lldb::addr_t current_id =295layer.GetMetadataByIndex(i).GetFirstInstructionLoadAddress();296lldb::addr_t next_id =297layer.GetMetadataByIndex(i + 1).GetFirstInstructionLoadAddress();298tail_map[current_id].insert(next_id);299}300301// Mark last block as tail so the algorithm stops gracefully302lldb::addr_t last_id = layer.GetMetadataByIndex(num_units - 1)303.GetFirstInstructionLoadAddress();304tails.insert(last_id);305for (const auto &it : tail_map) {306lldb::addr_t id = it.first;307const std::unordered_set<lldb::addr_t> successor_set = it.second;308// ID of 0 represents an error - errors can't be heads or tails309if (id && successor_set.size() > 1)310tails.insert(id);311}312313// Need to keep track of size of string since things we push are variable314// length315size_t superblock_size = 0;316// Each super block always has the same first unit (we call this the317// super block head) This gurantee allows us to use the super block head as318// the unique key mapping to the super block it begins319std::optional<size_t> superblock_head;320auto construct_next_layer = [&](size_t merge_start, size_t n) -> void {321if (!superblock_head)322return;323if (new_block_layer->GetBlockById(*superblock_head)) {324new_block_layer->AppendRepeatedBlock(*superblock_head);325} else {326HTRBlock new_block = layer.MergeUnits(merge_start, n);327new_block_layer->AppendNewBlock(*superblock_head, std::move(new_block));328}329};330331for (size_t i = 0; i < num_units; i++) {332lldb::addr_t unit_id =333layer.GetMetadataByIndex(i).GetFirstInstructionLoadAddress();334auto isHead = heads.count(unit_id) > 0;335auto isTail = tails.count(unit_id) > 0;336337if (isHead && isTail) {338// Head logic339if (superblock_size) { // this handles (tail, head) adjacency -340// otherwise an empty341// block is created342// End previous super block343construct_next_layer(i - superblock_size, superblock_size);344}345// Current id is first in next super block since it's a head346superblock_head = unit_id;347superblock_size = 1;348349// Tail logic350construct_next_layer(i - superblock_size + 1, superblock_size);351// Reset the block_head since the prev super block has come to and end352superblock_head = std::nullopt;353superblock_size = 0;354} else if (isHead) {355if (superblock_size) { // this handles (tail, head) adjacency -356// otherwise an empty357// block is created358// End previous super block359construct_next_layer(i - superblock_size, superblock_size);360}361// Current id is first in next super block since it's a head362superblock_head = unit_id;363superblock_size = 1;364} else if (isTail) {365if (!superblock_head)366superblock_head = unit_id;367superblock_size++;368369// End previous super block370construct_next_layer(i - superblock_size + 1, superblock_size);371// Reset the block_head since the prev super block has come to and end372superblock_head = std::nullopt;373superblock_size = 0;374} else {375if (!superblock_head)376superblock_head = unit_id;377superblock_size++;378}379}380}381return new_block_layer;382}383384llvm::json::Value lldb_private::toJSON(const TraceHTR &htr) {385std::vector<llvm::json::Value> layers_as_json;386for (size_t i = 0; i < htr.GetInstructionLayer().GetInstructionTrace().size();387i++) {388size_t layer_id = htr.GetInstructionLayer().GetLayerId();389HTRBlockMetadata metadata = htr.GetInstructionLayer().GetMetadataByIndex(i);390lldb::addr_t load_address = metadata.GetFirstInstructionLoadAddress();391392std::string display_name;393394std::stringstream stream;395stream << "0x" << std::hex << load_address;396std::string load_address_hex_string(stream.str());397display_name.assign(load_address_hex_string);398399// name: load address of the first instruction of the block and the name400// of the most frequently called function from the block (if applicable)401402// ph: the event type - 'X' for Complete events (see link to documentation403// below)404405// Since trace timestamps aren't yet supported in HTR, the ts (timestamp) is406// based on the instruction's offset in the trace and the dur (duration) is407// 1 since this layer contains single instructions. Using the instruction408// offset and a duration of 1 oversimplifies the true timing information of409// the trace, nonetheless, these approximate timestamps/durations provide an410// clear visualization of the trace.411412// ts: offset from the beginning of the trace for the first instruction in413// the block414415// dur: 1 since this layer contains single instructions.416417// pid: the ID of the HTR layer the blocks belong to418419// See420// https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview#heading=h.j75x71ritcoy421// for documentation on the Trace Event Format422layers_as_json.emplace_back(llvm::json::Object{423{"name", display_name},424{"ph", "X"},425{"ts", (int64_t)i},426{"dur", 1},427{"pid", (int64_t)layer_id},428});429}430431for (const auto &layer : htr.GetBlockLayers()) {432size_t start_ts = 0;433std::vector<size_t> block_id_trace = layer->GetBlockIdTrace();434for (size_t i = 0; i < block_id_trace.size(); i++) {435size_t id = block_id_trace[i];436// Guranteed that this ID is valid, so safe to dereference here.437HTRBlock block = *layer->GetBlockById(id);438llvm::json::Value block_json = toJSON(block);439size_t layer_id = layer->GetLayerId();440441HTRBlockMetadata metadata = block.GetMetadata();442443std::optional<llvm::StringRef> most_freq_func =444metadata.GetMostFrequentlyCalledFunction();445std::stringstream stream;446stream << "0x" << std::hex << metadata.GetFirstInstructionLoadAddress();447std::string offset_hex_string(stream.str());448std::string display_name =449most_freq_func ? offset_hex_string + ": " + most_freq_func->str()450: offset_hex_string;451452// Since trace timestamps aren't yet supported in HTR, the ts (timestamp)453// and dur (duration) are based on the block's offset in the trace and454// number of instructions in the block, respectively. Using the block455// offset and the number of instructions oversimplifies the true timing456// information of the trace, nonetheless, these approximate457// timestamps/durations provide an understandable visualization of the458// trace.459auto duration = metadata.GetNumInstructions();460layers_as_json.emplace_back(llvm::json::Object{461{"name", display_name},462{"ph", "X"},463{"ts", (int64_t)start_ts},464{"dur", (int64_t)duration},465{"pid", (int64_t)layer_id},466{"args", block_json},467});468start_ts += duration;469}470}471return layers_as_json;472}473474llvm::json::Value lldb_private::toJSON(const HTRBlock &block) {475return llvm::json::Value(476llvm::json::Object{{"Metadata", block.GetMetadata()}});477}478479llvm::json::Value lldb_private::toJSON(const HTRBlockMetadata &metadata) {480std::vector<llvm::json::Value> function_calls;481for (const auto &it : metadata.GetFunctionCalls()) {482ConstString name = it.first;483size_t n_calls = it.second;484function_calls.emplace_back(llvm::formatv("({0}: {1})", name, n_calls));485}486487return llvm::json::Value(llvm::json::Object{488{"Number of Instructions", (ssize_t)metadata.GetNumInstructions()},489{"Functions", function_calls}});490}491492493