llvm-project/lldb/source/Target/TraceInstructionDumper.cpp
Alisamar Husain bcf1978a87 [intelpt] Refactoring instruction decoding for flexibility
Now the decoded thread has Append methods that provide more flexibility
in terms of the underlying data structure that represents the
instructions. In this case, we are able to represent the sporadic errors
as map and thus reduce the size of each instruction.

Differential Revision: https://reviews.llvm.org/D122293
2022-03-26 11:34:47 -07:00

293 lines
9.8 KiB
C++

//===-- TraceInstructionDumper.cpp ----------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "lldb/Target/TraceInstructionDumper.h"
#include "lldb/Core/Module.h"
#include "lldb/Symbol/Function.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/SectionLoadList.h"
using namespace lldb;
using namespace lldb_private;
using namespace llvm;
TraceInstructionDumper::TraceInstructionDumper(lldb::TraceCursorUP &&cursor_up,
int initial_index, bool raw,
bool show_tsc)
: m_cursor_up(std::move(cursor_up)), m_index(initial_index), m_raw(raw),
m_show_tsc(show_tsc) {}
/// \return
/// Return \b true if the cursor could move one step.
bool TraceInstructionDumper::TryMoveOneStep() {
if (!m_cursor_up->Next()) {
SetNoMoreData();
return false;
}
m_index += m_cursor_up->IsForwards() ? 1 : -1;
return true;
}
/// \return
/// The number of characters that would be needed to print the given
/// integer.
static int GetNumberOfChars(int num) {
if (num == 0)
return 1;
return (num < 0 ? 1 : 0) + static_cast<int>(log10(abs(num))) + 1;
}
/// Helper struct that holds symbol, disassembly and address information of an
/// instruction.
struct InstructionSymbolInfo {
SymbolContext sc;
Address address;
lldb::addr_t load_address;
lldb::DisassemblerSP disassembler;
lldb::InstructionSP instruction;
lldb_private::ExecutionContext exe_ctx;
};
// This custom LineEntry validator is neded because some line_entries have
// 0 as line, which is meaningless. Notice that LineEntry::IsValid only
// checks that line is not LLDB_INVALID_LINE_NUMBER, i.e. UINT32_MAX.
static bool IsLineEntryValid(const LineEntry &line_entry) {
return line_entry.IsValid() && line_entry.line > 0;
}
/// \return
/// \b true if the provided line entries match line, column and source file.
/// This function assumes that the line entries are valid.
static bool FileLineAndColumnMatches(const LineEntry &a, const LineEntry &b) {
if (a.line != b.line)
return false;
if (a.column != b.column)
return false;
return a.file == b.file;
}
/// Compare the symbol contexts of the provided \a InstructionSymbolInfo
/// objects.
///
/// \return
/// \a true if both instructions belong to the same scope level analized
/// in the following order:
/// - module
/// - symbol
/// - function
/// - line
static bool
IsSameInstructionSymbolContext(const InstructionSymbolInfo &prev_insn,
const InstructionSymbolInfo &insn) {
// module checks
if (insn.sc.module_sp != prev_insn.sc.module_sp)
return false;
// symbol checks
if (insn.sc.symbol != prev_insn.sc.symbol)
return false;
// function checks
if (!insn.sc.function && !prev_insn.sc.function)
return true;
else if (insn.sc.function != prev_insn.sc.function)
return false;
// line entry checks
const bool curr_line_valid = IsLineEntryValid(insn.sc.line_entry);
const bool prev_line_valid = IsLineEntryValid(prev_insn.sc.line_entry);
if (curr_line_valid && prev_line_valid)
return FileLineAndColumnMatches(insn.sc.line_entry,
prev_insn.sc.line_entry);
return curr_line_valid == prev_line_valid;
}
/// Dump the symbol context of the given instruction address if it's different
/// from the symbol context of the previous instruction in the trace.
///
/// \param[in] prev_sc
/// The symbol context of the previous instruction in the trace.
///
/// \param[in] address
/// The address whose symbol information will be dumped.
///
/// \return
/// The symbol context of the current address, which might differ from the
/// previous one.
static void
DumpInstructionSymbolContext(Stream &s,
Optional<InstructionSymbolInfo> prev_insn,
InstructionSymbolInfo &insn) {
if (prev_insn && IsSameInstructionSymbolContext(*prev_insn, insn))
return;
s.Printf(" ");
if (!insn.sc.module_sp)
s.Printf("(none)");
else if (!insn.sc.function && !insn.sc.symbol)
s.Printf("%s`(none)",
insn.sc.module_sp->GetFileSpec().GetFilename().AsCString());
else
insn.sc.DumpStopContext(&s, insn.exe_ctx.GetTargetPtr(), insn.address,
/*show_fullpaths=*/false,
/*show_module=*/true, /*show_inlined_frames=*/false,
/*show_function_arguments=*/true,
/*show_function_name=*/true);
s.Printf("\n");
}
static void DumpInstructionDisassembly(Stream &s, InstructionSymbolInfo &insn) {
if (!insn.instruction)
return;
s.Printf(" ");
insn.instruction->Dump(&s, /*max_opcode_byte_size=*/0, /*show_address=*/false,
/*show_bytes=*/false, &insn.exe_ctx, &insn.sc,
/*prev_sym_ctx=*/nullptr,
/*disassembly_addr_format=*/nullptr,
/*max_address_text_size=*/0);
}
void TraceInstructionDumper::SetNoMoreData() { m_no_more_data = true; }
bool TraceInstructionDumper::HasMoreData() { return !m_no_more_data; }
void TraceInstructionDumper::DumpInstructions(Stream &s, size_t count) {
ThreadSP thread_sp = m_cursor_up->GetExecutionContextRef().GetThreadSP();
if (!thread_sp) {
s.Printf("invalid thread");
return;
}
s.Printf("thread #%u: tid = %" PRIu64 "\n", thread_sp->GetIndexID(),
thread_sp->GetID());
int digits_count = GetNumberOfChars(
m_cursor_up->IsForwards() ? m_index + count - 1 : m_index - count + 1);
bool was_prev_instruction_an_error = false;
auto printMissingInstructionsMessage = [&]() {
s.Printf(" ...missing instructions\n");
};
auto printInstructionIndex = [&]() {
s.Printf(" [%*d] ", digits_count, m_index);
if (m_show_tsc) {
s.Printf("[tsc=");
if (Optional<uint64_t> timestamp = m_cursor_up->GetCounter(lldb::eTraceCounterTSC))
s.Printf("0x%016" PRIx64, *timestamp);
else
s.Printf("unavailable");
s.Printf("] ");
}
};
InstructionSymbolInfo prev_insn_info;
Target &target = thread_sp->GetProcess()->GetTarget();
ExecutionContext exe_ctx;
target.CalculateExecutionContext(exe_ctx);
const ArchSpec &arch = target.GetArchitecture();
// Find the symbol context for the given address reusing the previous
// instruction's symbol context when possible.
auto calculateSymbolContext = [&](const Address &address) {
AddressRange range;
if (prev_insn_info.sc.GetAddressRange(eSymbolContextEverything, 0,
/*inline_block_range*/ false,
range) &&
range.Contains(address))
return prev_insn_info.sc;
SymbolContext sc;
address.CalculateSymbolContext(&sc, eSymbolContextEverything);
return sc;
};
// Find the disassembler for the given address reusing the previous
// instruction's disassembler when possible.
auto calculateDisass = [&](const Address &address, const SymbolContext &sc) {
if (prev_insn_info.disassembler) {
if (InstructionSP instruction =
prev_insn_info.disassembler->GetInstructionList()
.GetInstructionAtAddress(address))
return std::make_tuple(prev_insn_info.disassembler, instruction);
}
if (sc.function) {
if (DisassemblerSP disassembler =
sc.function->GetInstructions(exe_ctx, nullptr)) {
if (InstructionSP instruction =
disassembler->GetInstructionList().GetInstructionAtAddress(
address))
return std::make_tuple(disassembler, instruction);
}
}
// We fallback to a single instruction disassembler
AddressRange range(address, arch.GetMaximumOpcodeByteSize());
DisassemblerSP disassembler =
Disassembler::DisassembleRange(arch, /*plugin_name*/ nullptr,
/*flavor*/ nullptr, target, range);
return std::make_tuple(disassembler,
disassembler ? disassembler->GetInstructionList()
.GetInstructionAtAddress(address)
: InstructionSP());
};
for (size_t i = 0; i < count; i++) {
if (!HasMoreData()) {
s.Printf(" no more data\n");
break;
}
if (const char *err = m_cursor_up->GetError()) {
if (!m_cursor_up->IsForwards() && !was_prev_instruction_an_error)
printMissingInstructionsMessage();
was_prev_instruction_an_error = true;
printInstructionIndex();
s << err;
} else {
if (m_cursor_up->IsForwards() && was_prev_instruction_an_error)
printMissingInstructionsMessage();
was_prev_instruction_an_error = false;
InstructionSymbolInfo insn_info;
if (!m_raw) {
insn_info.load_address = m_cursor_up->GetLoadAddress();
insn_info.exe_ctx = exe_ctx;
insn_info.address.SetLoadAddress(insn_info.load_address, &target);
insn_info.sc = calculateSymbolContext(insn_info.address);
std::tie(insn_info.disassembler, insn_info.instruction) =
calculateDisass(insn_info.address, insn_info.sc);
DumpInstructionSymbolContext(s, prev_insn_info, insn_info);
}
printInstructionIndex();
s.Printf("0x%016" PRIx64, m_cursor_up->GetLoadAddress());
if (!m_raw)
DumpInstructionDisassembly(s, insn_info);
prev_insn_info = insn_info;
}
s.Printf("\n");
TryMoveOneStep();
}
}