| //===-- ThreadPlanTracer.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 <cstring> |
| |
| #include "lldb/Core/Debugger.h" |
| #include "lldb/Core/Disassembler.h" |
| #include "lldb/Core/DumpRegisterValue.h" |
| #include "lldb/Core/Module.h" |
| #include "lldb/Core/StreamFile.h" |
| #include "lldb/Core/Value.h" |
| #include "lldb/Symbol/TypeList.h" |
| #include "lldb/Symbol/TypeSystem.h" |
| #include "lldb/Target/ABI.h" |
| #include "lldb/Target/Process.h" |
| #include "lldb/Target/RegisterContext.h" |
| #include "lldb/Target/SectionLoadList.h" |
| #include "lldb/Target/Target.h" |
| #include "lldb/Target/Thread.h" |
| #include "lldb/Target/ThreadPlan.h" |
| #include "lldb/Utility/DataBufferHeap.h" |
| #include "lldb/Utility/DataExtractor.h" |
| #include "lldb/Utility/Log.h" |
| #include "lldb/Utility/State.h" |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| |
| #pragma mark ThreadPlanTracer |
| |
| ThreadPlanTracer::ThreadPlanTracer(Thread &thread, lldb::StreamSP &stream_sp) |
| : m_process(*thread.GetProcess().get()), m_tid(thread.GetID()), |
| m_single_step(true), m_enabled(false), m_stream_sp(stream_sp) {} |
| |
| ThreadPlanTracer::ThreadPlanTracer(Thread &thread) |
| : m_process(*thread.GetProcess().get()), m_tid(thread.GetID()), |
| m_single_step(true), m_enabled(false), m_stream_sp() {} |
| |
| Stream *ThreadPlanTracer::GetLogStream() { |
| if (m_stream_sp) |
| return m_stream_sp.get(); |
| else { |
| TargetSP target_sp(GetThread().CalculateTarget()); |
| if (target_sp) |
| return &(target_sp->GetDebugger().GetOutputStream()); |
| } |
| return nullptr; |
| } |
| |
| Thread &ThreadPlanTracer::GetThread() { |
| if (m_thread) |
| return *m_thread; |
| |
| ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(m_tid); |
| m_thread = thread_sp.get(); |
| return *m_thread; |
| } |
| void ThreadPlanTracer::Log() { |
| SymbolContext sc; |
| bool show_frame_index = false; |
| bool show_fullpaths = false; |
| |
| Stream *stream = GetLogStream(); |
| if (stream) { |
| GetThread().GetStackFrameAtIndex(0)->Dump(stream, show_frame_index, |
| show_fullpaths); |
| stream->Printf("\n"); |
| stream->Flush(); |
| } |
| } |
| |
| bool ThreadPlanTracer::TracerExplainsStop() { |
| if (m_enabled && m_single_step) { |
| lldb::StopInfoSP stop_info = GetThread().GetStopInfo(); |
| return (stop_info->GetStopReason() == eStopReasonTrace); |
| } else |
| return false; |
| } |
| |
| #pragma mark ThreadPlanAssemblyTracer |
| |
| ThreadPlanAssemblyTracer::ThreadPlanAssemblyTracer(Thread &thread, |
| lldb::StreamSP &stream_sp) |
| : ThreadPlanTracer(thread, stream_sp), m_disassembler_sp(), m_intptr_type(), |
| m_register_values() {} |
| |
| ThreadPlanAssemblyTracer::ThreadPlanAssemblyTracer(Thread &thread) |
| : ThreadPlanTracer(thread), m_disassembler_sp(), m_intptr_type(), |
| m_register_values() {} |
| |
| Disassembler *ThreadPlanAssemblyTracer::GetDisassembler() { |
| if (!m_disassembler_sp) |
| m_disassembler_sp = Disassembler::FindPlugin( |
| m_process.GetTarget().GetArchitecture(), nullptr, nullptr); |
| return m_disassembler_sp.get(); |
| } |
| |
| TypeFromUser ThreadPlanAssemblyTracer::GetIntPointerType() { |
| if (!m_intptr_type.IsValid()) { |
| if (auto target_sp = m_process.CalculateTarget()) { |
| auto type_system_or_err = |
| target_sp->GetScratchTypeSystemForLanguage(eLanguageTypeC); |
| if (auto err = type_system_or_err.takeError()) { |
| LLDB_LOG_ERROR( |
| lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_TYPES), |
| std::move(err), |
| "Unable to get integer pointer type from TypeSystem"); |
| } else { |
| m_intptr_type = TypeFromUser( |
| type_system_or_err->GetBuiltinTypeForEncodingAndBitSize( |
| eEncodingUint, |
| target_sp->GetArchitecture().GetAddressByteSize() * 8)); |
| } |
| } |
| } |
| return m_intptr_type; |
| } |
| |
| ThreadPlanAssemblyTracer::~ThreadPlanAssemblyTracer() = default; |
| |
| void ThreadPlanAssemblyTracer::TracingStarted() { |
| } |
| |
| void ThreadPlanAssemblyTracer::TracingEnded() { m_register_values.clear(); } |
| |
| void ThreadPlanAssemblyTracer::Log() { |
| Stream *stream = GetLogStream(); |
| |
| if (!stream) |
| return; |
| |
| RegisterContext *reg_ctx = GetThread().GetRegisterContext().get(); |
| |
| lldb::addr_t pc = reg_ctx->GetPC(); |
| Address pc_addr; |
| bool addr_valid = false; |
| uint8_t buffer[16] = {0}; // Must be big enough for any single instruction |
| addr_valid = m_process.GetTarget().GetSectionLoadList().ResolveLoadAddress( |
| pc, pc_addr); |
| |
| pc_addr.Dump(stream, &GetThread(), Address::DumpStyleResolvedDescription, |
| Address::DumpStyleModuleWithFileAddress); |
| stream->PutCString(" "); |
| |
| Disassembler *disassembler = GetDisassembler(); |
| if (disassembler) { |
| Status err; |
| m_process.ReadMemory(pc, buffer, sizeof(buffer), err); |
| |
| if (err.Success()) { |
| DataExtractor extractor(buffer, sizeof(buffer), m_process.GetByteOrder(), |
| m_process.GetAddressByteSize()); |
| |
| bool data_from_file = false; |
| if (addr_valid) |
| disassembler->DecodeInstructions(pc_addr, extractor, 0, 1, false, |
| data_from_file); |
| else |
| disassembler->DecodeInstructions(Address(pc), extractor, 0, 1, false, |
| data_from_file); |
| |
| InstructionList &instruction_list = disassembler->GetInstructionList(); |
| const uint32_t max_opcode_byte_size = |
| instruction_list.GetMaxOpcocdeByteSize(); |
| |
| if (instruction_list.GetSize()) { |
| const bool show_bytes = true; |
| const bool show_address = true; |
| Instruction *instruction = |
| instruction_list.GetInstructionAtIndex(0).get(); |
| const FormatEntity::Entry *disassemble_format = |
| m_process.GetTarget().GetDebugger().GetDisassemblyFormat(); |
| instruction->Dump(stream, max_opcode_byte_size, show_address, |
| show_bytes, nullptr, nullptr, nullptr, |
| disassemble_format, 0); |
| } |
| } |
| } |
| |
| const ABI *abi = m_process.GetABI().get(); |
| TypeFromUser intptr_type = GetIntPointerType(); |
| |
| if (abi && intptr_type.IsValid()) { |
| ValueList value_list; |
| const int num_args = 1; |
| |
| for (int arg_index = 0; arg_index < num_args; ++arg_index) { |
| Value value; |
| value.SetValueType(Value::eValueTypeScalar); |
| value.SetCompilerType(intptr_type); |
| value_list.PushValue(value); |
| } |
| |
| if (abi->GetArgumentValues(GetThread(), value_list)) { |
| for (int arg_index = 0; arg_index < num_args; ++arg_index) { |
| stream->Printf( |
| "\n\targ[%d]=%llx", arg_index, |
| value_list.GetValueAtIndex(arg_index)->GetScalar().ULongLong()); |
| |
| if (arg_index + 1 < num_args) |
| stream->PutCString(", "); |
| } |
| } |
| } |
| |
| if (m_register_values.empty()) { |
| RegisterContext *reg_ctx = GetThread().GetRegisterContext().get(); |
| m_register_values.resize(reg_ctx->GetRegisterCount()); |
| } |
| |
| RegisterValue reg_value; |
| for (uint32_t reg_num = 0, num_registers = reg_ctx->GetRegisterCount(); |
| reg_num < num_registers; ++reg_num) { |
| const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(reg_num); |
| if (reg_ctx->ReadRegister(reg_info, reg_value)) { |
| assert(reg_num < m_register_values.size()); |
| if (m_register_values[reg_num].GetType() == RegisterValue::eTypeInvalid || |
| reg_value != m_register_values[reg_num]) { |
| if (reg_value.GetType() != RegisterValue::eTypeInvalid) { |
| stream->PutCString("\n\t"); |
| DumpRegisterValue(reg_value, stream, reg_info, true, false, |
| eFormatDefault); |
| } |
| } |
| m_register_values[reg_num] = reg_value; |
| } |
| } |
| stream->EOL(); |
| stream->Flush(); |
| } |