diff --git a/lldb/include/lldb/Target/Trace.h b/lldb/include/lldb/Target/Trace.h --- a/lldb/include/lldb/Target/Trace.h +++ b/lldb/include/lldb/Target/Trace.h @@ -49,6 +49,12 @@ Backwards, }; + enum class TraceDumpFormat { + Default = 0, + Raw = 1, + JSON = 2, + }; + /// Dump the trace data that this plug-in has access to. /// /// This function will dump all of the trace data for all threads in a user @@ -167,11 +173,12 @@ /// \param[in] end_position /// The position of the last instruction to print. /// - /// \param[in] raw - /// Dump only instruction addresses without disassembly nor symbol - /// information. + /// \param[in] format + /// If \b Default, then dump instruction addresses with disassembly and symbol information. + /// If \b Raw, then only dump instruction addresses. + /// If \b JSON, then print addresses and disassembly in JSON format. void DumpTraceInstructions(Thread &thread, Stream &s, size_t count, - size_t end_position, bool raw); + size_t end_position, TraceDumpFormat format); /// Run the provided callback on the instructions of the trace of the given /// thread. @@ -204,14 +211,28 @@ std::function load_addr)> callback) = 0; + struct InstructionSymbolInfo { + SymbolContext sc; + Address address; + lldb::DisassemblerSP disassembler; + }; + + /// Similar to \a TraverseInstructions byt the callback receives a \a InstructionSymbolInfo object + /// with symbol information for the given instruction, calculated efficiently. + void TraverseInstructionsWithSymbolInfo( + Thread &thread, size_t position, TraceDirection direction, + std::function insn)> + callback); + /// Get the number of available instructions in the trace of the given thread. /// /// \param[in] thread /// The thread whose trace will be inspected. /// /// \return - /// The total number of instructions in the trace. - virtual size_t GetInstructionCount(Thread &thread) = 0; + /// The total number of instructions in the trace, or \a llvm::None if the + /// thread is not being traced. + virtual llvm::Optional GetInstructionCount(Thread &thread) = 0; /// Check if a thread is currently traced by this object. /// diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp --- a/lldb/source/Commands/CommandObjectThread.cpp +++ b/lldb/source/Commands/CommandObjectThread.cpp @@ -2149,7 +2149,7 @@ result.SetError("error: no more data"); else trace_sp->DumpTraceInstructions(*thread_sp, result.GetOutputStream(), - count, position, m_options.m_raw); + count, position, m_options.m_raw? Trace::TraceDumpFormat::Raw : Trace::TraceDumpFormat::Default); return true; } diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h @@ -70,7 +70,7 @@ std::function load_addr)> callback) override; - size_t GetInstructionCount(Thread &thread) override; + llvm::Optional GetInstructionCount(Thread &thread) override; size_t GetCursorPosition(Thread &thread) override; diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp @@ -122,11 +122,11 @@ break; } -size_t TraceIntelPT::GetInstructionCount(Thread &thread) { +Optional TraceIntelPT::GetInstructionCount(Thread &thread) { if (const DecodedThread *decoded_thread = Decode(thread)) return decoded_thread->GetInstructions().size(); else - return 0; + return None; } Expected TraceIntelPT::GetCPUInfoForLiveProcess() { diff --git a/lldb/source/Target/Trace.cpp b/lldb/source/Target/Trace.cpp --- a/lldb/source/Target/Trace.cpp +++ b/lldb/source/Target/Trace.cpp @@ -101,6 +101,47 @@ return num == 0 ? 1 : static_cast(log10(num)) + 1; } +static bool IsSameInstructionSymbolContext(Optional prev_insn, Trace::InstructionSymbolInfo &insn) { + if (!prev_insn) + return false; + + // line entry checks + if (insn.sc.line_entry.IsValid()) { + if (prev_insn->sc.line_entry.IsValid()) + return LineEntry::Compare(insn.sc.line_entry, prev_insn->sc.line_entry) == 0; + else + return false; + } + + if (prev_insn->sc.line_entry.IsValid()) + return false; + + // function checks + if (insn.sc.function != nullptr) { + if (prev_insn->sc.function != nullptr) + return insn.sc.function == prev_insn->sc.function; + else + return false; + } + + if (prev_insn->sc.function != nullptr) + return false; + + // symbol checks + if (insn.sc.symbol != nullptr) { + if (prev_insn->sc.symbol != nullptr) + return insn.sc.symbol == prev_insn->sc.symbol; + else + return false; + } + + if (prev_insn->sc.symbol != nullptr) + return false; + + // module checks + return insn.sc.module_sp == prev_insn->sc.module_sp; +} + /// Dump the symbol context of the given instruction address if it's different /// from the symbol context of the previous instruction in the trace. /// @@ -113,37 +154,23 @@ /// \return /// The symbol context of the current address, which might differ from the /// previous one. -static SymbolContext DumpSymbolContext(Stream &s, const SymbolContext &prev_sc, - Target &target, const Address &address) { - AddressRange range; - if (prev_sc.GetAddressRange(eSymbolContextEverything, 0, - /*inline_block_range*/ false, range) && - range.ContainsFileAddress(address)) - return prev_sc; - - SymbolContext sc; - address.CalculateSymbolContext(&sc, eSymbolContextEverything); - - if (!prev_sc.module_sp && !sc.module_sp) - return sc; - if (prev_sc.module_sp == sc.module_sp && !sc.function && !sc.symbol && - !prev_sc.function && !prev_sc.symbol) - return sc; +static void DumpInstructionSymbolContext(Stream &s, Optional prev_insn, Trace::InstructionSymbolInfo &insn, Target &target) { + if (IsSameInstructionSymbolContext(prev_insn, insn)) + return; s.Printf(" "); - if (!sc.module_sp) + if (!insn.sc.module_sp) s.Printf("(none)"); - else if (!sc.function && !sc.symbol) + else if (!insn.sc.function && !insn.sc.symbol) s.Printf("%s`(none)", - sc.module_sp->GetFileSpec().GetFilename().AsCString()); + insn.sc.module_sp->GetFileSpec().GetFilename().AsCString()); else - sc.DumpStopContext(&s, &target, address, /*show_fullpath*/ false, + insn.sc.DumpStopContext(&s, &target, insn.address, /*show_fullpath*/ false, /*show_module*/ true, /*show_inlined_frames*/ false, /*show_function_arguments*/ true, /*show_function_name*/ true); s.Printf("\n"); - return sc; } /// Dump an instruction given by its address using a given disassembler, unless @@ -157,135 +184,122 @@ /// /// \return /// \b true if the information could be dumped, \b false otherwise. -static bool TryDumpInstructionInfo(Stream &s, - const DisassemblerSP &disassembler, - const ExecutionContext &exe_ctx, - const Address &address) { - if (!disassembler) - return false; +static void DumpInstructionDisassembly(Stream &s, + Trace::InstructionSymbolInfo &insn, + const ExecutionContext &exe_ctx) { + if (!insn.disassembler) + return; if (InstructionSP instruction = - disassembler->GetInstructionList().GetInstructionAtAddress(address)) { + insn.disassembler->GetInstructionList().GetInstructionAtAddress(insn.address)) { instruction->Dump(&s, /*show_address*/ false, /*show_bytes*/ false, /*max_opcode_byte_size*/ 0, &exe_ctx, - /*sym_ctx*/ nullptr, /*prev_sym_ctx*/ nullptr, + &insn.sc, /*prev_sym_ctx*/ nullptr, /*disassembly_addr_format*/ nullptr, /*max_address_text_size*/ 0); - return true; - } - - return false; -} - -/// Dump an instruction instruction given by its address. -/// -/// \param[in] prev_disassembler -/// The disassembler that was used to dump the previous instruction in the -/// trace. It is useful to avoid recomputations. -/// -/// \param[in] address -/// The address of the instruction to dump. -/// -/// \return -/// A disassembler that contains the given instruction, which might differ -/// from the previous disassembler. -static DisassemblerSP -DumpInstructionInfo(Stream &s, const SymbolContext &sc, - const DisassemblerSP &prev_disassembler, - ExecutionContext &exe_ctx, const Address &address) { - // We first try to use the previous disassembler - if (TryDumpInstructionInfo(s, prev_disassembler, exe_ctx, address)) - return prev_disassembler; - - // Now we try using the current function's disassembler - if (sc.function) { - DisassemblerSP disassembler = - sc.function->GetInstructions(exe_ctx, nullptr, true); - if (TryDumpInstructionInfo(s, disassembler, exe_ctx, address)) - return disassembler; } - - // We fallback to disassembly one instruction - Target &target = exe_ctx.GetTargetRef(); - const ArchSpec &arch = target.GetArchitecture(); - AddressRange range(address, arch.GetMaximumOpcodeByteSize() * 1); - DisassemblerSP disassembler = Disassembler::DisassembleRange( - arch, /*plugin_name*/ nullptr, - /*flavor*/ nullptr, target, range, /*prefer_file_cache*/ true); - if (TryDumpInstructionInfo(s, disassembler, exe_ctx, address)) - return disassembler; - return nullptr; } void Trace::DumpTraceInstructions(Thread &thread, Stream &s, size_t count, - size_t end_position, bool raw) { - if (!IsTraced(thread)) { + size_t end_position, TraceDumpFormat format) { + Optional instructions_count = GetInstructionCount(thread); + if (!instructions_count) { s.Printf("thread #%u: tid = %" PRIu64 ", not traced\n", thread.GetIndexID(), - thread.GetID()); + thread.GetID()); return; } - size_t instructions_count = GetInstructionCount(thread); s.Printf("thread #%u: tid = %" PRIu64 ", total instructions = %zu\n", - thread.GetIndexID(), thread.GetID(), instructions_count); + thread.GetIndexID(), thread.GetID(), *instructions_count); - if (count == 0 || end_position >= instructions_count) + if (count == 0 || end_position >= *instructions_count) return; + int digits_count = GetNumberOfDigits(end_position); size_t start_position = end_position + 1 < count ? 0 : end_position + 1 - count; - - int digits_count = GetNumberOfDigits(end_position); auto printInstructionIndex = [&](size_t index) { s.Printf(" [%*zu] ", digits_count, index); }; bool was_prev_instruction_an_error = false; Target &target = thread.GetProcess()->GetTarget(); - - SymbolContext sc; - DisassemblerSP disassembler; ExecutionContext exe_ctx; target.CalculateExecutionContext(exe_ctx); - - TraverseInstructions( - thread, start_position, TraceDirection::Forwards, - [&](size_t index, Expected load_address) -> bool { - if (load_address) { - // We print an empty line after a sequence of errors to show more - // clearly that there's a gap in the trace - if (was_prev_instruction_an_error) + Optional prev_insn; + + TraverseInstructionsWithSymbolInfo(thread, start_position, TraceDirection::Forwards, + [&](size_t index, Expected insn) -> bool { + if (!insn) { + printInstructionIndex(index); + s << toString(insn.takeError()); + + prev_insn = None; + was_prev_instruction_an_error = true; + } else { + if (was_prev_instruction_an_error) s.Printf(" ...missing instructions\n"); + if (format != TraceDumpFormat::Raw) + DumpInstructionSymbolContext(s, prev_insn, *insn, target); + printInstructionIndex(index); + s.Printf("0x%016" PRIx64 " ", insn->address.GetLoadAddress(&target)); + if (format != TraceDumpFormat::Raw) + DumpInstructionDisassembly(s, *insn, exe_ctx); + + prev_insn = *insn; + was_prev_instruction_an_error = false; + } + + s.Printf("\n"); + return index < end_position; + }); +} - Address address; - if (!raw) { - target.GetSectionLoadList().ResolveLoadAddress(*load_address, - address); - - sc = DumpSymbolContext(s, sc, target, address); - } - - printInstructionIndex(index); - s.Printf("0x%016" PRIx64 " ", *load_address); - - if (!raw) { - disassembler = - DumpInstructionInfo(s, sc, disassembler, exe_ctx, address); - } - - was_prev_instruction_an_error = false; - } else { - printInstructionIndex(index); - s << toString(load_address.takeError()); - was_prev_instruction_an_error = true; - if (!raw) - sc = SymbolContext(); - } - - s.Printf("\n"); +void Trace::TraverseInstructionsWithSymbolInfo( + Thread &thread, size_t position, TraceDirection direction, + std::function insn)> + callback) { + InstructionSymbolInfo prev_insn; + Target &target = thread.GetProcess()->GetTarget(); + const ArchSpec &arch = target.GetArchitecture(); - return index < end_position; - }); + TraverseInstructions( + thread, + position, + direction, + [&](size_t index, Expected load_address) -> bool { + if (!load_address) + return callback(index, load_address.takeError()); + + if (*load_address == 0x00007ffff7bd96e8) { + (void)1; + } + + // Fill the address + InstructionSymbolInfo insn; + target.GetSectionLoadList().ResolveLoadAddress(*load_address, insn.address); + + // Fill the symbol context + AddressRange range; + if (prev_insn.sc.GetAddressRange(eSymbolContextEverything, 0, + /*inline_block_range*/ false, range) && + range.ContainsFileAddress(insn.address)) + insn.sc = prev_insn.sc; + else + insn.address.CalculateSymbolContext(&insn.sc, eSymbolContextEverything); + + // Fill the disassembler + if (prev_insn.disassembler && prev_insn.disassembler->GetInstructionList().GetInstructionAtAddress(insn.address)) + insn.disassembler = prev_insn.disassembler; + else { + AddressRange range(insn.address, arch.GetMaximumOpcodeByteSize() * 1); + insn.disassembler = Disassembler::DisassembleRange( + arch, /*plugin_name*/ nullptr, + /*flavor*/ nullptr, target, range, /*prefer_file_cache*/ true); + } + prev_insn = insn; + return callback(index, insn); + }); } Error Trace::Start(const llvm::json::Value &request) {