Path: blob/main/contrib/llvm-project/lldb/source/Symbol/LineTable.cpp
39587 views
//===-- LineTable.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 "lldb/Symbol/LineTable.h"9#include "lldb/Core/Address.h"10#include "lldb/Core/Module.h"11#include "lldb/Core/Section.h"12#include "lldb/Symbol/CompileUnit.h"13#include "lldb/Utility/Stream.h"14#include <algorithm>1516using namespace lldb;17using namespace lldb_private;1819// LineTable constructor20LineTable::LineTable(CompileUnit *comp_unit)21: m_comp_unit(comp_unit), m_entries() {}2223LineTable::LineTable(CompileUnit *comp_unit,24std::vector<std::unique_ptr<LineSequence>> &&sequences)25: m_comp_unit(comp_unit), m_entries() {26LineTable::Entry::LessThanBinaryPredicate less_than_bp(this);27llvm::stable_sort(sequences, less_than_bp);28for (const auto &sequence : sequences) {29LineSequenceImpl *seq = static_cast<LineSequenceImpl *>(sequence.get());30m_entries.insert(m_entries.end(), seq->m_entries.begin(),31seq->m_entries.end());32}33}3435// Destructor36LineTable::~LineTable() = default;3738void LineTable::InsertLineEntry(lldb::addr_t file_addr, uint32_t line,39uint16_t column, uint16_t file_idx,40bool is_start_of_statement,41bool is_start_of_basic_block,42bool is_prologue_end, bool is_epilogue_begin,43bool is_terminal_entry) {44Entry entry(file_addr, line, column, file_idx, is_start_of_statement,45is_start_of_basic_block, is_prologue_end, is_epilogue_begin,46is_terminal_entry);4748LineTable::Entry::LessThanBinaryPredicate less_than_bp(this);49entry_collection::iterator pos =50llvm::upper_bound(m_entries, entry, less_than_bp);5152// Stream s(stdout);53// s << "\n\nBefore:\n";54// Dump (&s, Address::DumpStyleFileAddress);55m_entries.insert(pos, entry);56// s << "After:\n";57// Dump (&s, Address::DumpStyleFileAddress);58}5960LineSequence::LineSequence() = default;6162void LineTable::LineSequenceImpl::Clear() { m_entries.clear(); }6364std::unique_ptr<LineSequence> LineTable::CreateLineSequenceContainer() {65return std::make_unique<LineTable::LineSequenceImpl>();66}6768void LineTable::AppendLineEntryToSequence(69LineSequence *sequence, lldb::addr_t file_addr, uint32_t line,70uint16_t column, uint16_t file_idx, bool is_start_of_statement,71bool is_start_of_basic_block, bool is_prologue_end, bool is_epilogue_begin,72bool is_terminal_entry) {73assert(sequence != nullptr);74LineSequenceImpl *seq = reinterpret_cast<LineSequenceImpl *>(sequence);75Entry entry(file_addr, line, column, file_idx, is_start_of_statement,76is_start_of_basic_block, is_prologue_end, is_epilogue_begin,77is_terminal_entry);78entry_collection &entries = seq->m_entries;79// Replace the last entry if the address is the same, otherwise append it. If80// we have multiple line entries at the same address, this indicates illegal81// DWARF so this "fixes" the line table to be correct. If not fixed this can82// cause a line entry's address that when resolved back to a symbol context,83// could resolve to a different line entry. We really want a84// 1 to 1 mapping85// here to avoid these kinds of inconsistencies. We will need tor revisit86// this if the DWARF line tables are updated to allow multiple entries at the87// same address legally.88if (!entries.empty() && entries.back().file_addr == file_addr) {89// GCC don't use the is_prologue_end flag to mark the first instruction90// after the prologue.91// Instead of it is issuing a line table entry for the first instruction92// of the prologue and one for the first instruction after the prologue. If93// the size of the prologue is 0 instruction then the 2 line entry will94// have the same file address. Removing it will remove our ability to95// properly detect the location of the end of prologe so we set the96// prologue_end flag to preserve this information (setting the prologue_end97// flag for an entry what is after the prologue end don't have any effect)98entry.is_prologue_end = entry.file_idx == entries.back().file_idx;99entries.back() = entry;100} else101entries.push_back(entry);102}103104void LineTable::InsertSequence(LineSequence *sequence) {105assert(sequence != nullptr);106LineSequenceImpl *seq = reinterpret_cast<LineSequenceImpl *>(sequence);107if (seq->m_entries.empty())108return;109Entry &entry = seq->m_entries.front();110111// If the first entry address in this sequence is greater than or equal to112// the address of the last item in our entry collection, just append.113if (m_entries.empty() ||114!Entry::EntryAddressLessThan(entry, m_entries.back())) {115m_entries.insert(m_entries.end(), seq->m_entries.begin(),116seq->m_entries.end());117return;118}119120// Otherwise, find where this belongs in the collection121entry_collection::iterator begin_pos = m_entries.begin();122entry_collection::iterator end_pos = m_entries.end();123LineTable::Entry::LessThanBinaryPredicate less_than_bp(this);124entry_collection::iterator pos =125upper_bound(begin_pos, end_pos, entry, less_than_bp);126127// We should never insert a sequence in the middle of another sequence128if (pos != begin_pos) {129while (pos < end_pos && !((pos - 1)->is_terminal_entry))130pos++;131}132133#ifndef NDEBUG134// If we aren't inserting at the beginning, the previous entry should135// terminate a sequence.136if (pos != begin_pos) {137entry_collection::iterator prev_pos = pos - 1;138assert(prev_pos->is_terminal_entry);139}140#endif141m_entries.insert(pos, seq->m_entries.begin(), seq->m_entries.end());142}143144LineTable::Entry::LessThanBinaryPredicate::LessThanBinaryPredicate(145LineTable *line_table)146: m_line_table(line_table) {}147148bool LineTable::Entry::LessThanBinaryPredicate::149operator()(const LineTable::Entry &a, const LineTable::Entry &b) const {150#define LT_COMPARE(a, b) \151if (a != b) \152return a < b153LT_COMPARE(a.file_addr, b.file_addr);154// b and a reversed on purpose below.155LT_COMPARE(b.is_terminal_entry, a.is_terminal_entry);156LT_COMPARE(a.line, b.line);157LT_COMPARE(a.column, b.column);158LT_COMPARE(a.is_start_of_statement, b.is_start_of_statement);159LT_COMPARE(a.is_start_of_basic_block, b.is_start_of_basic_block);160// b and a reversed on purpose below.161LT_COMPARE(b.is_prologue_end, a.is_prologue_end);162LT_COMPARE(a.is_epilogue_begin, b.is_epilogue_begin);163LT_COMPARE(a.file_idx, b.file_idx);164return false;165#undef LT_COMPARE166}167168bool LineTable::Entry::LessThanBinaryPredicate::169operator()(const std::unique_ptr<LineSequence> &sequence_a,170const std::unique_ptr<LineSequence> &sequence_b) const {171auto *seq_a = static_cast<const LineSequenceImpl *>(sequence_a.get());172auto *seq_b = static_cast<const LineSequenceImpl *>(sequence_b.get());173return (*this)(seq_a->m_entries.front(), seq_b->m_entries.front());174}175176uint32_t LineTable::GetSize() const { return m_entries.size(); }177178bool LineTable::GetLineEntryAtIndex(uint32_t idx, LineEntry &line_entry) {179if (idx < m_entries.size()) {180ConvertEntryAtIndexToLineEntry(idx, line_entry);181return true;182}183line_entry.Clear();184return false;185}186187bool LineTable::FindLineEntryByAddress(const Address &so_addr,188LineEntry &line_entry,189uint32_t *index_ptr) {190if (index_ptr != nullptr)191*index_ptr = UINT32_MAX;192193bool success = false;194195if (so_addr.GetModule().get() == m_comp_unit->GetModule().get()) {196Entry search_entry;197search_entry.file_addr = so_addr.GetFileAddress();198if (search_entry.file_addr != LLDB_INVALID_ADDRESS) {199entry_collection::const_iterator begin_pos = m_entries.begin();200entry_collection::const_iterator end_pos = m_entries.end();201entry_collection::const_iterator pos = lower_bound(202begin_pos, end_pos, search_entry, Entry::EntryAddressLessThan);203if (pos != end_pos) {204if (pos != begin_pos) {205if (pos->file_addr != search_entry.file_addr)206--pos;207else if (pos->file_addr == search_entry.file_addr) {208// If this is a termination entry, it shouldn't match since entries209// with the "is_terminal_entry" member set to true are termination210// entries that define the range for the previous entry.211if (pos->is_terminal_entry) {212// The matching entry is a terminal entry, so we skip ahead to213// the next entry to see if there is another entry following this214// one whose section/offset matches.215++pos;216if (pos != end_pos) {217if (pos->file_addr != search_entry.file_addr)218pos = end_pos;219}220}221222if (pos != end_pos) {223// While in the same section/offset backup to find the first line224// entry that matches the address in case there are multiple225while (pos != begin_pos) {226entry_collection::const_iterator prev_pos = pos - 1;227if (prev_pos->file_addr == search_entry.file_addr &&228prev_pos->is_terminal_entry == false)229--pos;230else231break;232}233}234}235}236else237{238// There might be code in the containing objfile before the first239// line table entry. Make sure that does not get considered part of240// the first line table entry.241if (pos->file_addr > so_addr.GetFileAddress())242return false;243}244245// Make sure we have a valid match and that the match isn't a246// terminating entry for a previous line...247if (pos != end_pos && pos->is_terminal_entry == false) {248uint32_t match_idx = std::distance(begin_pos, pos);249success = ConvertEntryAtIndexToLineEntry(match_idx, line_entry);250if (index_ptr != nullptr && success)251*index_ptr = match_idx;252}253}254}255}256return success;257}258259bool LineTable::ConvertEntryAtIndexToLineEntry(uint32_t idx,260LineEntry &line_entry) {261if (idx >= m_entries.size())262return false;263264const Entry &entry = m_entries[idx];265ModuleSP module_sp(m_comp_unit->GetModule());266if (!module_sp)267return false;268269addr_t file_addr = entry.file_addr;270271// A terminal entry can point outside of a module or a section. Decrement the272// address to ensure it resolves correctly.273if (entry.is_terminal_entry)274--file_addr;275276if (!module_sp->ResolveFileAddress(file_addr,277line_entry.range.GetBaseAddress()))278return false;279280// Now undo the decrement above.281if (entry.is_terminal_entry)282line_entry.range.GetBaseAddress().Slide(1);283284if (!entry.is_terminal_entry && idx + 1 < m_entries.size())285line_entry.range.SetByteSize(m_entries[idx + 1].file_addr -286entry.file_addr);287else288line_entry.range.SetByteSize(0);289290line_entry.file_sp = std::make_shared<SupportFile>(291m_comp_unit->GetSupportFiles().GetFileSpecAtIndex(entry.file_idx));292line_entry.original_file_sp =293m_comp_unit->GetSupportFiles().GetSupportFileAtIndex(entry.file_idx);294line_entry.line = entry.line;295line_entry.column = entry.column;296line_entry.is_start_of_statement = entry.is_start_of_statement;297line_entry.is_start_of_basic_block = entry.is_start_of_basic_block;298line_entry.is_prologue_end = entry.is_prologue_end;299line_entry.is_epilogue_begin = entry.is_epilogue_begin;300line_entry.is_terminal_entry = entry.is_terminal_entry;301return true;302}303304uint32_t LineTable::FindLineEntryIndexByFileIndex(305uint32_t start_idx, uint32_t file_idx,306const SourceLocationSpec &src_location_spec, LineEntry *line_entry_ptr) {307auto file_idx_matcher = [](uint32_t file_index, uint16_t entry_file_idx) {308return file_index == entry_file_idx;309};310return FindLineEntryIndexByFileIndexImpl<uint32_t>(311312start_idx, file_idx, src_location_spec, line_entry_ptr, file_idx_matcher);313}314315uint32_t LineTable::FindLineEntryIndexByFileIndex(316uint32_t start_idx, const std::vector<uint32_t> &file_idx,317const SourceLocationSpec &src_location_spec, LineEntry *line_entry_ptr) {318auto file_idx_matcher = [](const std::vector<uint32_t> &file_indexes,319uint16_t entry_file_idx) {320return llvm::is_contained(file_indexes, entry_file_idx);321};322323return FindLineEntryIndexByFileIndexImpl<std::vector<uint32_t>>(324start_idx, file_idx, src_location_spec, line_entry_ptr, file_idx_matcher);325}326327size_t LineTable::FindLineEntriesForFileIndex(uint32_t file_idx, bool append,328SymbolContextList &sc_list) {329330if (!append)331sc_list.Clear();332333size_t num_added = 0;334const size_t count = m_entries.size();335if (count > 0) {336SymbolContext sc(m_comp_unit);337338for (size_t idx = 0; idx < count; ++idx) {339// Skip line table rows that terminate the previous row340// (is_terminal_entry is non-zero)341if (m_entries[idx].is_terminal_entry)342continue;343344if (m_entries[idx].file_idx == file_idx) {345if (ConvertEntryAtIndexToLineEntry(idx, sc.line_entry)) {346++num_added;347sc_list.Append(sc);348}349}350}351}352return num_added;353}354355void LineTable::Dump(Stream *s, Target *target, Address::DumpStyle style,356Address::DumpStyle fallback_style, bool show_line_ranges) {357const size_t count = m_entries.size();358LineEntry line_entry;359SupportFileSP prev_file;360for (size_t idx = 0; idx < count; ++idx) {361ConvertEntryAtIndexToLineEntry(idx, line_entry);362line_entry.Dump(s, target, !prev_file->Equal(*line_entry.original_file_sp),363style, fallback_style, show_line_ranges);364s->EOL();365prev_file = line_entry.original_file_sp;366}367}368369void LineTable::GetDescription(Stream *s, Target *target,370DescriptionLevel level) {371const size_t count = m_entries.size();372LineEntry line_entry;373for (size_t idx = 0; idx < count; ++idx) {374ConvertEntryAtIndexToLineEntry(idx, line_entry);375line_entry.GetDescription(s, level, m_comp_unit, target, true);376s->EOL();377}378}379380size_t LineTable::GetContiguousFileAddressRanges(FileAddressRanges &file_ranges,381bool append) {382if (!append)383file_ranges.Clear();384const size_t initial_count = file_ranges.GetSize();385386const size_t count = m_entries.size();387LineEntry line_entry;388FileAddressRanges::Entry range(LLDB_INVALID_ADDRESS, 0);389for (size_t idx = 0; idx < count; ++idx) {390const Entry &entry = m_entries[idx];391392if (entry.is_terminal_entry) {393if (range.GetRangeBase() != LLDB_INVALID_ADDRESS) {394range.SetRangeEnd(entry.file_addr);395file_ranges.Append(range);396range.Clear(LLDB_INVALID_ADDRESS);397}398} else if (range.GetRangeBase() == LLDB_INVALID_ADDRESS) {399range.SetRangeBase(entry.file_addr);400}401}402return file_ranges.GetSize() - initial_count;403}404405LineTable *LineTable::LinkLineTable(const FileRangeMap &file_range_map) {406std::unique_ptr<LineTable> line_table_up(new LineTable(m_comp_unit));407LineSequenceImpl sequence;408const size_t count = m_entries.size();409LineEntry line_entry;410const FileRangeMap::Entry *file_range_entry = nullptr;411const FileRangeMap::Entry *prev_file_range_entry = nullptr;412lldb::addr_t prev_file_addr = LLDB_INVALID_ADDRESS;413bool prev_entry_was_linked = false;414bool range_changed = false;415for (size_t idx = 0; idx < count; ++idx) {416const Entry &entry = m_entries[idx];417418const bool end_sequence = entry.is_terminal_entry;419const lldb::addr_t lookup_file_addr =420entry.file_addr - (end_sequence ? 1 : 0);421if (file_range_entry == nullptr ||422!file_range_entry->Contains(lookup_file_addr)) {423prev_file_range_entry = file_range_entry;424file_range_entry = file_range_map.FindEntryThatContains(lookup_file_addr);425range_changed = true;426}427428lldb::addr_t prev_end_entry_linked_file_addr = LLDB_INVALID_ADDRESS;429lldb::addr_t entry_linked_file_addr = LLDB_INVALID_ADDRESS;430431bool terminate_previous_entry = false;432if (file_range_entry) {433entry_linked_file_addr = entry.file_addr -434file_range_entry->GetRangeBase() +435file_range_entry->data;436// Determine if we need to terminate the previous entry when the previous437// entry was not contiguous with this one after being linked.438if (range_changed && prev_file_range_entry) {439prev_end_entry_linked_file_addr =440std::min<lldb::addr_t>(entry.file_addr,441prev_file_range_entry->GetRangeEnd()) -442prev_file_range_entry->GetRangeBase() + prev_file_range_entry->data;443if (prev_end_entry_linked_file_addr != entry_linked_file_addr)444terminate_previous_entry = prev_entry_was_linked;445}446} else if (prev_entry_was_linked) {447// This entry doesn't have a remapping and it needs to be removed. Watch448// out in case we need to terminate a previous entry needs to be449// terminated now that one line entry in a sequence is not longer valid.450if (!sequence.m_entries.empty() &&451!sequence.m_entries.back().is_terminal_entry) {452terminate_previous_entry = true;453}454}455456if (terminate_previous_entry && !sequence.m_entries.empty()) {457assert(prev_file_addr != LLDB_INVALID_ADDRESS);458UNUSED_IF_ASSERT_DISABLED(prev_file_addr);459sequence.m_entries.push_back(sequence.m_entries.back());460if (prev_end_entry_linked_file_addr == LLDB_INVALID_ADDRESS)461prev_end_entry_linked_file_addr =462std::min<lldb::addr_t>(entry.file_addr,463prev_file_range_entry->GetRangeEnd()) -464prev_file_range_entry->GetRangeBase() + prev_file_range_entry->data;465sequence.m_entries.back().file_addr = prev_end_entry_linked_file_addr;466sequence.m_entries.back().is_terminal_entry = true;467468// Append the sequence since we just terminated the previous one469line_table_up->InsertSequence(&sequence);470sequence.Clear();471}472473// Now link the current entry474if (file_range_entry) {475// This entry has an address remapping and it needs to have its address476// relinked477sequence.m_entries.push_back(entry);478sequence.m_entries.back().file_addr = entry_linked_file_addr;479}480481// If we have items in the sequence and the last entry is a terminal entry,482// insert this sequence into our new line table.483if (!sequence.m_entries.empty() &&484sequence.m_entries.back().is_terminal_entry) {485line_table_up->InsertSequence(&sequence);486sequence.Clear();487prev_entry_was_linked = false;488} else {489prev_entry_was_linked = file_range_entry != nullptr;490}491prev_file_addr = entry.file_addr;492range_changed = false;493}494if (line_table_up->m_entries.empty())495return nullptr;496return line_table_up.release();497}498499500