blob: c1cdcd64308f9f42bbb91c9fcfa112eb23b89214 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_DEVELOPER_DEBUG_ZXDB_SYMBOLS_LINE_TABLE_H_
#define SRC_DEVELOPER_DEBUG_ZXDB_SYMBOLS_LINE_TABLE_H_
#include <optional>
#include <string>
#include <vector>
#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h"
#include "llvm/DebugInfo/DWARF/DWARFDie.h"
#include "src/developer/debug/zxdb/common/address_range.h"
#include "src/developer/debug/zxdb/symbols/arch.h"
#include "src/lib/containers/cpp/array_view.h"
namespace zxdb {
class SymbolContext;
// This virtual interface wraps the line information for a single DWARFUnit. This indirection allows
// the operations that operate on the line table to be more easily mocked for tests (our
// requirements are quite low).
class LineTable {
public:
using Row = llvm::DWARFDebugLine::Row;
// The line table can contain lines marked with a line number of 0. These entries indicate code
// that is compiler-generated and not associated with any source line.
enum SkipMode {
// Return the exact row entry for the address, even if it's marked compiler-generated.
kExactMatch,
// If a line query matches a compiler generated line, advances to the next
// non-compiler-generated line. This usually makes more sense to the user since compiler
// generated instructions will normally be associated with some code.
//
// Thie mode can still return compiler generated line entries if there is no "next" row to
// advance to.
kSkipCompilerGenerated,
};
struct FoundRow {
FoundRow() = default;
FoundRow(containers::array_view<Row> s, size_t i) : sequence(s), index(i) {}
bool empty() const { return sequence.empty(); }
// Returns the row. Call only when !empty().
const LineTable::Row& get() const { return sequence[index]; }
// The sequence of rows associated with the address. These will be contiguous addresses. This
// will be empty if nothing was matched. If nonempty, the last row will always be marked with an
// EndSequence bit.
containers::array_view<Row> sequence;
// Index within the sequence of the found row. Valid when !empty().
size_t index = 0;
};
virtual ~LineTable() = default;
// Returns the number of file names referenced by this line table. The DWARFDebugLine::Row::File
// entries are 1-based (!) indices into a table of this size.
virtual size_t GetNumFileNames() const = 0;
// Returns the absolute file name for the given file index. This is the value from
// DWARFDebugLine::Row::File (1-based). It will return an empty optional on failure, which
// includes lookup errors or if the file is not set for this row (0 index).
virtual std::optional<std::string> GetFileNameByIndex(uint64_t file_id) const = 0;
// Helper wrapper to extract the file from a row.
std::optional<std::string> GetFileNameForRow(const Row& row) const {
return GetFileNameByIndex(row.File);
}
// Returns the DIE associated with the subroutine for the given row. This may be an invalid DIE if
// there is no subroutine for this code (could be compiler-generated).
//
// TODO(brettw) remove and have the callers that need this take a DwarfUnit.
virtual llvm::DWARFDie GetSubroutineForRow(const Row& row) const = 0;
// Query for sequences. This is used for iterating through the entire line table.
//
// Sequences consist of a contiguous range of addresses and will be in sorted order.
size_t GetNumSequences() const;
containers::array_view<Row> GetSequenceAt(size_t index) const;
// Returns the sequence of rows (contiguous addresses ending in an EndSequence tag) containing the
// address. The returned array will be empty if the address was not found. See GetRowForAddress().
//
// Watch out: the addresses in the returned rows will all be module-relative.
containers::array_view<Row> GetRowSequenceForAddress(const SymbolContext& address_context,
TargetPointer absolute_address) const;
// Finds the row in the line table that covers the given address. If there is no match, the
// returned sequence will be empty.
//
// Watch out: the addresses in the returned rows will all be module-relative.
FoundRow GetRowForAddress(const SymbolContext& address_context, TargetPointer absolute_address,
SkipMode skip_mode = kSkipCompilerGenerated) const;
protected:
// Returns the line table row information.
//
// This will not necessarily be sorted by address and may contain stripped regions. Queries should
// go through the sequence table.
//
// The implementation should ensure that the returned value never changes. This will be indexed
// into sequences and cached.
virtual const std::vector<Row>& GetRows() const = 0;
public:
// The DWARF row table will be mostly sorted by address but there will be sequences of addresses
// that are out-of-order relative to each other. In pracice, on common reason for this is when
// code is stripped, the stripped code will have its start address set back to 0.
//
// This tracks the blocks of rows with contiguous addresses. To find a row corresponding to an
// address, binary search to find the block, then binary search the rows referenced by the block.
struct Sequence {
Sequence() = default;
Sequence(const AddressRange& a, size_t r_begin, size_t r_end)
: addresses(a), row_begin(r_begin), row_end(r_end) {}
AddressRange addresses;
// Index into GetRows() of the beginning.
size_t row_begin = 0;
// Index into GetRows() of the ending. This will be the index of the EndSequence row.
//
// If the table doesn't end in an EndSequence row, the last sequence will be ignored so this row
// is guaranteed to exist.
size_t row_end = 0;
};
// Will return an null pointer if there isn't one found.
const Sequence* GetSequenceForRelativeAddress(TargetPointer relative_address) const;
// Ensures that the sequences_ vector is populated from the rows.
void EnsureSequences() const;
// Sorted by Sequence.addresses.end() so lower_bound() can find the right one. Lazily populated,
// see EnsureSequences().
mutable std::vector<Sequence> sequences_;
};
} // namespace zxdb
#endif // SRC_DEVELOPER_DEBUG_ZXDB_SYMBOLS_LINE_TABLE_H_