|  | //===-- 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->Printf("\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(); | 
|  | } |