| //===-- 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/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/LLDBLog.h" | |
| #include "lldb/Utility/Log.h" | |
| #include "lldb/Utility/State.h" | |
| #include "lldb/lldb-forward.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_enabled(false), m_stream_sp(stream_sp), m_thread(nullptr) {} | |
| ThreadPlanTracer::ThreadPlanTracer(Thread &thread) | |
| : m_process(*thread.GetProcess().get()), m_tid(thread.GetID()), | |
| m_enabled(false), m_stream_sp(), m_thread(nullptr) {} | |
| StreamSP ThreadPlanTracer::GetLogStreamSP() { | |
| if (m_stream_sp) | |
| return m_stream_sp; | |
| else { | |
| TargetSP target_sp(GetThread().CalculateTarget()); | |
| if (target_sp) | |
| return target_sp->GetDebugger().GetAsyncOutputStream(); | |
| } | |
| 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; | |
| if (StreamSP stream_sp = GetLogStreamSP()) { | |
| GetThread().GetStackFrameAtIndex(0)->Dump(stream_sp.get(), show_frame_index, | |
| show_fullpaths); | |
| stream_sp->PutCString("\n"); | |
| stream_sp->Flush(); | |
| } | |
| } | |
| bool ThreadPlanTracer::TracerExplainsStop() { | |
| if (m_enabled) { | |
| 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, 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( | |
| GetLog(LLDBLog::Types), std::move(err), | |
| "Unable to get integer pointer type from TypeSystem: {0}"); | |
| } else { | |
| if (auto ts = *type_system_or_err) | |
| m_intptr_type = TypeFromUser(ts->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() { | |
| StreamSP stream_sp = GetLogStreamSP(); | |
| if (!stream_sp) | |
| 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().ResolveLoadAddress(pc, pc_addr); | |
| pc_addr.Dump(stream_sp.get(), &GetThread(), | |
| Address::DumpStyleResolvedDescription, | |
| Address::DumpStyleModuleWithFileAddress); | |
| stream_sp->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; | |
| const bool show_control_flow_kind = true; | |
| Instruction *instruction = | |
| instruction_list.GetInstructionAtIndex(0).get(); | |
| FormatEntity::Entry disassemble_format = | |
| m_process.GetTarget().GetDebugger().GetDisassemblyFormat(); | |
| instruction->Dump(stream_sp.get(), max_opcode_byte_size, show_address, | |
| show_bytes, show_control_flow_kind, 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::ValueType::Scalar); | |
| 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_sp->Printf( | |
| "\n\targ[%d]=%llx", arg_index, | |
| value_list.GetValueAtIndex(arg_index)->GetScalar().ULongLong()); | |
| if (arg_index + 1 < num_args) | |
| stream_sp->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_sp->PutCString("\n\t"); | |
| DumpRegisterValue(reg_value, *stream_sp, *reg_info, true, false, | |
| eFormatDefault); | |
| } | |
| } | |
| m_register_values[reg_num] = reg_value; | |
| } | |
| } | |
| stream_sp->EOL(); | |
| stream_sp->Flush(); | |
| } |