//===-- 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(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 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 timestamp = m_cursor_up->GetTimestampCounter()) 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 (Error err = m_cursor_up->GetError()) { if (!m_cursor_up->IsForwards() && !was_prev_instruction_an_error) printMissingInstructionsMessage(); was_prev_instruction_an_error = true; printInstructionIndex(); s << toString(std::move(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(); } }