diff options
Diffstat (limited to 'source/Plugins/Process/MacOSX-Kernel')
18 files changed, 4657 insertions, 0 deletions
diff --git a/source/Plugins/Process/MacOSX-Kernel/CMakeLists.txt b/source/Plugins/Process/MacOSX-Kernel/CMakeLists.txt new file mode 100644 index 000000000000..681b7405e2b8 --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/CMakeLists.txt @@ -0,0 +1,10 @@ +add_lldb_library(lldbPluginProcessMacOSXKernel + CommunicationKDP.cpp + ProcessKDP.cpp + ProcessKDPLog.cpp + RegisterContextKDP_arm.cpp + RegisterContextKDP_arm64.cpp + RegisterContextKDP_i386.cpp + RegisterContextKDP_x86_64.cpp + ThreadKDP.cpp + ) diff --git a/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.cpp b/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.cpp new file mode 100644 index 000000000000..5c1c3284a35c --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.cpp @@ -0,0 +1,1445 @@ +//===-- CommunicationKDP.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "CommunicationKDP.h" + +// C Includes +#include <errno.h> +#include <limits.h> +#include <string.h> + +// C++ Includes + +// Other libraries and framework includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/State.h" +#include "lldb/Core/UUID.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/TimeValue.h" +#include "lldb/Target/Process.h" + +// Project includes +#include "ProcessKDPLog.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// CommunicationKDP constructor +//---------------------------------------------------------------------- +CommunicationKDP::CommunicationKDP (const char *comm_name) : + Communication(comm_name), + m_addr_byte_size (4), + m_byte_order (eByteOrderLittle), + m_packet_timeout (5), + m_sequence_mutex (Mutex::eMutexTypeRecursive), + m_is_running (false), + m_session_key (0u), + m_request_sequence_id (0u), + m_exception_sequence_id (0u), + m_kdp_version_version (0u), + m_kdp_version_feature (0u), + m_kdp_hostinfo_cpu_mask (0u), + m_kdp_hostinfo_cpu_type (0u), + m_kdp_hostinfo_cpu_subtype (0u) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CommunicationKDP::~CommunicationKDP() +{ + if (IsConnected()) + { + Disconnect(); + } +} + +bool +CommunicationKDP::SendRequestPacket (const PacketStreamType &request_packet) +{ + Mutex::Locker locker(m_sequence_mutex); + return SendRequestPacketNoLock (request_packet); +} + +#if 0 +typedef struct { + uint8_t request; // Either: CommandType | ePacketTypeRequest, or CommandType | ePacketTypeReply + uint8_t sequence; + uint16_t length; // Length of entire packet including this header + uint32_t key; // Session key +} kdp_hdr_t; +#endif + +void +CommunicationKDP::MakeRequestPacketHeader (CommandType request_type, + PacketStreamType &request_packet, + uint16_t request_length) +{ + request_packet.Clear(); + request_packet.PutHex8 (request_type | ePacketTypeRequest); // Set the request type + request_packet.PutHex8 (m_request_sequence_id++); // Sequence number + request_packet.PutHex16 (request_length); // Length of the packet including this header + request_packet.PutHex32 (m_session_key); // Session key +} + +bool +CommunicationKDP::SendRequestAndGetReply (const CommandType command, + const PacketStreamType &request_packet, + DataExtractor &reply_packet) +{ + if (IsRunning()) + { + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PACKETS)); + if (log) + { + PacketStreamType log_strm; + DumpPacket (log_strm, request_packet.GetData(), request_packet.GetSize()); + log->Printf("error: kdp running, not sending packet: %.*s", (uint32_t)log_strm.GetSize(), log_strm.GetData()); + } + return false; + } + + Mutex::Locker locker(m_sequence_mutex); +#ifdef LLDB_CONFIGURATION_DEBUG + // NOTE: this only works for packets that are in native endian byte order + assert (request_packet.GetSize() == *((uint16_t *)(request_packet.GetData() + 2))); +#endif + lldb::offset_t offset = 1; + const uint32_t num_retries = 3; + for (uint32_t i=0; i<num_retries; ++i) + { + if (SendRequestPacketNoLock(request_packet)) + { + const uint8_t request_sequence_id = (uint8_t)request_packet.GetData()[1]; + while (1) + { + if (WaitForPacketWithTimeoutMicroSecondsNoLock (reply_packet, GetPacketTimeoutInMicroSeconds ())) + { + offset = 0; + const uint8_t reply_command = reply_packet.GetU8 (&offset); + const uint8_t reply_sequence_id = reply_packet.GetU8 (&offset); + if (request_sequence_id == reply_sequence_id) + { + // The sequent ID was correct, now verify we got the response we were looking for + if ((reply_command & eCommandTypeMask) == command) + { + // Success + if (command == KDP_RESUMECPUS) + m_is_running.SetValue(true, eBroadcastAlways); + return true; + } + else + { + // Failed to get the correct response, bail + reply_packet.Clear(); + return false; + } + } + else if (reply_sequence_id > request_sequence_id) + { + // Sequence ID was greater than the sequence ID of the packet we sent, something + // is really wrong... + reply_packet.Clear(); + return false; + } + else + { + // The reply sequence ID was less than our current packet's sequence ID + // so we should keep trying to get a response because this was a response + // for a previous packet that we must have retried. + } + } + else + { + // Break and retry sending the packet as we didn't get a response due to timeout + break; + } + } + } + } + reply_packet.Clear(); + return false; +} + +bool +CommunicationKDP::SendRequestPacketNoLock (const PacketStreamType &request_packet) +{ + if (IsConnected()) + { + const char *packet_data = request_packet.GetData(); + const size_t packet_size = request_packet.GetSize(); + + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PACKETS)); + if (log) + { + PacketStreamType log_strm; + DumpPacket (log_strm, packet_data, packet_size); + log->Printf("%.*s", (uint32_t)log_strm.GetSize(), log_strm.GetData()); + } + ConnectionStatus status = eConnectionStatusSuccess; + + size_t bytes_written = Write (packet_data, + packet_size, + status, + NULL); + + if (bytes_written == packet_size) + return true; + + if (log) + log->Printf ("error: failed to send packet entire packet %" PRIu64 " of %" PRIu64 " bytes sent", (uint64_t)bytes_written, (uint64_t)packet_size); + } + return false; +} + +bool +CommunicationKDP::GetSequenceMutex (Mutex::Locker& locker) +{ + return locker.TryLock (m_sequence_mutex); +} + + +bool +CommunicationKDP::WaitForNotRunningPrivate (const TimeValue *timeout_ptr) +{ + return m_is_running.WaitForValueEqualTo (false, timeout_ptr, NULL); +} + +size_t +CommunicationKDP::WaitForPacketWithTimeoutMicroSeconds (DataExtractor &packet, uint32_t timeout_usec) +{ + Mutex::Locker locker(m_sequence_mutex); + return WaitForPacketWithTimeoutMicroSecondsNoLock (packet, timeout_usec); +} + +size_t +CommunicationKDP::WaitForPacketWithTimeoutMicroSecondsNoLock (DataExtractor &packet, uint32_t timeout_usec) +{ + uint8_t buffer[8192]; + Error error; + + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PACKETS | KDP_LOG_VERBOSE)); + + // Check for a packet from our cache first without trying any reading... + if (CheckForPacket (NULL, 0, packet)) + return packet.GetByteSize(); + + bool timed_out = false; + while (IsConnected() && !timed_out) + { + lldb::ConnectionStatus status = eConnectionStatusNoConnection; + size_t bytes_read = Read (buffer, sizeof(buffer), timeout_usec, status, &error); + + if (log) + log->Printf ("%s: Read (buffer, (sizeof(buffer), timeout_usec = 0x%x, status = %s, error = %s) => bytes_read = %" PRIu64, + __PRETTY_FUNCTION__, + timeout_usec, + Communication::ConnectionStatusAsCString (status), + error.AsCString(), + (uint64_t)bytes_read); + + if (bytes_read > 0) + { + if (CheckForPacket (buffer, bytes_read, packet)) + return packet.GetByteSize(); + } + else + { + switch (status) + { + case eConnectionStatusInterrupted: + case eConnectionStatusTimedOut: + timed_out = true; + break; + case eConnectionStatusSuccess: + //printf ("status = success but error = %s\n", error.AsCString("<invalid>")); + break; + + case eConnectionStatusEndOfFile: + case eConnectionStatusNoConnection: + case eConnectionStatusLostConnection: + case eConnectionStatusError: + Disconnect(); + break; + } + } + } + packet.Clear (); + return 0; +} + +bool +CommunicationKDP::CheckForPacket (const uint8_t *src, size_t src_len, DataExtractor &packet) +{ + // Put the packet data into the buffer in a thread safe fashion + Mutex::Locker locker(m_bytes_mutex); + + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PACKETS)); + + if (src && src_len > 0) + { + if (log && log->GetVerbose()) + { + PacketStreamType log_strm; + DataExtractor::DumpHexBytes (&log_strm, src, src_len, UINT32_MAX, LLDB_INVALID_ADDRESS); + log->Printf ("CommunicationKDP::%s adding %u bytes: %s", + __FUNCTION__, + (uint32_t)src_len, + log_strm.GetData()); + } + m_bytes.append ((const char *)src, src_len); + } + + // Make sure we at least have enough bytes for a packet header + const size_t bytes_available = m_bytes.size(); + if (bytes_available >= 8) + { + packet.SetData (&m_bytes[0], bytes_available, m_byte_order); + lldb::offset_t offset = 0; + uint8_t reply_command = packet.GetU8(&offset); + switch (reply_command) + { + case ePacketTypeRequest | KDP_EXCEPTION: + case ePacketTypeRequest | KDP_TERMINATION: + // We got an exception request, so be sure to send an ACK + { + PacketStreamType request_ack_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + // Set the reply but and make the ACK packet + request_ack_packet.PutHex8 (reply_command | ePacketTypeReply); + request_ack_packet.PutHex8 (packet.GetU8(&offset)); + request_ack_packet.PutHex16 (packet.GetU16(&offset)); + request_ack_packet.PutHex32 (packet.GetU32(&offset)); + m_is_running.SetValue(false, eBroadcastAlways); + // Ack to the exception or termination + SendRequestPacketNoLock (request_ack_packet); + } + // Fall through to case below to get packet contents + case ePacketTypeReply | KDP_CONNECT: + case ePacketTypeReply | KDP_DISCONNECT: + case ePacketTypeReply | KDP_HOSTINFO: + case ePacketTypeReply | KDP_VERSION: + case ePacketTypeReply | KDP_MAXBYTES: + case ePacketTypeReply | KDP_READMEM: + case ePacketTypeReply | KDP_WRITEMEM: + case ePacketTypeReply | KDP_READREGS: + case ePacketTypeReply | KDP_WRITEREGS: + case ePacketTypeReply | KDP_LOAD: + case ePacketTypeReply | KDP_IMAGEPATH: + case ePacketTypeReply | KDP_SUSPEND: + case ePacketTypeReply | KDP_RESUMECPUS: + case ePacketTypeReply | KDP_BREAKPOINT_SET: + case ePacketTypeReply | KDP_BREAKPOINT_REMOVE: + case ePacketTypeReply | KDP_REGIONS: + case ePacketTypeReply | KDP_REATTACH: + case ePacketTypeReply | KDP_HOSTREBOOT: + case ePacketTypeReply | KDP_READMEM64: + case ePacketTypeReply | KDP_WRITEMEM64: + case ePacketTypeReply | KDP_BREAKPOINT_SET64: + case ePacketTypeReply | KDP_BREAKPOINT_REMOVE64: + case ePacketTypeReply | KDP_KERNELVERSION: + case ePacketTypeReply | KDP_READPHYSMEM64: + case ePacketTypeReply | KDP_WRITEPHYSMEM64: + case ePacketTypeReply | KDP_READIOPORT: + case ePacketTypeReply | KDP_WRITEIOPORT: + case ePacketTypeReply | KDP_READMSR64: + case ePacketTypeReply | KDP_WRITEMSR64: + case ePacketTypeReply | KDP_DUMPINFO: + { + offset = 2; + const uint16_t length = packet.GetU16 (&offset); + if (length <= bytes_available) + { + // We have an entire packet ready, we need to copy the data + // bytes into a buffer that will be owned by the packet and + // erase the bytes from our communcation buffer "m_bytes" + packet.SetData (DataBufferSP (new DataBufferHeap (&m_bytes[0], length))); + m_bytes.erase (0, length); + + if (log) + { + PacketStreamType log_strm; + DumpPacket (log_strm, packet); + + log->Printf("%.*s", (uint32_t)log_strm.GetSize(), log_strm.GetData()); + } + return true; + } + } + break; + + default: + // Unrecognized reply command byte, erase this byte and try to get back on track + if (log) + log->Printf ("CommunicationKDP::%s: tossing junk byte: 0x%2.2x", + __FUNCTION__, + (uint8_t)m_bytes[0]); + m_bytes.erase(0, 1); + break; + } + } + packet.Clear(); + return false; +} + + +bool +CommunicationKDP::SendRequestConnect (uint16_t reply_port, + uint16_t exc_port, + const char *greeting) +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + if (greeting == NULL) + greeting = ""; + + const CommandType command = KDP_CONNECT; + // Length is 82 uint16_t and the length of the greeting C string with the terminating NULL + const uint32_t command_length = 8 + 2 + 2 + ::strlen(greeting) + 1; + MakeRequestPacketHeader (command, request_packet, command_length); + // Always send connect ports as little endian + request_packet.SetByteOrder (eByteOrderLittle); + request_packet.PutHex16 (htons(reply_port)); + request_packet.PutHex16 (htons(exc_port)); + request_packet.SetByteOrder (m_byte_order); + request_packet.PutCString (greeting); + DataExtractor reply_packet; + return SendRequestAndGetReply (command, request_packet, reply_packet); +} + +void +CommunicationKDP::ClearKDPSettings () +{ + m_request_sequence_id = 0; + m_kdp_version_version = 0; + m_kdp_version_feature = 0; + m_kdp_hostinfo_cpu_mask = 0; + m_kdp_hostinfo_cpu_type = 0; + m_kdp_hostinfo_cpu_subtype = 0; +} + +bool +CommunicationKDP::SendRequestReattach (uint16_t reply_port) +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + const CommandType command = KDP_REATTACH; + // Length is 8 bytes for the header plus 2 bytes for the reply UDP port + const uint32_t command_length = 8 + 2; + MakeRequestPacketHeader (command, request_packet, command_length); + // Always send connect ports as little endian + request_packet.SetByteOrder (eByteOrderLittle); + request_packet.PutHex16(htons(reply_port)); + request_packet.SetByteOrder (m_byte_order); + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + // Reset the sequence ID to zero for reattach + ClearKDPSettings (); + lldb::offset_t offset = 4; + m_session_key = reply_packet.GetU32 (&offset); + return true; + } + return false; +} + +uint32_t +CommunicationKDP::GetVersion () +{ + if (!VersionIsValid()) + SendRequestVersion(); + return m_kdp_version_version; +} + +uint32_t +CommunicationKDP::GetFeatureFlags () +{ + if (!VersionIsValid()) + SendRequestVersion(); + return m_kdp_version_feature; +} + +bool +CommunicationKDP::SendRequestVersion () +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + const CommandType command = KDP_VERSION; + const uint32_t command_length = 8; + MakeRequestPacketHeader (command, request_packet, command_length); + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + lldb::offset_t offset = 8; + m_kdp_version_version = reply_packet.GetU32 (&offset); + m_kdp_version_feature = reply_packet.GetU32 (&offset); + return true; + } + return false; +} + +#if 0 // Disable KDP_IMAGEPATH for now, it seems to hang the KDP connection... +const char * +CommunicationKDP::GetImagePath () +{ + if (m_image_path.empty()) + SendRequestImagePath(); + return m_image_path.c_str(); +} + +bool +CommunicationKDP::SendRequestImagePath () +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + const CommandType command = KDP_IMAGEPATH; + const uint32_t command_length = 8; + MakeRequestPacketHeader (command, request_packet, command_length); + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + const char *path = reply_packet.PeekCStr(8); + if (path && path[0]) + m_kernel_version.assign (path); + return true; + } + return false; +} +#endif + +uint32_t +CommunicationKDP::GetCPUMask () +{ + if (!HostInfoIsValid()) + SendRequestHostInfo(); + return m_kdp_hostinfo_cpu_mask; +} + +uint32_t +CommunicationKDP::GetCPUType () +{ + if (!HostInfoIsValid()) + SendRequestHostInfo(); + return m_kdp_hostinfo_cpu_type; +} + +uint32_t +CommunicationKDP::GetCPUSubtype () +{ + if (!HostInfoIsValid()) + SendRequestHostInfo(); + return m_kdp_hostinfo_cpu_subtype; +} + +lldb_private::UUID +CommunicationKDP::GetUUID () +{ + UUID uuid; + if (GetKernelVersion() == NULL) + return uuid; + + if (m_kernel_version.find("UUID=") == std::string::npos) + return uuid; + + size_t p = m_kernel_version.find("UUID=") + strlen ("UUID="); + std::string uuid_str = m_kernel_version.substr(p, 36); + if (uuid_str.size() < 32) + return uuid; + + if (uuid.SetFromCString (uuid_str.c_str()) == 0) + { + UUID invalid_uuid; + return invalid_uuid; + } + + return uuid; +} + +bool +CommunicationKDP::RemoteIsEFI () +{ + if (GetKernelVersion() == NULL) + return false; + if (strncmp (m_kernel_version.c_str(), "EFI", 3) == 0) + return true; + else + return false; +} + +bool +CommunicationKDP::RemoteIsDarwinKernel () +{ + if (GetKernelVersion() == NULL) + return false; + if (m_kernel_version.find("Darwin Kernel") != std::string::npos) + return true; + else + return false; +} + +lldb::addr_t +CommunicationKDP::GetLoadAddress () +{ + if (GetKernelVersion() == NULL) + return LLDB_INVALID_ADDRESS; + + if (m_kernel_version.find("stext=") == std::string::npos) + return LLDB_INVALID_ADDRESS; + size_t p = m_kernel_version.find("stext=") + strlen ("stext="); + if (m_kernel_version[p] != '0' || m_kernel_version[p + 1] != 'x') + return LLDB_INVALID_ADDRESS; + + addr_t kernel_load_address; + errno = 0; + kernel_load_address = ::strtoul (m_kernel_version.c_str() + p, NULL, 16); + if (errno != 0 || kernel_load_address == 0) + return LLDB_INVALID_ADDRESS; + + return kernel_load_address; +} + +bool +CommunicationKDP::SendRequestHostInfo () +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + const CommandType command = KDP_HOSTINFO; + const uint32_t command_length = 8; + MakeRequestPacketHeader (command, request_packet, command_length); + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + lldb::offset_t offset = 8; + m_kdp_hostinfo_cpu_mask = reply_packet.GetU32 (&offset); + m_kdp_hostinfo_cpu_type = reply_packet.GetU32 (&offset); + m_kdp_hostinfo_cpu_subtype = reply_packet.GetU32 (&offset); + + ArchSpec kernel_arch; + kernel_arch.SetArchitecture (eArchTypeMachO, + m_kdp_hostinfo_cpu_type, + m_kdp_hostinfo_cpu_subtype); + + m_addr_byte_size = kernel_arch.GetAddressByteSize(); + m_byte_order = kernel_arch.GetByteOrder(); + return true; + } + return false; +} + +const char * +CommunicationKDP::GetKernelVersion () +{ + if (m_kernel_version.empty()) + SendRequestKernelVersion (); + return m_kernel_version.c_str(); +} + +bool +CommunicationKDP::SendRequestKernelVersion () +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + const CommandType command = KDP_KERNELVERSION; + const uint32_t command_length = 8; + MakeRequestPacketHeader (command, request_packet, command_length); + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + const char *kernel_version_cstr = reply_packet.PeekCStr(8); + if (kernel_version_cstr && kernel_version_cstr[0]) + m_kernel_version.assign (kernel_version_cstr); + return true; + } + return false; +} + +bool +CommunicationKDP::SendRequestDisconnect () +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + const CommandType command = KDP_DISCONNECT; + const uint32_t command_length = 8; + MakeRequestPacketHeader (command, request_packet, command_length); + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + // Are we supposed to get a reply for disconnect? + } + ClearKDPSettings (); + return true; +} + +uint32_t +CommunicationKDP::SendRequestReadMemory (lldb::addr_t addr, + void *dst, + uint32_t dst_len, + Error &error) +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + bool use_64 = (GetVersion() >= 11); + uint32_t command_addr_byte_size = use_64 ? 8 : 4; + const CommandType command = use_64 ? KDP_READMEM64 : KDP_READMEM; + // Size is header + address size + uint32_t length + const uint32_t command_length = 8 + command_addr_byte_size + 4; + MakeRequestPacketHeader (command, request_packet, command_length); + request_packet.PutMaxHex64 (addr, command_addr_byte_size); + request_packet.PutHex32 (dst_len); + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + lldb::offset_t offset = 8; + uint32_t kdp_error = reply_packet.GetU32 (&offset); + uint32_t src_len = reply_packet.GetByteSize() - 12; + + if (src_len > 0) + { + const void *src = reply_packet.GetData(&offset, src_len); + if (src) + { + ::memcpy (dst, src, src_len); + error.Clear(); + return src_len; + } + } + if (kdp_error) + error.SetErrorStringWithFormat ("kdp read memory failed (error %u)", kdp_error); + else + error.SetErrorString ("kdp read memory failed"); + } + else + { + error.SetErrorString ("failed to send packet"); + } + return 0; +} + + +uint32_t +CommunicationKDP::SendRequestWriteMemory (lldb::addr_t addr, + const void *src, + uint32_t src_len, + Error &error) +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + bool use_64 = (GetVersion() >= 11); + uint32_t command_addr_byte_size = use_64 ? 8 : 4; + const CommandType command = use_64 ? KDP_WRITEMEM64 : KDP_WRITEMEM; + // Size is header + address size + uint32_t length + const uint32_t command_length = 8 + command_addr_byte_size + 4 + src_len; + MakeRequestPacketHeader (command, request_packet, command_length); + request_packet.PutMaxHex64 (addr, command_addr_byte_size); + request_packet.PutHex32 (src_len); + request_packet.PutRawBytes(src, src_len); + + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + lldb::offset_t offset = 8; + uint32_t kdp_error = reply_packet.GetU32 (&offset); + if (kdp_error) + error.SetErrorStringWithFormat ("kdp write memory failed (error %u)", kdp_error); + else + { + error.Clear(); + return src_len; + } + } + else + { + error.SetErrorString ("failed to send packet"); + } + return 0; +} + +bool +CommunicationKDP::SendRawRequest (uint8_t command_byte, + const void *src, // Raw packet payload bytes + uint32_t src_len, // Raw packet payload length + DataExtractor &reply_packet, + Error &error) +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + // Size is header + address size + uint32_t length + const uint32_t command_length = 8 + src_len; + const CommandType command = (CommandType)command_byte; + MakeRequestPacketHeader (command, request_packet, command_length); + request_packet.PutRawBytes(src, src_len); + + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + lldb::offset_t offset = 8; + uint32_t kdp_error = reply_packet.GetU32 (&offset); + if (kdp_error && (command_byte != KDP_DUMPINFO)) + error.SetErrorStringWithFormat ("request packet 0x%8.8x failed (error %u)", command_byte, kdp_error); + else + { + error.Clear(); + return true; + } + } + else + { + error.SetErrorString ("failed to send packet"); + } + return false; +} + + +const char * +CommunicationKDP::GetCommandAsCString (uint8_t command) +{ + switch (command) + { + case KDP_CONNECT: return "KDP_CONNECT"; + case KDP_DISCONNECT: return "KDP_DISCONNECT"; + case KDP_HOSTINFO: return "KDP_HOSTINFO"; + case KDP_VERSION: return "KDP_VERSION"; + case KDP_MAXBYTES: return "KDP_MAXBYTES"; + case KDP_READMEM: return "KDP_READMEM"; + case KDP_WRITEMEM: return "KDP_WRITEMEM"; + case KDP_READREGS: return "KDP_READREGS"; + case KDP_WRITEREGS: return "KDP_WRITEREGS"; + case KDP_LOAD: return "KDP_LOAD"; + case KDP_IMAGEPATH: return "KDP_IMAGEPATH"; + case KDP_SUSPEND: return "KDP_SUSPEND"; + case KDP_RESUMECPUS: return "KDP_RESUMECPUS"; + case KDP_EXCEPTION: return "KDP_EXCEPTION"; + case KDP_TERMINATION: return "KDP_TERMINATION"; + case KDP_BREAKPOINT_SET: return "KDP_BREAKPOINT_SET"; + case KDP_BREAKPOINT_REMOVE: return "KDP_BREAKPOINT_REMOVE"; + case KDP_REGIONS: return "KDP_REGIONS"; + case KDP_REATTACH: return "KDP_REATTACH"; + case KDP_HOSTREBOOT: return "KDP_HOSTREBOOT"; + case KDP_READMEM64: return "KDP_READMEM64"; + case KDP_WRITEMEM64: return "KDP_WRITEMEM64"; + case KDP_BREAKPOINT_SET64: return "KDP_BREAKPOINT64_SET"; + case KDP_BREAKPOINT_REMOVE64: return "KDP_BREAKPOINT64_REMOVE"; + case KDP_KERNELVERSION: return "KDP_KERNELVERSION"; + case KDP_READPHYSMEM64: return "KDP_READPHYSMEM64"; + case KDP_WRITEPHYSMEM64: return "KDP_WRITEPHYSMEM64"; + case KDP_READIOPORT: return "KDP_READIOPORT"; + case KDP_WRITEIOPORT: return "KDP_WRITEIOPORT"; + case KDP_READMSR64: return "KDP_READMSR64"; + case KDP_WRITEMSR64: return "KDP_WRITEMSR64"; + case KDP_DUMPINFO: return "KDP_DUMPINFO"; + } + return NULL; +} + +void +CommunicationKDP::DumpPacket (Stream &s, const void *data, uint32_t data_len) +{ + DataExtractor extractor (data, data_len, m_byte_order, m_addr_byte_size); + DumpPacket (s, extractor); +} + +void +CommunicationKDP::DumpPacket (Stream &s, const DataExtractor& packet) +{ + const char *error_desc = NULL; + if (packet.GetByteSize() < 8) + { + error_desc = "error: invalid packet (too short): "; + } + else + { + lldb::offset_t offset = 0; + const uint8_t first_packet_byte = packet.GetU8 (&offset); + const uint8_t sequence_id = packet.GetU8 (&offset); + const uint16_t length = packet.GetU16 (&offset); + const uint32_t key = packet.GetU32 (&offset); + const CommandType command = ExtractCommand (first_packet_byte); + const char *command_name = GetCommandAsCString (command); + if (command_name) + { + const bool is_reply = ExtractIsReply(first_packet_byte); + s.Printf ("(running=%i) %s %24s: 0x%2.2x 0x%2.2x 0x%4.4x 0x%8.8x ", + IsRunning(), + is_reply ? "<--" : "-->", + command_name, + first_packet_byte, + sequence_id, + length, + key); + + if (is_reply) + { + // Dump request reply packets + switch (command) + { + // Commands that return a single 32 bit error + case KDP_CONNECT: + case KDP_WRITEMEM: + case KDP_WRITEMEM64: + case KDP_BREAKPOINT_SET: + case KDP_BREAKPOINT_REMOVE: + case KDP_BREAKPOINT_SET64: + case KDP_BREAKPOINT_REMOVE64: + case KDP_WRITEREGS: + case KDP_LOAD: + case KDP_WRITEIOPORT: + case KDP_WRITEMSR64: + { + const uint32_t error = packet.GetU32 (&offset); + s.Printf(" (error=0x%8.8x)", error); + } + break; + + case KDP_DISCONNECT: + case KDP_REATTACH: + case KDP_HOSTREBOOT: + case KDP_SUSPEND: + case KDP_RESUMECPUS: + case KDP_EXCEPTION: + case KDP_TERMINATION: + // No return value for the reply, just the header to ack + s.PutCString(" ()"); + break; + + case KDP_HOSTINFO: + { + const uint32_t cpu_mask = packet.GetU32 (&offset); + const uint32_t cpu_type = packet.GetU32 (&offset); + const uint32_t cpu_subtype = packet.GetU32 (&offset); + s.Printf(" (cpu_mask=0x%8.8x, cpu_type=0x%8.8x, cpu_subtype=0x%8.8x)", cpu_mask, cpu_type, cpu_subtype); + } + break; + + case KDP_VERSION: + { + const uint32_t version = packet.GetU32 (&offset); + const uint32_t feature = packet.GetU32 (&offset); + s.Printf(" (version=0x%8.8x, feature=0x%8.8x)", version, feature); + } + break; + + case KDP_REGIONS: + { + const uint32_t region_count = packet.GetU32 (&offset); + s.Printf(" (count = %u", region_count); + for (uint32_t i=0; i<region_count; ++i) + { + const addr_t region_addr = packet.GetPointer (&offset); + const uint32_t region_size = packet.GetU32 (&offset); + const uint32_t region_prot = packet.GetU32 (&offset); + s.Printf("\n\tregion[%" PRIu64 "] = { range = [0x%16.16" PRIx64 " - 0x%16.16" PRIx64 "), size = 0x%8.8x, prot = %s }", region_addr, region_addr, region_addr + region_size, region_size, GetPermissionsAsCString (region_prot)); + } + } + break; + + case KDP_READMEM: + case KDP_READMEM64: + case KDP_READPHYSMEM64: + { + const uint32_t error = packet.GetU32 (&offset); + const uint32_t count = packet.GetByteSize() - offset; + s.Printf(" (error = 0x%8.8x:\n", error); + if (count > 0) + packet.Dump (&s, // Stream to dump to + offset, // Offset within "packet" + eFormatBytesWithASCII, // Format to use + 1, // Size of each item in bytes + count, // Number of items + 16, // Number per line + m_last_read_memory_addr, // Don't show addresses before each line + 0, 0); // No bitfields + } + break; + + case KDP_READREGS: + { + const uint32_t error = packet.GetU32 (&offset); + const uint32_t count = packet.GetByteSize() - offset; + s.Printf(" (error = 0x%8.8x regs:\n", error); + if (count > 0) + packet.Dump (&s, // Stream to dump to + offset, // Offset within "packet" + eFormatHex, // Format to use + m_addr_byte_size, // Size of each item in bytes + count / m_addr_byte_size, // Number of items + 16 / m_addr_byte_size, // Number per line + LLDB_INVALID_ADDRESS, // Don't show addresses before each line + 0, 0); // No bitfields + } + break; + + case KDP_KERNELVERSION: + { + const char *kernel_version = packet.PeekCStr(8); + s.Printf(" (version = \"%s\")", kernel_version); + } + break; + + case KDP_MAXBYTES: + { + const uint32_t max_bytes = packet.GetU32 (&offset); + s.Printf(" (max_bytes = 0x%8.8x (%u))", max_bytes, max_bytes); + } + break; + case KDP_IMAGEPATH: + { + const char *path = packet.GetCStr(&offset); + s.Printf(" (path = \"%s\")", path); + } + break; + + case KDP_READIOPORT: + case KDP_READMSR64: + { + const uint32_t error = packet.GetU32 (&offset); + const uint32_t count = packet.GetByteSize() - offset; + s.Printf(" (error = 0x%8.8x io:\n", error); + if (count > 0) + packet.Dump (&s, // Stream to dump to + offset, // Offset within "packet" + eFormatHex, // Format to use + 1, // Size of each item in bytes + count, // Number of items + 16, // Number per line + LLDB_INVALID_ADDRESS, // Don't show addresses before each line + 0, 0); // No bitfields + } + break; + case KDP_DUMPINFO: + { + const uint32_t count = packet.GetByteSize() - offset; + s.Printf(" (count = %u, bytes = \n", count); + if (count > 0) + packet.Dump (&s, // Stream to dump to + offset, // Offset within "packet" + eFormatHex, // Format to use + 1, // Size of each item in bytes + count, // Number of items + 16, // Number per line + LLDB_INVALID_ADDRESS, // Don't show addresses before each line + 0, 0); // No bitfields + + } + break; + + default: + s.Printf(" (add support for dumping this packet reply!!!"); + break; + + } + } + else + { + // Dump request packets + switch (command) + { + case KDP_CONNECT: + { + const uint16_t reply_port = ntohs(packet.GetU16 (&offset)); + const uint16_t exc_port = ntohs(packet.GetU16 (&offset)); + s.Printf(" (reply_port = %u, exc_port = %u, greeting = \"%s\")", reply_port, exc_port, packet.GetCStr(&offset)); + } + break; + + case KDP_DISCONNECT: + case KDP_HOSTREBOOT: + case KDP_HOSTINFO: + case KDP_VERSION: + case KDP_REGIONS: + case KDP_KERNELVERSION: + case KDP_MAXBYTES: + case KDP_IMAGEPATH: + case KDP_SUSPEND: + // No args, just the header in the request... + s.PutCString(" ()"); + break; + + case KDP_RESUMECPUS: + { + const uint32_t cpu_mask = packet.GetU32 (&offset); + s.Printf(" (cpu_mask = 0x%8.8x)", cpu_mask); + } + break; + + case KDP_READMEM: + { + const uint32_t addr = packet.GetU32 (&offset); + const uint32_t size = packet.GetU32 (&offset); + s.Printf(" (addr = 0x%8.8x, size = %u)", addr, size); + m_last_read_memory_addr = addr; + } + break; + + case KDP_WRITEMEM: + { + const uint32_t addr = packet.GetU32 (&offset); + const uint32_t size = packet.GetU32 (&offset); + s.Printf(" (addr = 0x%8.8x, size = %u, bytes = \n", addr, size); + if (size > 0) + DataExtractor::DumpHexBytes(&s, packet.GetData(&offset, size), size, 32, addr); + } + break; + + case KDP_READMEM64: + { + const uint64_t addr = packet.GetU64 (&offset); + const uint32_t size = packet.GetU32 (&offset); + s.Printf(" (addr = 0x%16.16" PRIx64 ", size = %u)", addr, size); + m_last_read_memory_addr = addr; + } + break; + + case KDP_READPHYSMEM64: + { + const uint64_t addr = packet.GetU64 (&offset); + const uint32_t size = packet.GetU32 (&offset); + const uint32_t lcpu = packet.GetU16 (&offset); + s.Printf(" (addr = 0x%16.16llx, size = %u, lcpu = %u)", addr, size, lcpu); + m_last_read_memory_addr = addr; + } + break; + + case KDP_WRITEMEM64: + { + const uint64_t addr = packet.GetU64 (&offset); + const uint32_t size = packet.GetU32 (&offset); + s.Printf(" (addr = 0x%16.16" PRIx64 ", size = %u, bytes = \n", addr, size); + if (size > 0) + DataExtractor::DumpHexBytes(&s, packet.GetData(&offset, size), size, 32, addr); + } + break; + + case KDP_WRITEPHYSMEM64: + { + const uint64_t addr = packet.GetU64 (&offset); + const uint32_t size = packet.GetU32 (&offset); + const uint32_t lcpu = packet.GetU16 (&offset); + s.Printf(" (addr = 0x%16.16llx, size = %u, lcpu = %u, bytes = \n", addr, size, lcpu); + if (size > 0) + DataExtractor::DumpHexBytes(&s, packet.GetData(&offset, size), size, 32, addr); + } + break; + + case KDP_READREGS: + { + const uint32_t cpu = packet.GetU32 (&offset); + const uint32_t flavor = packet.GetU32 (&offset); + s.Printf(" (cpu = %u, flavor = %u)", cpu, flavor); + } + break; + + case KDP_WRITEREGS: + { + const uint32_t cpu = packet.GetU32 (&offset); + const uint32_t flavor = packet.GetU32 (&offset); + const uint32_t nbytes = packet.GetByteSize() - offset; + s.Printf(" (cpu = %u, flavor = %u, regs = \n", cpu, flavor); + if (nbytes > 0) + packet.Dump (&s, // Stream to dump to + offset, // Offset within "packet" + eFormatHex, // Format to use + m_addr_byte_size, // Size of each item in bytes + nbytes / m_addr_byte_size, // Number of items + 16 / m_addr_byte_size, // Number per line + LLDB_INVALID_ADDRESS, // Don't show addresses before each line + 0, 0); // No bitfields + } + break; + + + case KDP_BREAKPOINT_SET: + case KDP_BREAKPOINT_REMOVE: + { + const uint32_t addr = packet.GetU32 (&offset); + s.Printf(" (addr = 0x%8.8x)", addr); + } + break; + + case KDP_BREAKPOINT_SET64: + case KDP_BREAKPOINT_REMOVE64: + { + const uint64_t addr = packet.GetU64 (&offset); + s.Printf(" (addr = 0x%16.16" PRIx64 ")", addr); + } + break; + + + case KDP_LOAD: + { + const char *path = packet.GetCStr(&offset); + s.Printf(" (path = \"%s\")", path); + } + break; + + case KDP_EXCEPTION: + { + const uint32_t count = packet.GetU32 (&offset); + + for (uint32_t i=0; i<count; ++i) + { + const uint32_t cpu = packet.GetU32 (&offset); + const uint32_t exc = packet.GetU32 (&offset); + const uint32_t code = packet.GetU32 (&offset); + const uint32_t subcode = packet.GetU32 (&offset); + const char *exc_cstr = NULL; + switch (exc) + { + case 1: exc_cstr = "EXC_BAD_ACCESS"; break; + case 2: exc_cstr = "EXC_BAD_INSTRUCTION"; break; + case 3: exc_cstr = "EXC_ARITHMETIC"; break; + case 4: exc_cstr = "EXC_EMULATION"; break; + case 5: exc_cstr = "EXC_SOFTWARE"; break; + case 6: exc_cstr = "EXC_BREAKPOINT"; break; + case 7: exc_cstr = "EXC_SYSCALL"; break; + case 8: exc_cstr = "EXC_MACH_SYSCALL"; break; + case 9: exc_cstr = "EXC_RPC_ALERT"; break; + case 10: exc_cstr = "EXC_CRASH"; break; + default: + break; + } + + s.Printf ("{ cpu = 0x%8.8x, exc = %s (%u), code = %u (0x%8.8x), subcode = %u (0x%8.8x)} ", + cpu, exc_cstr, exc, code, code, subcode, subcode); + } + } + break; + + case KDP_TERMINATION: + { + const uint32_t term_code = packet.GetU32 (&offset); + const uint32_t exit_code = packet.GetU32 (&offset); + s.Printf(" (term_code = 0x%8.8x (%u), exit_code = 0x%8.8x (%u))", term_code, term_code, exit_code, exit_code); + } + break; + + case KDP_REATTACH: + { + const uint16_t reply_port = ntohs(packet.GetU16 (&offset)); + s.Printf(" (reply_port = %u)", reply_port); + } + break; + + case KDP_READMSR64: + { + const uint32_t address = packet.GetU32 (&offset); + const uint16_t lcpu = packet.GetU16 (&offset); + s.Printf(" (address=0x%8.8x, lcpu=0x%4.4x)", address, lcpu); + } + break; + + case KDP_WRITEMSR64: + { + const uint32_t address = packet.GetU32 (&offset); + const uint16_t lcpu = packet.GetU16 (&offset); + const uint32_t nbytes = packet.GetByteSize() - offset; + s.Printf(" (address=0x%8.8x, lcpu=0x%4.4x, nbytes=0x%8.8x)", lcpu, address, nbytes); + if (nbytes > 0) + packet.Dump (&s, // Stream to dump to + offset, // Offset within "packet" + eFormatHex, // Format to use + 1, // Size of each item in bytes + nbytes, // Number of items + 16, // Number per line + LLDB_INVALID_ADDRESS, // Don't show addresses before each line + 0, 0); // No bitfields + } + break; + + case KDP_READIOPORT: + { + const uint16_t lcpu = packet.GetU16 (&offset); + const uint16_t address = packet.GetU16 (&offset); + const uint16_t nbytes = packet.GetU16 (&offset); + s.Printf(" (lcpu=0x%4.4x, address=0x%4.4x, nbytes=%u)", lcpu, address, nbytes); + } + break; + + case KDP_WRITEIOPORT: + { + const uint16_t lcpu = packet.GetU16 (&offset); + const uint16_t address = packet.GetU16 (&offset); + const uint16_t nbytes = packet.GetU16 (&offset); + s.Printf(" (lcpu = %u, addr = 0x%4.4x, nbytes = %u, bytes = \n", lcpu, address, nbytes); + if (nbytes > 0) + packet.Dump (&s, // Stream to dump to + offset, // Offset within "packet" + eFormatHex, // Format to use + 1, // Size of each item in bytes + nbytes, // Number of items + 16, // Number per line + LLDB_INVALID_ADDRESS, // Don't show addresses before each line + 0, 0); // No bitfields + } + break; + + case KDP_DUMPINFO: + { + const uint32_t count = packet.GetByteSize() - offset; + s.Printf(" (count = %u, bytes = \n", count); + if (count > 0) + packet.Dump (&s, // Stream to dump to + offset, // Offset within "packet" + eFormatHex, // Format to use + 1, // Size of each item in bytes + count, // Number of items + 16, // Number per line + LLDB_INVALID_ADDRESS, // Don't show addresses before each line + 0, 0); // No bitfields + + } + break; + + } + } + } + else + { + error_desc = "error: invalid packet command: "; + } + } + + if (error_desc) + { + s.PutCString (error_desc); + + packet.Dump (&s, // Stream to dump to + 0, // Offset into "packet" + eFormatBytes, // Dump as hex bytes + 1, // Size of each item is 1 for single bytes + packet.GetByteSize(), // Number of bytes + UINT32_MAX, // Num bytes per line + LLDB_INVALID_ADDRESS, // Base address + 0, 0); // Bitfield info set to not do anything bitfield related + } +} + +uint32_t +CommunicationKDP::SendRequestReadRegisters (uint32_t cpu, + uint32_t flavor, + void *dst, + uint32_t dst_len, + Error &error) +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + const CommandType command = KDP_READREGS; + // Size is header + 4 byte cpu and 4 byte flavor + const uint32_t command_length = 8 + 4 + 4; + MakeRequestPacketHeader (command, request_packet, command_length); + request_packet.PutHex32 (cpu); + request_packet.PutHex32 (flavor); + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + lldb::offset_t offset = 8; + uint32_t kdp_error = reply_packet.GetU32 (&offset); + uint32_t src_len = reply_packet.GetByteSize() - 12; + + if (src_len > 0) + { + const uint32_t bytes_to_copy = std::min<uint32_t>(src_len, dst_len); + const void *src = reply_packet.GetData(&offset, bytes_to_copy); + if (src) + { + ::memcpy (dst, src, bytes_to_copy); + error.Clear(); + // Return the number of bytes we could have returned regardless if + // we copied them or not, just so we know when things don't match up + return src_len; + } + } + if (kdp_error) + error.SetErrorStringWithFormat("failed to read kdp registers for cpu %u flavor %u (error %u)", cpu, flavor, kdp_error); + else + error.SetErrorStringWithFormat("failed to read kdp registers for cpu %u flavor %u", cpu, flavor); + } + else + { + error.SetErrorString ("failed to send packet"); + } + return 0; +} + +uint32_t +CommunicationKDP::SendRequestWriteRegisters (uint32_t cpu, + uint32_t flavor, + const void *src, + uint32_t src_len, + Error &error) +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + const CommandType command = KDP_WRITEREGS; + // Size is header + 4 byte cpu and 4 byte flavor + const uint32_t command_length = 8 + 4 + 4 + src_len; + MakeRequestPacketHeader (command, request_packet, command_length); + request_packet.PutHex32 (cpu); + request_packet.PutHex32 (flavor); + request_packet.Write(src, src_len); + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + lldb::offset_t offset = 8; + uint32_t kdp_error = reply_packet.GetU32 (&offset); + if (kdp_error == 0) + return src_len; + error.SetErrorStringWithFormat("failed to read kdp registers for cpu %u flavor %u (error %u)", cpu, flavor, kdp_error); + } + else + { + error.SetErrorString ("failed to send packet"); + } + return 0; +} + + +bool +CommunicationKDP::SendRequestResume () +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + const CommandType command = KDP_RESUMECPUS; + const uint32_t command_length = 12; + MakeRequestPacketHeader (command, request_packet, command_length); + request_packet.PutHex32(GetCPUMask()); + + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + return true; + return false; +} + +bool +CommunicationKDP::SendRequestBreakpoint (bool set, addr_t addr) +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + bool use_64 = (GetVersion() >= 11); + uint32_t command_addr_byte_size = use_64 ? 8 : 4; + const CommandType command = set ? (use_64 ? KDP_BREAKPOINT_SET64 : KDP_BREAKPOINT_SET ): + (use_64 ? KDP_BREAKPOINT_REMOVE64 : KDP_BREAKPOINT_REMOVE); + + const uint32_t command_length = 8 + command_addr_byte_size; + MakeRequestPacketHeader (command, request_packet, command_length); + request_packet.PutMaxHex64 (addr, command_addr_byte_size); + + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + lldb::offset_t offset = 8; + uint32_t kdp_error = reply_packet.GetU32 (&offset); + if (kdp_error == 0) + return true; + } + return false; +} + +bool +CommunicationKDP::SendRequestSuspend () +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + const CommandType command = KDP_SUSPEND; + const uint32_t command_length = 8; + MakeRequestPacketHeader (command, request_packet, command_length); + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + return true; + return false; +} + diff --git a/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.h b/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.h new file mode 100644 index 000000000000..98a146d5a06d --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.h @@ -0,0 +1,347 @@ +//===-- CommunicationKDP.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommunicationKDP_h_ +#define liblldb_CommunicationKDP_h_ + +// C Includes +// C++ Includes +#include <list> +#include <string> + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/Communication.h" +#include "lldb/Core/Listener.h" +#include "lldb/Core/StreamBuffer.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Host/Predicate.h" +#include "lldb/Host/TimeValue.h" + +class CommunicationKDP : public lldb_private::Communication +{ +public: + enum + { + eBroadcastBitRunPacketSent = kLoUserBroadcastBit + }; + + const static uint32_t kMaxPacketSize = 1200; + const static uint32_t kMaxDataSize = 1024; + typedef lldb_private::StreamBuffer<1024> PacketStreamType; + typedef enum + { + KDP_CONNECT = 0u, + KDP_DISCONNECT, + KDP_HOSTINFO, + KDP_VERSION, + KDP_MAXBYTES, + KDP_READMEM, + KDP_WRITEMEM, + KDP_READREGS, + KDP_WRITEREGS, + KDP_LOAD, + KDP_IMAGEPATH, + KDP_SUSPEND, + KDP_RESUMECPUS, + KDP_EXCEPTION, + KDP_TERMINATION, + KDP_BREAKPOINT_SET, + KDP_BREAKPOINT_REMOVE, + KDP_REGIONS, + KDP_REATTACH, + KDP_HOSTREBOOT, + KDP_READMEM64, + KDP_WRITEMEM64, + KDP_BREAKPOINT_SET64, + KDP_BREAKPOINT_REMOVE64, + KDP_KERNELVERSION, + KDP_READPHYSMEM64, + KDP_WRITEPHYSMEM64, + KDP_READIOPORT, + KDP_WRITEIOPORT, + KDP_READMSR64, + KDP_WRITEMSR64, + KDP_DUMPINFO + } CommandType; + + enum + { + KDP_FEATURE_BP = (1u << 0) + }; + + typedef enum + { + KDP_PROTERR_SUCCESS = 0, + KDP_PROTERR_ALREADY_CONNECTED, + KDP_PROTERR_BAD_NBYTES, + KDP_PROTERR_BADFLAVOR + } KDPError; + + typedef enum + { + ePacketTypeRequest = 0x00u, + ePacketTypeReply = 0x80u, + ePacketTypeMask = 0x80u, + eCommandTypeMask = 0x7fu + } PacketType; + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CommunicationKDP (const char *comm_name); + + virtual + ~CommunicationKDP(); + + bool + SendRequestPacket (const PacketStreamType &request_packet); + + // Wait for a packet within 'nsec' seconds + size_t + WaitForPacketWithTimeoutMicroSeconds (lldb_private::DataExtractor &response, + uint32_t usec); + + bool + GetSequenceMutex(lldb_private::Mutex::Locker& locker); + + bool + CheckForPacket (const uint8_t *src, + size_t src_len, + lldb_private::DataExtractor &packet); + bool + IsRunning() const + { + return m_is_running.GetValue(); + } + + //------------------------------------------------------------------ + // Set the global packet timeout. + // + // For clients, this is the timeout that gets used when sending + // packets and waiting for responses. For servers, this might not + // get used, and if it doesn't this should be moved to the + // CommunicationKDPClient. + //------------------------------------------------------------------ + uint32_t + SetPacketTimeout (uint32_t packet_timeout) + { + const uint32_t old_packet_timeout = m_packet_timeout; + m_packet_timeout = packet_timeout; + return old_packet_timeout; + } + + uint32_t + GetPacketTimeoutInMicroSeconds () const + { + return m_packet_timeout * lldb_private::TimeValue::MicroSecPerSec; + } + + //------------------------------------------------------------------ + // Public Request Packets + //------------------------------------------------------------------ + bool + SendRequestConnect (uint16_t reply_port, + uint16_t exc_port, + const char *greeting); + + bool + SendRequestReattach (uint16_t reply_port); + + bool + SendRequestDisconnect (); + + uint32_t + SendRequestReadMemory (lldb::addr_t addr, + void *dst, + uint32_t dst_size, + lldb_private::Error &error); + + uint32_t + SendRequestWriteMemory (lldb::addr_t addr, + const void *src, + uint32_t src_len, + lldb_private::Error &error); + + bool + SendRawRequest (uint8_t command_byte, + const void *src, + uint32_t src_len, + lldb_private::DataExtractor &reply, + lldb_private::Error &error); + + uint32_t + SendRequestReadRegisters (uint32_t cpu, + uint32_t flavor, + void *dst, + uint32_t dst_size, + lldb_private::Error &error); + + uint32_t + SendRequestWriteRegisters (uint32_t cpu, + uint32_t flavor, + const void *src, + uint32_t src_size, + lldb_private::Error &error); + + const char * + GetKernelVersion (); + + // Disable KDP_IMAGEPATH for now, it seems to hang the KDP connection... + // const char * + // GetImagePath (); + + uint32_t + GetVersion (); + + uint32_t + GetFeatureFlags (); + + bool + LocalBreakpointsAreSupported () + { + return (GetFeatureFlags() & KDP_FEATURE_BP) != 0; + } + + uint32_t + GetCPUMask (); + + uint32_t + GetCPUType (); + + uint32_t + GetCPUSubtype (); + + lldb_private::UUID + GetUUID (); + + bool + RemoteIsEFI (); + + bool + RemoteIsDarwinKernel (); + + lldb::addr_t + GetLoadAddress (); + + bool + SendRequestResume (); + + bool + SendRequestSuspend (); + + bool + SendRequestBreakpoint (bool set, lldb::addr_t addr); + +protected: + + bool + SendRequestPacketNoLock (const PacketStreamType &request_packet); + + size_t + WaitForPacketWithTimeoutMicroSecondsNoLock (lldb_private::DataExtractor &response, + uint32_t timeout_usec); + + bool + WaitForNotRunningPrivate (const lldb_private::TimeValue *timeout_ptr); + + void + MakeRequestPacketHeader (CommandType request_type, + PacketStreamType &request_packet, + uint16_t request_length); + + //------------------------------------------------------------------ + // Protected Request Packets (use public accessors which will cache + // results. + //------------------------------------------------------------------ + bool + SendRequestVersion (); + + bool + SendRequestHostInfo (); + + bool + SendRequestKernelVersion (); + + // Disable KDP_IMAGEPATH for now, it seems to hang the KDP connection... + //bool + //SendRequestImagePath (); + + void + DumpPacket (lldb_private::Stream &s, + const void *data, + uint32_t data_len); + + void + DumpPacket (lldb_private::Stream &s, + const lldb_private::DataExtractor& extractor); + + bool + VersionIsValid() const + { + return m_kdp_version_version != 0; + } + + bool + HostInfoIsValid() const + { + return m_kdp_hostinfo_cpu_type != 0; + } + + bool + ExtractIsReply (uint8_t first_packet_byte) const + { + // TODO: handle big endian... + return (first_packet_byte & ePacketTypeMask) != 0; + } + + CommandType + ExtractCommand (uint8_t first_packet_byte) const + { + // TODO: handle big endian... + return (CommandType)(first_packet_byte & eCommandTypeMask); + } + + static const char * + GetCommandAsCString (uint8_t command); + + void + ClearKDPSettings (); + + bool + SendRequestAndGetReply (const CommandType command, + const PacketStreamType &request_packet, + lldb_private::DataExtractor &reply_packet); + //------------------------------------------------------------------ + // Classes that inherit from CommunicationKDP can see and modify these + //------------------------------------------------------------------ + uint32_t m_addr_byte_size; + lldb::ByteOrder m_byte_order; + uint32_t m_packet_timeout; + lldb_private::Mutex m_sequence_mutex; // Restrict access to sending/receiving packets to a single thread at a time + lldb_private::Predicate<bool> m_is_running; + uint32_t m_session_key; + uint8_t m_request_sequence_id; + uint8_t m_exception_sequence_id; + uint32_t m_kdp_version_version; + uint32_t m_kdp_version_feature; + uint32_t m_kdp_hostinfo_cpu_mask; + uint32_t m_kdp_hostinfo_cpu_type; + uint32_t m_kdp_hostinfo_cpu_subtype; + std::string m_kernel_version; + //std::string m_image_path; // Disable KDP_IMAGEPATH for now, it seems to hang the KDP connection... + lldb::addr_t m_last_read_memory_addr; // Last memory read address for logging +private: + //------------------------------------------------------------------ + // For CommunicationKDP only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (CommunicationKDP); +}; + +#endif // liblldb_CommunicationKDP_h_ diff --git a/source/Plugins/Process/MacOSX-Kernel/Makefile b/source/Plugins/Process/MacOSX-Kernel/Makefile new file mode 100644 index 000000000000..e42f390ffe0d --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Process/MacOSX-Darwin/Makefile -------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginProcessDarwin +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp b/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp new file mode 100644 index 000000000000..628f76d104fe --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp @@ -0,0 +1,1211 @@ +//===-- ProcessKDP.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +#include <errno.h> +#include <stdlib.h> + +// C++ Includes +#include <mutex> + +// Other libraries and framework includes +#include "lldb/Core/Debugger.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/State.h" +#include "lldb/Core/UUID.h" +#include "lldb/Host/ConnectionFileDescriptor.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/Symbols.h" +#include "lldb/Host/ThreadLauncher.h" +#include "lldb/Host/common/TCPSocket.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/OptionGroupString.h" +#include "lldb/Interpreter/OptionGroupUInt64.h" +#include "lldb/Interpreter/OptionValueProperties.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/StringExtractor.h" + +#define USEC_PER_SEC 1000000 + +// Project includes +#include "ProcessKDP.h" +#include "ProcessKDPLog.h" +#include "ThreadKDP.h" +#include "Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.h" +#include "Plugins/DynamicLoader/Static/DynamicLoaderStatic.h" + +using namespace lldb; +using namespace lldb_private; + +namespace { + + static PropertyDefinition + g_properties[] = + { + { "packet-timeout" , OptionValue::eTypeUInt64 , true , 5, NULL, NULL, "Specify the default packet timeout in seconds." }, + { NULL , OptionValue::eTypeInvalid, false, 0, NULL, NULL, NULL } + }; + + enum + { + ePropertyPacketTimeout + }; + + class PluginProperties : public Properties + { + public: + + static ConstString + GetSettingName () + { + return ProcessKDP::GetPluginNameStatic(); + } + + PluginProperties() : + Properties () + { + m_collection_sp.reset (new OptionValueProperties(GetSettingName())); + m_collection_sp->Initialize(g_properties); + } + + virtual + ~PluginProperties() + { + } + + uint64_t + GetPacketTimeout() + { + const uint32_t idx = ePropertyPacketTimeout; + return m_collection_sp->GetPropertyAtIndexAsUInt64(NULL, idx, g_properties[idx].default_uint_value); + } + }; + + typedef std::shared_ptr<PluginProperties> ProcessKDPPropertiesSP; + + static const ProcessKDPPropertiesSP & + GetGlobalPluginProperties() + { + static ProcessKDPPropertiesSP g_settings_sp; + if (!g_settings_sp) + g_settings_sp.reset (new PluginProperties ()); + return g_settings_sp; + } + +} // anonymous namespace end + +static const lldb::tid_t g_kernel_tid = 1; + +ConstString +ProcessKDP::GetPluginNameStatic() +{ + static ConstString g_name("kdp-remote"); + return g_name; +} + +const char * +ProcessKDP::GetPluginDescriptionStatic() +{ + return "KDP Remote protocol based debugging plug-in for darwin kernel debugging."; +} + +void +ProcessKDP::Terminate() +{ + PluginManager::UnregisterPlugin (ProcessKDP::CreateInstance); +} + + +lldb::ProcessSP +ProcessKDP::CreateInstance (TargetSP target_sp, + Listener &listener, + const FileSpec *crash_file_path) +{ + lldb::ProcessSP process_sp; + if (crash_file_path == NULL) + process_sp.reset(new ProcessKDP (target_sp, listener)); + return process_sp; +} + +bool +ProcessKDP::CanDebug(TargetSP target_sp, bool plugin_specified_by_name) +{ + if (plugin_specified_by_name) + return true; + + // For now we are just making sure the file exists for a given module + Module *exe_module = target_sp->GetExecutableModulePointer(); + if (exe_module) + { + const llvm::Triple &triple_ref = target_sp->GetArchitecture().GetTriple(); + switch (triple_ref.getOS()) + { + case llvm::Triple::Darwin: // Should use "macosx" for desktop and "ios" for iOS, but accept darwin just in case + case llvm::Triple::MacOSX: // For desktop targets + case llvm::Triple::IOS: // For arm targets + case llvm::Triple::TvOS: + case llvm::Triple::WatchOS: + if (triple_ref.getVendor() == llvm::Triple::Apple) + { + ObjectFile *exe_objfile = exe_module->GetObjectFile(); + if (exe_objfile->GetType() == ObjectFile::eTypeExecutable && + exe_objfile->GetStrata() == ObjectFile::eStrataKernel) + return true; + } + break; + + default: + break; + } + } + return false; +} + +//---------------------------------------------------------------------- +// ProcessKDP constructor +//---------------------------------------------------------------------- +ProcessKDP::ProcessKDP(TargetSP target_sp, Listener &listener) : + Process (target_sp, listener), + m_comm("lldb.process.kdp-remote.communication"), + m_async_broadcaster (NULL, "lldb.process.kdp-remote.async-broadcaster"), + m_dyld_plugin_name (), + m_kernel_load_addr (LLDB_INVALID_ADDRESS), + m_command_sp(), + m_kernel_thread_wp() +{ + m_async_broadcaster.SetEventName (eBroadcastBitAsyncThreadShouldExit, "async thread should exit"); + m_async_broadcaster.SetEventName (eBroadcastBitAsyncContinue, "async thread continue"); + const uint64_t timeout_seconds = GetGlobalPluginProperties()->GetPacketTimeout(); + if (timeout_seconds > 0) + m_comm.SetPacketTimeout(timeout_seconds); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ProcessKDP::~ProcessKDP() +{ + Clear(); + // We need to call finalize on the process before destroying ourselves + // to make sure all of the broadcaster cleanup goes as planned. If we + // destruct this class, then Process::~Process() might have problems + // trying to fully destroy the broadcaster. + Finalize(); +} + +//---------------------------------------------------------------------- +// PluginInterface +//---------------------------------------------------------------------- +lldb_private::ConstString +ProcessKDP::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ProcessKDP::GetPluginVersion() +{ + return 1; +} + +Error +ProcessKDP::WillLaunch (Module* module) +{ + Error error; + error.SetErrorString ("launching not supported in kdp-remote plug-in"); + return error; +} + +Error +ProcessKDP::WillAttachToProcessWithID (lldb::pid_t pid) +{ + Error error; + error.SetErrorString ("attaching to a by process ID not supported in kdp-remote plug-in"); + return error; +} + +Error +ProcessKDP::WillAttachToProcessWithName (const char *process_name, bool wait_for_launch) +{ + Error error; + error.SetErrorString ("attaching to a by process name not supported in kdp-remote plug-in"); + return error; +} + +bool +ProcessKDP::GetHostArchitecture(ArchSpec &arch) +{ + uint32_t cpu = m_comm.GetCPUType(); + if (cpu) + { + uint32_t sub = m_comm.GetCPUSubtype(); + arch.SetArchitecture(eArchTypeMachO, cpu, sub); + // Leave architecture vendor as unspecified unknown + arch.GetTriple().setVendor(llvm::Triple::UnknownVendor); + arch.GetTriple().setVendorName(llvm::StringRef()); + return true; + } + arch.Clear(); + return false; +} + +Error +ProcessKDP::DoConnectRemote (Stream *strm, const char *remote_url) +{ + Error error; + + // Don't let any JIT happen when doing KDP as we can't allocate + // memory and we don't want to be mucking with threads that might + // already be handling exceptions + SetCanJIT(false); + + if (remote_url == NULL || remote_url[0] == '\0') + { + error.SetErrorStringWithFormat ("invalid connection URL '%s'", remote_url); + return error; + } + + std::unique_ptr<ConnectionFileDescriptor> conn_ap(new ConnectionFileDescriptor()); + if (conn_ap.get()) + { + // Only try once for now. + // TODO: check if we should be retrying? + const uint32_t max_retry_count = 1; + for (uint32_t retry_count = 0; retry_count < max_retry_count; ++retry_count) + { + if (conn_ap->Connect(remote_url, &error) == eConnectionStatusSuccess) + break; + usleep (100000); + } + } + + if (conn_ap->IsConnected()) + { + const TCPSocket& socket = static_cast<const TCPSocket&>(*conn_ap->GetReadObject()); + const uint16_t reply_port = socket.GetLocalPortNumber(); + + if (reply_port != 0) + { + m_comm.SetConnection(conn_ap.release()); + + if (m_comm.SendRequestReattach(reply_port)) + { + if (m_comm.SendRequestConnect(reply_port, reply_port, "Greetings from LLDB...")) + { + m_comm.GetVersion(); + + Target &target = GetTarget(); + ArchSpec kernel_arch; + // The host architecture + GetHostArchitecture(kernel_arch); + ArchSpec target_arch = target.GetArchitecture(); + // Merge in any unspecified stuff into the target architecture in + // case the target arch isn't set at all or incompletely. + target_arch.MergeFrom(kernel_arch); + target.SetArchitecture(target_arch); + + /* Get the kernel's UUID and load address via KDP_KERNELVERSION packet. */ + /* An EFI kdp session has neither UUID nor load address. */ + + UUID kernel_uuid = m_comm.GetUUID (); + addr_t kernel_load_addr = m_comm.GetLoadAddress (); + + if (m_comm.RemoteIsEFI ()) + { + // Select an invalid plugin name for the dynamic loader so one doesn't get used + // since EFI does its own manual loading via python scripting + static ConstString g_none_dynamic_loader("none"); + m_dyld_plugin_name = g_none_dynamic_loader; + + if (kernel_uuid.IsValid()) { + // If EFI passed in a UUID= try to lookup UUID + // The slide will not be provided. But the UUID + // lookup will be used to launch EFI debug scripts + // from the dSYM, that can load all of the symbols. + ModuleSpec module_spec; + module_spec.GetUUID() = kernel_uuid; + module_spec.GetArchitecture() = target.GetArchitecture(); + + // Lookup UUID locally, before attempting dsymForUUID like action + module_spec.GetSymbolFileSpec() = Symbols::LocateExecutableSymbolFile(module_spec); + if (module_spec.GetSymbolFileSpec()) + { + ModuleSpec executable_module_spec = Symbols::LocateExecutableObjectFile (module_spec); + if (executable_module_spec.GetFileSpec().Exists()) + { + module_spec.GetFileSpec() = executable_module_spec.GetFileSpec(); + } + } + if (!module_spec.GetSymbolFileSpec() || !module_spec.GetSymbolFileSpec()) + Symbols::DownloadObjectAndSymbolFile (module_spec, true); + + if (module_spec.GetFileSpec().Exists()) + { + ModuleSP module_sp(new Module (module_spec)); + if (module_sp.get() && module_sp->GetObjectFile()) + { + // Get the current target executable + ModuleSP exe_module_sp (target.GetExecutableModule ()); + + // Make sure you don't already have the right module loaded and they will be uniqued + if (exe_module_sp.get() != module_sp.get()) + target.SetExecutableModule (module_sp, false); + } + } + } + } + else if (m_comm.RemoteIsDarwinKernel ()) + { + m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic(); + if (kernel_load_addr != LLDB_INVALID_ADDRESS) + { + m_kernel_load_addr = kernel_load_addr; + } + } + + // Set the thread ID + UpdateThreadListIfNeeded (); + SetID (1); + GetThreadList (); + SetPrivateState (eStateStopped); + StreamSP async_strm_sp(target.GetDebugger().GetAsyncOutputStream()); + if (async_strm_sp) + { + const char *cstr; + if ((cstr = m_comm.GetKernelVersion ()) != NULL) + { + async_strm_sp->Printf ("Version: %s\n", cstr); + async_strm_sp->Flush(); + } +// if ((cstr = m_comm.GetImagePath ()) != NULL) +// { +// async_strm_sp->Printf ("Image Path: %s\n", cstr); +// async_strm_sp->Flush(); +// } + } + } + else + { + error.SetErrorString("KDP_REATTACH failed"); + } + } + else + { + error.SetErrorString("KDP_REATTACH failed"); + } + } + else + { + error.SetErrorString("invalid reply port from UDP connection"); + } + } + else + { + if (error.Success()) + error.SetErrorStringWithFormat ("failed to connect to '%s'", remote_url); + } + if (error.Fail()) + m_comm.Disconnect(); + + return error; +} + +//---------------------------------------------------------------------- +// Process Control +//---------------------------------------------------------------------- +Error +ProcessKDP::DoLaunch (Module *exe_module, + ProcessLaunchInfo &launch_info) +{ + Error error; + error.SetErrorString ("launching not supported in kdp-remote plug-in"); + return error; +} + +Error +ProcessKDP::DoAttachToProcessWithID (lldb::pid_t attach_pid, const ProcessAttachInfo &attach_info) +{ + Error error; + error.SetErrorString ("attach to process by ID is not suppported in kdp remote debugging"); + return error; +} + +Error +ProcessKDP::DoAttachToProcessWithName (const char *process_name, const ProcessAttachInfo &attach_info) +{ + Error error; + error.SetErrorString ("attach to process by name is not suppported in kdp remote debugging"); + return error; +} + + +void +ProcessKDP::DidAttach (ArchSpec &process_arch) +{ + Process::DidAttach(process_arch); + + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PROCESS)); + if (log) + log->Printf ("ProcessKDP::DidAttach()"); + if (GetID() != LLDB_INVALID_PROCESS_ID) + { + GetHostArchitecture(process_arch); + } +} + +addr_t +ProcessKDP::GetImageInfoAddress() +{ + return m_kernel_load_addr; +} + +lldb_private::DynamicLoader * +ProcessKDP::GetDynamicLoader () +{ + if (m_dyld_ap.get() == NULL) + m_dyld_ap.reset (DynamicLoader::FindPlugin(this, m_dyld_plugin_name.IsEmpty() ? NULL : m_dyld_plugin_name.GetCString())); + return m_dyld_ap.get(); +} + +Error +ProcessKDP::WillResume () +{ + return Error(); +} + +Error +ProcessKDP::DoResume () +{ + Error error; + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PROCESS)); + // Only start the async thread if we try to do any process control + if (!m_async_thread.IsJoinable()) + StartAsyncThread(); + + bool resume = false; + + // With KDP there is only one thread we can tell what to do + ThreadSP kernel_thread_sp (m_thread_list.FindThreadByProtocolID(g_kernel_tid)); + + if (kernel_thread_sp) + { + const StateType thread_resume_state = kernel_thread_sp->GetTemporaryResumeState(); + + if (log) + log->Printf ("ProcessKDP::DoResume() thread_resume_state = %s", StateAsCString(thread_resume_state)); + switch (thread_resume_state) + { + case eStateSuspended: + // Nothing to do here when a thread will stay suspended + // we just leave the CPU mask bit set to zero for the thread + if (log) + log->Printf ("ProcessKDP::DoResume() = suspended???"); + break; + + case eStateStepping: + { + lldb::RegisterContextSP reg_ctx_sp (kernel_thread_sp->GetRegisterContext()); + + if (reg_ctx_sp) + { + if (log) + log->Printf ("ProcessKDP::DoResume () reg_ctx_sp->HardwareSingleStep (true);"); + reg_ctx_sp->HardwareSingleStep (true); + resume = true; + } + else + { + error.SetErrorStringWithFormat("KDP thread 0x%llx has no register context", kernel_thread_sp->GetID()); + } + } + break; + + case eStateRunning: + { + lldb::RegisterContextSP reg_ctx_sp (kernel_thread_sp->GetRegisterContext()); + + if (reg_ctx_sp) + { + if (log) + log->Printf ("ProcessKDP::DoResume () reg_ctx_sp->HardwareSingleStep (false);"); + reg_ctx_sp->HardwareSingleStep (false); + resume = true; + } + else + { + error.SetErrorStringWithFormat("KDP thread 0x%llx has no register context", kernel_thread_sp->GetID()); + } + } + break; + + default: + // The only valid thread resume states are listed above + assert (!"invalid thread resume state"); + break; + } + } + + if (resume) + { + if (log) + log->Printf ("ProcessKDP::DoResume () sending resume"); + + if (m_comm.SendRequestResume ()) + { + m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncContinue); + SetPrivateState(eStateRunning); + } + else + error.SetErrorString ("KDP resume failed"); + } + else + { + error.SetErrorString ("kernel thread is suspended"); + } + + return error; +} + +lldb::ThreadSP +ProcessKDP::GetKernelThread() +{ + // KDP only tells us about one thread/core. Any other threads will usually + // be the ones that are read from memory by the OS plug-ins. + + ThreadSP thread_sp (m_kernel_thread_wp.lock()); + if (!thread_sp) + { + thread_sp.reset(new ThreadKDP (*this, g_kernel_tid)); + m_kernel_thread_wp = thread_sp; + } + return thread_sp; +} + + + + +bool +ProcessKDP::UpdateThreadList (ThreadList &old_thread_list, ThreadList &new_thread_list) +{ + // locker will keep a mutex locked until it goes out of scope + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_THREAD)); + if (log && log->GetMask().Test(KDP_LOG_VERBOSE)) + log->Printf ("ProcessKDP::%s (pid = %" PRIu64 ")", __FUNCTION__, GetID()); + + // Even though there is a CPU mask, it doesn't mean we can see each CPU + // individually, there is really only one. Lets call this thread 1. + ThreadSP thread_sp (old_thread_list.FindThreadByProtocolID(g_kernel_tid, false)); + if (!thread_sp) + thread_sp = GetKernelThread (); + new_thread_list.AddThread(thread_sp); + + return new_thread_list.GetSize(false) > 0; +} + +void +ProcessKDP::RefreshStateAfterStop () +{ + // Let all threads recover from stopping and do any clean up based + // on the previous thread state (if any). + m_thread_list.RefreshStateAfterStop(); +} + +Error +ProcessKDP::DoHalt (bool &caused_stop) +{ + Error error; + + if (m_comm.IsRunning()) + { + if (m_destroy_in_process) + { + // If we are attemping to destroy, we need to not return an error to + // Halt or DoDestroy won't get called. + // We are also currently running, so send a process stopped event + SetPrivateState (eStateStopped); + } + else + { + error.SetErrorString ("KDP cannot interrupt a running kernel"); + } + } + return error; +} + +Error +ProcessKDP::DoDetach(bool keep_stopped) +{ + Error error; + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_PROCESS)); + if (log) + log->Printf ("ProcessKDP::DoDetach(keep_stopped = %i)", keep_stopped); + + if (m_comm.IsRunning()) + { + // We are running and we can't interrupt a running kernel, so we need + // to just close the connection to the kernel and hope for the best + } + else + { + // If we are going to keep the target stopped, then don't send the disconnect message. + if (!keep_stopped && m_comm.IsConnected()) + { + const bool success = m_comm.SendRequestDisconnect(); + if (log) + { + if (success) + log->PutCString ("ProcessKDP::DoDetach() detach packet sent successfully"); + else + log->PutCString ("ProcessKDP::DoDetach() connection channel shutdown failed"); + } + m_comm.Disconnect (); + } + } + StopAsyncThread (); + m_comm.Clear(); + + SetPrivateState (eStateDetached); + ResumePrivateStateThread(); + + //KillDebugserverProcess (); + return error; +} + +Error +ProcessKDP::DoDestroy () +{ + // For KDP there really is no difference between destroy and detach + bool keep_stopped = false; + return DoDetach(keep_stopped); +} + +//------------------------------------------------------------------ +// Process Queries +//------------------------------------------------------------------ + +bool +ProcessKDP::IsAlive () +{ + return m_comm.IsConnected() && Process::IsAlive(); +} + +//------------------------------------------------------------------ +// Process Memory +//------------------------------------------------------------------ +size_t +ProcessKDP::DoReadMemory (addr_t addr, void *buf, size_t size, Error &error) +{ + uint8_t *data_buffer = (uint8_t *) buf; + if (m_comm.IsConnected()) + { + const size_t max_read_size = 512; + size_t total_bytes_read = 0; + + // Read the requested amount of memory in 512 byte chunks + while (total_bytes_read < size) + { + size_t bytes_to_read_this_request = size - total_bytes_read; + if (bytes_to_read_this_request > max_read_size) + { + bytes_to_read_this_request = max_read_size; + } + size_t bytes_read = m_comm.SendRequestReadMemory (addr + total_bytes_read, + data_buffer + total_bytes_read, + bytes_to_read_this_request, error); + total_bytes_read += bytes_read; + if (error.Fail() || bytes_read == 0) + { + return total_bytes_read; + } + } + + return total_bytes_read; + } + error.SetErrorString ("not connected"); + return 0; +} + +size_t +ProcessKDP::DoWriteMemory (addr_t addr, const void *buf, size_t size, Error &error) +{ + if (m_comm.IsConnected()) + return m_comm.SendRequestWriteMemory (addr, buf, size, error); + error.SetErrorString ("not connected"); + return 0; +} + +lldb::addr_t +ProcessKDP::DoAllocateMemory (size_t size, uint32_t permissions, Error &error) +{ + error.SetErrorString ("memory allocation not suppported in kdp remote debugging"); + return LLDB_INVALID_ADDRESS; +} + +Error +ProcessKDP::DoDeallocateMemory (lldb::addr_t addr) +{ + Error error; + error.SetErrorString ("memory deallocation not suppported in kdp remote debugging"); + return error; +} + +Error +ProcessKDP::EnableBreakpointSite (BreakpointSite *bp_site) +{ + if (m_comm.LocalBreakpointsAreSupported ()) + { + Error error; + if (!bp_site->IsEnabled()) + { + if (m_comm.SendRequestBreakpoint(true, bp_site->GetLoadAddress())) + { + bp_site->SetEnabled(true); + bp_site->SetType (BreakpointSite::eExternal); + } + else + { + error.SetErrorString ("KDP set breakpoint failed"); + } + } + return error; + } + return EnableSoftwareBreakpoint (bp_site); +} + +Error +ProcessKDP::DisableBreakpointSite (BreakpointSite *bp_site) +{ + if (m_comm.LocalBreakpointsAreSupported ()) + { + Error error; + if (bp_site->IsEnabled()) + { + BreakpointSite::Type bp_type = bp_site->GetType(); + if (bp_type == BreakpointSite::eExternal) + { + if (m_destroy_in_process && m_comm.IsRunning()) + { + // We are trying to destroy our connection and we are running + bp_site->SetEnabled(false); + } + else + { + if (m_comm.SendRequestBreakpoint(false, bp_site->GetLoadAddress())) + bp_site->SetEnabled(false); + else + error.SetErrorString ("KDP remove breakpoint failed"); + } + } + else + { + error = DisableSoftwareBreakpoint (bp_site); + } + } + return error; + } + return DisableSoftwareBreakpoint (bp_site); +} + +Error +ProcessKDP::EnableWatchpoint (Watchpoint *wp, bool notify) +{ + Error error; + error.SetErrorString ("watchpoints are not suppported in kdp remote debugging"); + return error; +} + +Error +ProcessKDP::DisableWatchpoint (Watchpoint *wp, bool notify) +{ + Error error; + error.SetErrorString ("watchpoints are not suppported in kdp remote debugging"); + return error; +} + +void +ProcessKDP::Clear() +{ + m_thread_list.Clear(); +} + +Error +ProcessKDP::DoSignal (int signo) +{ + Error error; + error.SetErrorString ("sending signals is not suppported in kdp remote debugging"); + return error; +} + +void +ProcessKDP::Initialize() +{ + static std::once_flag g_once_flag; + + std::call_once(g_once_flag, []() + { + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance, + DebuggerInitialize); + + Log::Callbacks log_callbacks = { + ProcessKDPLog::DisableLog, + ProcessKDPLog::EnableLog, + ProcessKDPLog::ListLogCategories + }; + + Log::RegisterLogChannel (ProcessKDP::GetPluginNameStatic(), log_callbacks); + }); +} + +void +ProcessKDP::DebuggerInitialize (lldb_private::Debugger &debugger) +{ + if (!PluginManager::GetSettingForProcessPlugin(debugger, PluginProperties::GetSettingName())) + { + const bool is_global_setting = true; + PluginManager::CreateSettingForProcessPlugin (debugger, + GetGlobalPluginProperties()->GetValueProperties(), + ConstString ("Properties for the kdp-remote process plug-in."), + is_global_setting); + } +} + +bool +ProcessKDP::StartAsyncThread () +{ + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_PROCESS)); + + if (log) + log->Printf ("ProcessKDP::StartAsyncThread ()"); + + if (m_async_thread.IsJoinable()) + return true; + + m_async_thread = ThreadLauncher::LaunchThread("<lldb.process.kdp-remote.async>", ProcessKDP::AsyncThread, this, NULL); + return m_async_thread.IsJoinable(); +} + +void +ProcessKDP::StopAsyncThread () +{ + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_PROCESS)); + + if (log) + log->Printf ("ProcessKDP::StopAsyncThread ()"); + + m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncThreadShouldExit); + + // Stop the stdio thread + if (m_async_thread.IsJoinable()) + m_async_thread.Join(nullptr); +} + + +void * +ProcessKDP::AsyncThread (void *arg) +{ + ProcessKDP *process = (ProcessKDP*) arg; + + const lldb::pid_t pid = process->GetID(); + + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PROCESS)); + if (log) + log->Printf ("ProcessKDP::AsyncThread (arg = %p, pid = %" PRIu64 ") thread starting...", arg, pid); + + Listener listener ("ProcessKDP::AsyncThread"); + EventSP event_sp; + const uint32_t desired_event_mask = eBroadcastBitAsyncContinue | + eBroadcastBitAsyncThreadShouldExit; + + + if (listener.StartListeningForEvents (&process->m_async_broadcaster, desired_event_mask) == desired_event_mask) + { + bool done = false; + while (!done) + { + if (log) + log->Printf ("ProcessKDP::AsyncThread (pid = %" PRIu64 ") listener.WaitForEvent (NULL, event_sp)...", + pid); + if (listener.WaitForEvent (NULL, event_sp)) + { + uint32_t event_type = event_sp->GetType(); + if (log) + log->Printf ("ProcessKDP::AsyncThread (pid = %" PRIu64 ") Got an event of type: %d...", + pid, + event_type); + + // When we are running, poll for 1 second to try and get an exception + // to indicate the process has stopped. If we don't get one, check to + // make sure no one asked us to exit + bool is_running = false; + DataExtractor exc_reply_packet; + do + { + switch (event_type) + { + case eBroadcastBitAsyncContinue: + { + is_running = true; + if (process->m_comm.WaitForPacketWithTimeoutMicroSeconds (exc_reply_packet, 1 * USEC_PER_SEC)) + { + ThreadSP thread_sp (process->GetKernelThread()); + if (thread_sp) + { + lldb::RegisterContextSP reg_ctx_sp (thread_sp->GetRegisterContext()); + if (reg_ctx_sp) + reg_ctx_sp->InvalidateAllRegisters(); + static_cast<ThreadKDP *>(thread_sp.get())->SetStopInfoFrom_KDP_EXCEPTION (exc_reply_packet); + } + + // TODO: parse the stop reply packet + is_running = false; + process->SetPrivateState(eStateStopped); + } + else + { + // Check to see if we are supposed to exit. There is no way to + // interrupt a running kernel, so all we can do is wait for an + // exception or detach... + if (listener.GetNextEvent(event_sp)) + { + // We got an event, go through the loop again + event_type = event_sp->GetType(); + } + } + } + break; + + case eBroadcastBitAsyncThreadShouldExit: + if (log) + log->Printf ("ProcessKDP::AsyncThread (pid = %" PRIu64 ") got eBroadcastBitAsyncThreadShouldExit...", + pid); + done = true; + is_running = false; + break; + + default: + if (log) + log->Printf ("ProcessKDP::AsyncThread (pid = %" PRIu64 ") got unknown event 0x%8.8x", + pid, + event_type); + done = true; + is_running = false; + break; + } + } while (is_running); + } + else + { + if (log) + log->Printf ("ProcessKDP::AsyncThread (pid = %" PRIu64 ") listener.WaitForEvent (NULL, event_sp) => false", + pid); + done = true; + } + } + } + + if (log) + log->Printf ("ProcessKDP::AsyncThread (arg = %p, pid = %" PRIu64 ") thread exiting...", + arg, + pid); + + process->m_async_thread.Reset(); + return NULL; +} + + +class CommandObjectProcessKDPPacketSend : public CommandObjectParsed +{ +private: + + OptionGroupOptions m_option_group; + OptionGroupUInt64 m_command_byte; + OptionGroupString m_packet_data; + + virtual Options * + GetOptions () + { + return &m_option_group; + } + + +public: + CommandObjectProcessKDPPacketSend(CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "process plugin packet send", + "Send a custom packet through the KDP protocol by specifying the command byte and the packet payload data. A packet will be sent with a correct header and payload, and the raw result bytes will be displayed as a string value. ", + NULL), + m_option_group (interpreter), + m_command_byte(LLDB_OPT_SET_1, true , "command", 'c', 0, eArgTypeNone, "Specify the command byte to use when sending the KDP request packet.", 0), + m_packet_data (LLDB_OPT_SET_1, false, "payload", 'p', 0, eArgTypeNone, "Specify packet payload bytes as a hex ASCII string with no spaces or hex prefixes.", NULL) + { + m_option_group.Append (&m_command_byte, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Append (&m_packet_data , LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Finalize(); + } + + ~CommandObjectProcessKDPPacketSend () + { + } + + bool + DoExecute (Args& command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + if (argc == 0) + { + if (!m_command_byte.GetOptionValue().OptionWasSet()) + { + result.AppendError ("the --command option must be set to a valid command byte"); + result.SetStatus (eReturnStatusFailed); + } + else + { + const uint64_t command_byte = m_command_byte.GetOptionValue().GetUInt64Value(0); + if (command_byte > 0 && command_byte <= UINT8_MAX) + { + ProcessKDP *process = (ProcessKDP *)m_interpreter.GetExecutionContext().GetProcessPtr(); + if (process) + { + const StateType state = process->GetState(); + + if (StateIsStoppedState (state, true)) + { + std::vector<uint8_t> payload_bytes; + const char *ascii_hex_bytes_cstr = m_packet_data.GetOptionValue().GetCurrentValue(); + if (ascii_hex_bytes_cstr && ascii_hex_bytes_cstr[0]) + { + StringExtractor extractor(ascii_hex_bytes_cstr); + const size_t ascii_hex_bytes_cstr_len = extractor.GetStringRef().size(); + if (ascii_hex_bytes_cstr_len & 1) + { + result.AppendErrorWithFormat ("payload data must contain an even number of ASCII hex characters: '%s'", ascii_hex_bytes_cstr); + result.SetStatus (eReturnStatusFailed); + return false; + } + payload_bytes.resize(ascii_hex_bytes_cstr_len/2); + if (extractor.GetHexBytes(&payload_bytes[0], payload_bytes.size(), '\xdd') != payload_bytes.size()) + { + result.AppendErrorWithFormat ("payload data must only contain ASCII hex characters (no spaces or hex prefixes): '%s'", ascii_hex_bytes_cstr); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + Error error; + DataExtractor reply; + process->GetCommunication().SendRawRequest (command_byte, + payload_bytes.empty() ? NULL : payload_bytes.data(), + payload_bytes.size(), + reply, + error); + + if (error.Success()) + { + // Copy the binary bytes into a hex ASCII string for the result + StreamString packet; + packet.PutBytesAsRawHex8(reply.GetDataStart(), + reply.GetByteSize(), + endian::InlHostByteOrder(), + endian::InlHostByteOrder()); + result.AppendMessage(packet.GetString().c_str()); + result.SetStatus (eReturnStatusSuccessFinishResult); + return true; + } + else + { + const char *error_cstr = error.AsCString(); + if (error_cstr && error_cstr[0]) + result.AppendError (error_cstr); + else + result.AppendErrorWithFormat ("unknown error 0x%8.8x", error.GetError()); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + else + { + result.AppendErrorWithFormat ("process must be stopped in order to send KDP packets, state is %s", StateAsCString (state)); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError ("invalid process"); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendErrorWithFormat ("invalid command byte 0x%" PRIx64 ", valid values are 1 - 255", command_byte); + result.SetStatus (eReturnStatusFailed); + } + } + } + else + { + result.AppendErrorWithFormat ("'%s' takes no arguments, only options.", m_cmd_name.c_str()); + result.SetStatus (eReturnStatusFailed); + } + return false; + } +}; + +class CommandObjectProcessKDPPacket : public CommandObjectMultiword +{ +private: + +public: + CommandObjectProcessKDPPacket(CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "process plugin packet", + "Commands that deal with KDP remote packets.", + NULL) + { + LoadSubCommand ("send", CommandObjectSP (new CommandObjectProcessKDPPacketSend (interpreter))); + } + + ~CommandObjectProcessKDPPacket () + { + } +}; + +class CommandObjectMultiwordProcessKDP : public CommandObjectMultiword +{ +public: + CommandObjectMultiwordProcessKDP (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "process plugin", + "A set of commands for operating on a ProcessKDP process.", + "process plugin <subcommand> [<subcommand-options>]") + { + LoadSubCommand ("packet", CommandObjectSP (new CommandObjectProcessKDPPacket (interpreter))); + } + + ~CommandObjectMultiwordProcessKDP () + { + } +}; + +CommandObject * +ProcessKDP::GetPluginCommandObject() +{ + if (!m_command_sp) + m_command_sp.reset (new CommandObjectMultiwordProcessKDP (GetTarget().GetDebugger().GetCommandInterpreter())); + return m_command_sp.get(); +} + diff --git a/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h b/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h new file mode 100644 index 000000000000..fe9a4e2844bf --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h @@ -0,0 +1,274 @@ +//===-- ProcessKDP.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ProcessKDP_h_ +#define liblldb_ProcessKDP_h_ + +// C Includes + +// C++ Includes +#include <list> +#include <vector> + +// Other libraries and framework includes +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/StringList.h" +#include "lldb/Core/ThreadSafeValue.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" + +#include "CommunicationKDP.h" + +class ThreadKDP; + +class ProcessKDP : public lldb_private::Process +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + static lldb::ProcessSP + CreateInstance (lldb::TargetSP target_sp, + lldb_private::Listener &listener, + const lldb_private::FileSpec *crash_file_path); + + static void + Initialize(); + + static void + DebuggerInitialize (lldb_private::Debugger &debugger); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + ProcessKDP(lldb::TargetSP target_sp, lldb_private::Listener &listener); + + virtual + ~ProcessKDP(); + + //------------------------------------------------------------------ + // Check if a given Process + //------------------------------------------------------------------ + virtual bool + CanDebug (lldb::TargetSP target_sp, + bool plugin_specified_by_name); + + virtual lldb_private::CommandObject * + GetPluginCommandObject(); + + //------------------------------------------------------------------ + // Creating a new process, or attaching to an existing one + //------------------------------------------------------------------ + virtual lldb_private::Error + WillLaunch (lldb_private::Module* module); + + virtual lldb_private::Error + DoLaunch (lldb_private::Module *exe_module, + lldb_private::ProcessLaunchInfo &launch_info); + + virtual lldb_private::Error + WillAttachToProcessWithID (lldb::pid_t pid); + + virtual lldb_private::Error + WillAttachToProcessWithName (const char *process_name, bool wait_for_launch); + + virtual lldb_private::Error + DoConnectRemote (lldb_private::Stream *strm, const char *remote_url); + + virtual lldb_private::Error + DoAttachToProcessWithID (lldb::pid_t pid, const lldb_private::ProcessAttachInfo &attach_info); + + virtual lldb_private::Error + DoAttachToProcessWithName (const char *process_name, const lldb_private::ProcessAttachInfo &attach_info); + + virtual void + DidAttach (lldb_private::ArchSpec &process_arch); + + lldb::addr_t + GetImageInfoAddress(); + + lldb_private::DynamicLoader * + GetDynamicLoader (); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + + //------------------------------------------------------------------ + // Process Control + //------------------------------------------------------------------ + virtual lldb_private::Error + WillResume (); + + virtual lldb_private::Error + DoResume (); + + virtual lldb_private::Error + DoHalt (bool &caused_stop); + + virtual lldb_private::Error + DoDetach (bool keep_stopped); + + virtual lldb_private::Error + DoSignal (int signal); + + virtual lldb_private::Error + DoDestroy (); + + virtual void + RefreshStateAfterStop(); + + //------------------------------------------------------------------ + // Process Queries + //------------------------------------------------------------------ + virtual bool + IsAlive (); + + //------------------------------------------------------------------ + // Process Memory + //------------------------------------------------------------------ + virtual size_t + DoReadMemory (lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error); + + virtual size_t + DoWriteMemory (lldb::addr_t addr, const void *buf, size_t size, lldb_private::Error &error); + + virtual lldb::addr_t + DoAllocateMemory (size_t size, uint32_t permissions, lldb_private::Error &error); + + virtual lldb_private::Error + DoDeallocateMemory (lldb::addr_t ptr); + + //---------------------------------------------------------------------- + // Process Breakpoints + //---------------------------------------------------------------------- + virtual lldb_private::Error + EnableBreakpointSite (lldb_private::BreakpointSite *bp_site); + + virtual lldb_private::Error + DisableBreakpointSite (lldb_private::BreakpointSite *bp_site); + + //---------------------------------------------------------------------- + // Process Watchpoints + //---------------------------------------------------------------------- + virtual lldb_private::Error + EnableWatchpoint (lldb_private::Watchpoint *wp, bool notify = true); + + virtual lldb_private::Error + DisableWatchpoint (lldb_private::Watchpoint *wp, bool notify = true); + + CommunicationKDP & + GetCommunication() + { + return m_comm; + } + +protected: + friend class ThreadKDP; + friend class CommunicationKDP; + + //---------------------------------------------------------------------- + // Accessors + //---------------------------------------------------------------------- + bool + IsRunning ( lldb::StateType state ) + { + return state == lldb::eStateRunning || IsStepping(state); + } + + bool + IsStepping ( lldb::StateType state) + { + return state == lldb::eStateStepping; + } + + bool + CanResume ( lldb::StateType state) + { + return state == lldb::eStateStopped; + } + + bool + HasExited (lldb::StateType state) + { + return state == lldb::eStateExited; + } + + bool + GetHostArchitecture (lldb_private::ArchSpec &arch); + + bool + ProcessIDIsValid ( ) const; + + void + Clear ( ); + + virtual bool + UpdateThreadList (lldb_private::ThreadList &old_thread_list, + lldb_private::ThreadList &new_thread_list); + + enum + { + eBroadcastBitAsyncContinue = (1 << 0), + eBroadcastBitAsyncThreadShouldExit = (1 << 1) + }; + + lldb::ThreadSP + GetKernelThread (); + + //------------------------------------------------------------------ + /// Broadcaster event bits definitions. + //------------------------------------------------------------------ + CommunicationKDP m_comm; + lldb_private::Broadcaster m_async_broadcaster; + lldb_private::HostThread m_async_thread; + lldb_private::ConstString m_dyld_plugin_name; + lldb::addr_t m_kernel_load_addr; + lldb::CommandObjectSP m_command_sp; + lldb::ThreadWP m_kernel_thread_wp; + + + bool + StartAsyncThread (); + + void + StopAsyncThread (); + + static void * + AsyncThread (void *arg); + +private: + //------------------------------------------------------------------ + // For ProcessKDP only + //------------------------------------------------------------------ + + DISALLOW_COPY_AND_ASSIGN (ProcessKDP); + +}; + +#endif // liblldb_ProcessKDP_h_ diff --git a/source/Plugins/Process/MacOSX-Kernel/ProcessKDPLog.cpp b/source/Plugins/Process/MacOSX-Kernel/ProcessKDPLog.cpp new file mode 100644 index 000000000000..79cb62aa0066 --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/ProcessKDPLog.cpp @@ -0,0 +1,186 @@ +//===-- ProcessKDPLog.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ProcessKDPLog.h" + +#include "lldb/Interpreter/Args.h" +#include "lldb/Core/StreamFile.h" + +#include "ProcessKDP.h" + +using namespace lldb; +using namespace lldb_private; + + +// We want to avoid global constructors where code needs to be run so here we +// control access to our static g_log_sp by hiding it in a singleton function +// that will construct the static g_log_sp the first time this function is +// called. +static bool g_log_enabled = false; +static Log * g_log = NULL; +static Log * +GetLog () +{ + if (!g_log_enabled) + return NULL; + return g_log; +} + +Log * +ProcessKDPLog::GetLogIfAllCategoriesSet (uint32_t mask) +{ + Log *log(GetLog ()); + if (log && mask) + { + uint32_t log_mask = log->GetMask().Get(); + if ((log_mask & mask) != mask) + return NULL; + } + return log; +} + +void +ProcessKDPLog::DisableLog (const char **categories, Stream *feedback_strm) +{ + Log *log (GetLog ()); + if (log) + { + uint32_t flag_bits = 0; + + if (categories[0] != NULL) + { + flag_bits = log->GetMask().Get(); + for (size_t i = 0; categories[i] != NULL; ++i) + { + const char *arg = categories[i]; + + + if (::strcasecmp (arg, "all") == 0 ) flag_bits &= ~KDP_LOG_ALL; + else if (::strcasecmp (arg, "async") == 0 ) flag_bits &= ~KDP_LOG_ASYNC; + else if (::strncasecmp (arg, "break", 5) == 0 ) flag_bits &= ~KDP_LOG_BREAKPOINTS; + else if (::strncasecmp (arg, "comm", 4) == 0 ) flag_bits &= ~KDP_LOG_COMM; + else if (::strcasecmp (arg, "default") == 0 ) flag_bits &= ~KDP_LOG_DEFAULT; + else if (::strcasecmp (arg, "packets") == 0 ) flag_bits &= ~KDP_LOG_PACKETS; + else if (::strcasecmp (arg, "memory") == 0 ) flag_bits &= ~KDP_LOG_MEMORY; + else if (::strcasecmp (arg, "data-short") == 0 ) flag_bits &= ~KDP_LOG_MEMORY_DATA_SHORT; + else if (::strcasecmp (arg, "data-long") == 0 ) flag_bits &= ~KDP_LOG_MEMORY_DATA_LONG; + else if (::strcasecmp (arg, "process") == 0 ) flag_bits &= ~KDP_LOG_PROCESS; + else if (::strcasecmp (arg, "step") == 0 ) flag_bits &= ~KDP_LOG_STEP; + else if (::strcasecmp (arg, "thread") == 0 ) flag_bits &= ~KDP_LOG_THREAD; + else if (::strcasecmp (arg, "verbose") == 0 ) flag_bits &= ~KDP_LOG_VERBOSE; + else if (::strncasecmp (arg, "watch", 5) == 0 ) flag_bits &= ~KDP_LOG_WATCHPOINTS; + else + { + feedback_strm->Printf("error: unrecognized log category '%s'\n", arg); + ListLogCategories (feedback_strm); + } + + } + } + + log->GetMask().Reset (flag_bits); + if (flag_bits == 0) + g_log_enabled = false; + } + + return; +} + +Log * +ProcessKDPLog::EnableLog (StreamSP &log_stream_sp, uint32_t log_options, const char **categories, Stream *feedback_strm) +{ + // Try see if there already is a log - that way we can reuse its settings. + // We could reuse the log in toto, but we don't know that the stream is the same. + uint32_t flag_bits = 0; + if (g_log) + flag_bits = g_log->GetMask().Get(); + + // Now make a new log with this stream if one was provided + if (log_stream_sp) + { + if (g_log) + g_log->SetStream(log_stream_sp); + else + g_log = new Log(log_stream_sp); + } + + if (g_log) + { + bool got_unknown_category = false; + for (size_t i=0; categories[i] != NULL; ++i) + { + const char *arg = categories[i]; + + if (::strcasecmp (arg, "all") == 0 ) flag_bits |= KDP_LOG_ALL; + else if (::strcasecmp (arg, "async") == 0 ) flag_bits |= KDP_LOG_ASYNC; + else if (::strncasecmp (arg, "break", 5) == 0 ) flag_bits |= KDP_LOG_BREAKPOINTS; + else if (::strncasecmp (arg, "comm", 4) == 0 ) flag_bits |= KDP_LOG_COMM; + else if (::strcasecmp (arg, "default") == 0 ) flag_bits |= KDP_LOG_DEFAULT; + else if (::strcasecmp (arg, "packets") == 0 ) flag_bits |= KDP_LOG_PACKETS; + else if (::strcasecmp (arg, "memory") == 0 ) flag_bits |= KDP_LOG_MEMORY; + else if (::strcasecmp (arg, "data-short") == 0 ) flag_bits |= KDP_LOG_MEMORY_DATA_SHORT; + else if (::strcasecmp (arg, "data-long") == 0 ) flag_bits |= KDP_LOG_MEMORY_DATA_LONG; + else if (::strcasecmp (arg, "process") == 0 ) flag_bits |= KDP_LOG_PROCESS; + else if (::strcasecmp (arg, "step") == 0 ) flag_bits |= KDP_LOG_STEP; + else if (::strcasecmp (arg, "thread") == 0 ) flag_bits |= KDP_LOG_THREAD; + else if (::strcasecmp (arg, "verbose") == 0 ) flag_bits |= KDP_LOG_VERBOSE; + else if (::strncasecmp (arg, "watch", 5) == 0 ) flag_bits |= KDP_LOG_WATCHPOINTS; + else + { + feedback_strm->Printf("error: unrecognized log category '%s'\n", arg); + if (got_unknown_category == false) + { + got_unknown_category = true; + ListLogCategories (feedback_strm); + } + } + } + if (flag_bits == 0) + flag_bits = KDP_LOG_DEFAULT; + g_log->GetMask().Reset(flag_bits); + g_log->GetOptions().Reset(log_options); + } + g_log_enabled = true; + return g_log; +} + +void +ProcessKDPLog::ListLogCategories (Stream *strm) +{ + strm->Printf ("Logging categories for '%s':\n" + " all - turn on all available logging categories\n" + " async - log asynchronous activity\n" + " break - log breakpoints\n" + " communication - log communication activity\n" + " default - enable the default set of logging categories for liblldb\n" + " packets - log gdb remote packets\n" + " memory - log memory reads and writes\n" + " data-short - log memory bytes for memory reads and writes for short transactions only\n" + " data-long - log memory bytes for memory reads and writes for all transactions\n" + " process - log process events and activities\n" + " thread - log thread events and activities\n" + " step - log step related activities\n" + " verbose - enable verbose logging\n" + " watch - log watchpoint related activities\n", + ProcessKDP::GetPluginNameStatic().GetCString()); +} + + +void +ProcessKDPLog::LogIf (uint32_t mask, const char *format, ...) +{ + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet (mask)); + if (log) + { + va_list args; + va_start (args, format); + log->VAPrintf (format, args); + va_end (args); + } +} diff --git a/source/Plugins/Process/MacOSX-Kernel/ProcessKDPLog.h b/source/Plugins/Process/MacOSX-Kernel/ProcessKDPLog.h new file mode 100644 index 000000000000..0cb32d9b2dcf --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/ProcessKDPLog.h @@ -0,0 +1,54 @@ +//===-- ProcessKDPLog.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ProcessKDPLog_h_ +#define liblldb_ProcessKDPLog_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes + +// Project includes +#include "lldb/Core/Log.h" + +#define KDP_LOG_VERBOSE (1u << 0) +#define KDP_LOG_PROCESS (1u << 1) +#define KDP_LOG_THREAD (1u << 2) +#define KDP_LOG_PACKETS (1u << 3) +#define KDP_LOG_MEMORY (1u << 4) // Log memory reads/writes calls +#define KDP_LOG_MEMORY_DATA_SHORT (1u << 5) // Log short memory reads/writes bytes +#define KDP_LOG_MEMORY_DATA_LONG (1u << 6) // Log all memory reads/writes bytes +#define KDP_LOG_BREAKPOINTS (1u << 7) +#define KDP_LOG_WATCHPOINTS (1u << 8) +#define KDP_LOG_STEP (1u << 9) +#define KDP_LOG_COMM (1u << 10) +#define KDP_LOG_ASYNC (1u << 11) +#define KDP_LOG_ALL (UINT32_MAX) +#define KDP_LOG_DEFAULT KDP_LOG_PACKETS + +class ProcessKDPLog +{ +public: + static lldb_private::Log * + GetLogIfAllCategoriesSet(uint32_t mask = 0); + + static void + DisableLog (const char **categories, lldb_private::Stream *feedback_strm); + + static lldb_private::Log * + EnableLog (lldb::StreamSP &log_stream_sp, uint32_t log_options, const char **categories, lldb_private::Stream *feedback_strm); + + static void + ListLogCategories (lldb_private::Stream *strm); + + static void + LogIf (uint32_t mask, const char *format, ...); +}; + +#endif // liblldb_ProcessKDPLog_h_ diff --git a/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm.cpp b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm.cpp new file mode 100644 index 000000000000..449ac646ab3c --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm.cpp @@ -0,0 +1,161 @@ +//===-- RegisterContextKDP_arm.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextKDP_arm.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "ProcessKDP.h" +#include "ThreadKDP.h" + +using namespace lldb; +using namespace lldb_private; + + +RegisterContextKDP_arm::RegisterContextKDP_arm (ThreadKDP &thread, uint32_t concrete_frame_idx) : + RegisterContextDarwin_arm (thread, concrete_frame_idx), + m_kdp_thread (thread) +{ +} + +RegisterContextKDP_arm::~RegisterContextKDP_arm() +{ +} + +int +RegisterContextKDP_arm::DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, GPRRegSet, &gpr, sizeof(gpr), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm::DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, FPURegSet, &fpu, sizeof(fpu), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm::DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, EXCRegSet, &exc, sizeof(exc), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm::DoReadDBG (lldb::tid_t tid, int flavor, DBG &dbg) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, DBGRegSet, &dbg, sizeof(dbg), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm::DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, GPRRegSet, &gpr, sizeof(gpr), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm::DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, FPURegSet, &fpu, sizeof(fpu), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm::DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, EXCRegSet, &exc, sizeof(exc), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm::DoWriteDBG (lldb::tid_t tid, int flavor, const DBG &dbg) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, DBGRegSet, &dbg, sizeof(dbg), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + + diff --git a/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm.h b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm.h new file mode 100644 index 000000000000..1e547289d9ce --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm.h @@ -0,0 +1,61 @@ +//===-- RegisterContextKDP_arm.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextKDP_arm_h_ +#define liblldb_RegisterContextKDP_arm_h_ + +// C Includes + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "Plugins/Process/Utility/RegisterContextDarwin_arm.h" + +class ThreadKDP; + +class RegisterContextKDP_arm : public RegisterContextDarwin_arm +{ +public: + + RegisterContextKDP_arm (ThreadKDP &thread, + uint32_t concrete_frame_idx); + + virtual + ~RegisterContextKDP_arm(); + +protected: + + virtual int + DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr); + + int + DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu); + + int + DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc); + + int + DoReadDBG (lldb::tid_t tid, int flavor, DBG &dbg); + + int + DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr); + + int + DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu); + + int + DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc); + + int + DoWriteDBG (lldb::tid_t tid, int flavor, const DBG &dbg); + + ThreadKDP &m_kdp_thread; +}; + +#endif // liblldb_RegisterContextKDP_arm_h_ diff --git a/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm64.cpp b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm64.cpp new file mode 100644 index 000000000000..ed62f1982d3c --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm64.cpp @@ -0,0 +1,161 @@ +//===-- RegisterContextKDP_arm64.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextKDP_arm64.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "ProcessKDP.h" +#include "ThreadKDP.h" + +using namespace lldb; +using namespace lldb_private; + + +RegisterContextKDP_arm64::RegisterContextKDP_arm64 (ThreadKDP &thread, uint32_t concrete_frame_idx) : + RegisterContextDarwin_arm64 (thread, concrete_frame_idx), + m_kdp_thread (thread) +{ +} + +RegisterContextKDP_arm64::~RegisterContextKDP_arm64() +{ +} + +int +RegisterContextKDP_arm64::DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, GPRRegSet, &gpr, sizeof(gpr), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm64::DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, FPURegSet, &fpu, sizeof(fpu), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm64::DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, EXCRegSet, &exc, sizeof(exc), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm64::DoReadDBG (lldb::tid_t tid, int flavor, DBG &dbg) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, DBGRegSet, &dbg, sizeof(dbg), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm64::DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, GPRRegSet, &gpr, sizeof(gpr), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm64::DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, FPURegSet, &fpu, sizeof(fpu), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm64::DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, EXCRegSet, &exc, sizeof(exc), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm64::DoWriteDBG (lldb::tid_t tid, int flavor, const DBG &dbg) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, DBGRegSet, &dbg, sizeof(dbg), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + + diff --git a/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm64.h b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm64.h new file mode 100644 index 000000000000..8780b7be4a9a --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm64.h @@ -0,0 +1,61 @@ +//===-- RegisterContextKDP_arm64.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextKDP_arm64_h_ +#define liblldb_RegisterContextKDP_arm64_h_ + +// C Includes + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "Plugins/Process/Utility/RegisterContextDarwin_arm64.h" + +class ThreadKDP; + +class RegisterContextKDP_arm64 : public RegisterContextDarwin_arm64 +{ +public: + + RegisterContextKDP_arm64 (ThreadKDP &thread, + uint32_t concrete_frame_idx); + + virtual + ~RegisterContextKDP_arm64(); + +protected: + + virtual int + DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr); + + int + DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu); + + int + DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc); + + int + DoReadDBG (lldb::tid_t tid, int flavor, DBG &dbg); + + int + DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr); + + int + DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu); + + int + DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc); + + int + DoWriteDBG (lldb::tid_t tid, int flavor, const DBG &dbg); + + ThreadKDP &m_kdp_thread; +}; + +#endif // liblldb_RegisterContextKDP_arm64_h_ diff --git a/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_i386.cpp b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_i386.cpp new file mode 100644 index 000000000000..882b0c2e931d --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_i386.cpp @@ -0,0 +1,129 @@ +//===-- RegisterContextKDP_i386.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "RegisterContextKDP_i386.h" +#include "ProcessKDP.h" +#include "ThreadKDP.h" + +using namespace lldb; +using namespace lldb_private; + + +RegisterContextKDP_i386::RegisterContextKDP_i386 (ThreadKDP &thread, uint32_t concrete_frame_idx) : + RegisterContextDarwin_i386 (thread, concrete_frame_idx), + m_kdp_thread (thread) +{ +} + +RegisterContextKDP_i386::~RegisterContextKDP_i386() +{ +} + +int +RegisterContextKDP_i386::DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, GPRRegSet, &gpr, sizeof(gpr), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_i386::DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, FPURegSet, &fpu, sizeof(fpu), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_i386::DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, EXCRegSet, &exc, sizeof(exc), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_i386::DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, GPRRegSet, &gpr, sizeof(gpr), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_i386::DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, FPURegSet, &fpu, sizeof(fpu), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_i386::DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, EXCRegSet, &exc, sizeof(exc), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + + diff --git a/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_i386.h b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_i386.h new file mode 100644 index 000000000000..4b6bc5b262f7 --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_i386.h @@ -0,0 +1,53 @@ +//===-- RegisterContextKDP_i386.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextKDP_i386_h_ +#define liblldb_RegisterContextKDP_i386_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "Plugins/Process/Utility/RegisterContextDarwin_i386.h" + +class ThreadKDP; + +class RegisterContextKDP_i386 : public RegisterContextDarwin_i386 +{ +public: + RegisterContextKDP_i386 (ThreadKDP &thread, + uint32_t concrete_frame_idx); + + virtual + ~RegisterContextKDP_i386(); + +protected: + + virtual int + DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr); + + int + DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu); + + int + DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc); + + int + DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr); + + int + DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu); + + int + DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc); + + ThreadKDP &m_kdp_thread; +}; + +#endif // liblldb_RegisterContextKDP_i386_h_ diff --git a/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_x86_64.cpp b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_x86_64.cpp new file mode 100644 index 000000000000..f4247a5da272 --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_x86_64.cpp @@ -0,0 +1,127 @@ +//===-- RegisterContextKDP_x86_64.cpp ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "RegisterContextKDP_x86_64.h" +#include "ProcessKDP.h" +#include "ThreadKDP.h" + +using namespace lldb; +using namespace lldb_private; + + +RegisterContextKDP_x86_64::RegisterContextKDP_x86_64 (ThreadKDP &thread, uint32_t concrete_frame_idx) : + RegisterContextDarwin_x86_64 (thread, concrete_frame_idx), + m_kdp_thread (thread) +{ +} + +RegisterContextKDP_x86_64::~RegisterContextKDP_x86_64() +{ +} + +int +RegisterContextKDP_x86_64::DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, GPRRegSet, &gpr, sizeof(gpr), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_x86_64::DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, FPURegSet, &fpu, sizeof(fpu), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_x86_64::DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, EXCRegSet, &exc, sizeof(exc), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_x86_64::DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, GPRRegSet, &gpr, sizeof(gpr), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_x86_64::DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, FPURegSet, &fpu, sizeof(fpu), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_x86_64::DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, EXCRegSet, &exc, sizeof(exc), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} diff --git a/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_x86_64.h b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_x86_64.h new file mode 100644 index 000000000000..a426349198ff --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_x86_64.h @@ -0,0 +1,54 @@ +//===-- RegisterContextKDP_x86_64.h -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextKDP_x86_64_h_ +#define liblldb_RegisterContextKDP_x86_64_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "Plugins/Process/Utility/RegisterContextDarwin_x86_64.h" + +class ThreadKDP; + +class RegisterContextKDP_x86_64 : public RegisterContextDarwin_x86_64 +{ +public: + + RegisterContextKDP_x86_64 (ThreadKDP &thread, + uint32_t concrete_frame_idx); + + virtual + ~RegisterContextKDP_x86_64(); + +protected: + + virtual int + DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr); + + int + DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu); + + int + DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc); + + int + DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr); + + int + DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu); + + int + DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc); + + ThreadKDP &m_kdp_thread; +}; + +#endif // liblldb_RegisterContextKDP_x86_64_h_ diff --git a/source/Plugins/Process/MacOSX-Kernel/ThreadKDP.cpp b/source/Plugins/Process/MacOSX-Kernel/ThreadKDP.cpp new file mode 100644 index 000000000000..3b8bed101a8a --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/ThreadKDP.cpp @@ -0,0 +1,211 @@ +//===-- ThreadKDP.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "ThreadKDP.h" + +#include "lldb/Utility/SafeMachO.h" + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/State.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Unwind.h" +#include "lldb/Breakpoint/Watchpoint.h" + +#include "ProcessKDP.h" +#include "ProcessKDPLog.h" +#include "RegisterContextKDP_arm.h" +#include "RegisterContextKDP_arm64.h" +#include "RegisterContextKDP_i386.h" +#include "RegisterContextKDP_x86_64.h" +#include "Plugins/Process/Utility/StopInfoMachException.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Thread Registers +//---------------------------------------------------------------------- + +ThreadKDP::ThreadKDP (Process &process, lldb::tid_t tid) : + Thread(process, tid), + m_thread_name (), + m_dispatch_queue_name (), + m_thread_dispatch_qaddr (LLDB_INVALID_ADDRESS) +{ + ProcessKDPLog::LogIf(KDP_LOG_THREAD, "%p: ThreadKDP::ThreadKDP (tid = 0x%4.4x)", this, GetID()); +} + +ThreadKDP::~ThreadKDP () +{ + ProcessKDPLog::LogIf(KDP_LOG_THREAD, "%p: ThreadKDP::~ThreadKDP (tid = 0x%4.4x)", this, GetID()); + DestroyThread(); +} + +const char * +ThreadKDP::GetName () +{ + if (m_thread_name.empty()) + return NULL; + return m_thread_name.c_str(); +} + +const char * +ThreadKDP::GetQueueName () +{ + return NULL; +} + +void +ThreadKDP::RefreshStateAfterStop() +{ + // Invalidate all registers in our register context. We don't set "force" to + // true because the stop reply packet might have had some register values + // that were expedited and these will already be copied into the register + // context by the time this function gets called. The KDPRegisterContext + // class has been made smart enough to detect when it needs to invalidate + // which registers are valid by putting hooks in the register read and + // register supply functions where they check the process stop ID and do + // the right thing. + const bool force = false; + lldb::RegisterContextSP reg_ctx_sp (GetRegisterContext()); + if (reg_ctx_sp) + reg_ctx_sp->InvalidateIfNeeded (force); +} + +bool +ThreadKDP::ThreadIDIsValid (lldb::tid_t thread) +{ + return thread != 0; +} + +void +ThreadKDP::Dump(Log *log, uint32_t index) +{ +} + + +bool +ThreadKDP::ShouldStop (bool &step_more) +{ + return true; +} +lldb::RegisterContextSP +ThreadKDP::GetRegisterContext () +{ + if (m_reg_context_sp.get() == NULL) + m_reg_context_sp = CreateRegisterContextForFrame (NULL); + return m_reg_context_sp; +} + +lldb::RegisterContextSP +ThreadKDP::CreateRegisterContextForFrame (StackFrame *frame) +{ + lldb::RegisterContextSP reg_ctx_sp; + uint32_t concrete_frame_idx = 0; + + if (frame) + concrete_frame_idx = frame->GetConcreteFrameIndex (); + + if (concrete_frame_idx == 0) + { + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + switch (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().GetCPUType()) + { + case llvm::MachO::CPU_TYPE_ARM: + reg_ctx_sp.reset (new RegisterContextKDP_arm (*this, concrete_frame_idx)); + break; + case llvm::MachO::CPU_TYPE_ARM64: + reg_ctx_sp.reset (new RegisterContextKDP_arm64 (*this, concrete_frame_idx)); + break; + case llvm::MachO::CPU_TYPE_I386: + reg_ctx_sp.reset (new RegisterContextKDP_i386 (*this, concrete_frame_idx)); + break; + case llvm::MachO::CPU_TYPE_X86_64: + reg_ctx_sp.reset (new RegisterContextKDP_x86_64 (*this, concrete_frame_idx)); + break; + default: + assert (!"Add CPU type support in KDP"); + break; + } + } + } + else + { + Unwind *unwinder = GetUnwinder (); + if (unwinder) + reg_ctx_sp = unwinder->CreateRegisterContextForFrame (frame); + } + return reg_ctx_sp; +} + +bool +ThreadKDP::CalculateStopInfo () +{ + ProcessSP process_sp (GetProcess()); + if (process_sp) + { + if (m_cached_stop_info_sp) + { + SetStopInfo (m_cached_stop_info_sp); + } + else + { + SetStopInfo(StopInfo::CreateStopReasonWithSignal (*this, SIGSTOP)); + } + return true; + } + return false; +} + +void +ThreadKDP::SetStopInfoFrom_KDP_EXCEPTION (const DataExtractor &exc_reply_packet) +{ + lldb::offset_t offset = 0; + uint8_t reply_command = exc_reply_packet.GetU8(&offset); + if (reply_command == CommunicationKDP::KDP_EXCEPTION) + { + offset = 8; + const uint32_t count = exc_reply_packet.GetU32 (&offset); + if (count >= 1) + { + //const uint32_t cpu = exc_reply_packet.GetU32 (&offset); + offset += 4; // Skip the useless CPU field + const uint32_t exc_type = exc_reply_packet.GetU32 (&offset); + const uint32_t exc_code = exc_reply_packet.GetU32 (&offset); + const uint32_t exc_subcode = exc_reply_packet.GetU32 (&offset); + // We have to make a copy of the stop info because the thread list + // will iterate through the threads and clear all stop infos.. + + // Let the StopInfoMachException::CreateStopReasonWithMachException() + // function update the PC if needed as we might hit a software breakpoint + // and need to decrement the PC (i386 and x86_64 need this) and KDP + // doesn't do this for us. + const bool pc_already_adjusted = false; + const bool adjust_pc_if_needed = true; + + m_cached_stop_info_sp = StopInfoMachException::CreateStopReasonWithMachException (*this, + exc_type, + 2, + exc_code, + exc_subcode, + 0, + pc_already_adjusted, + adjust_pc_if_needed); + } + } +} + diff --git a/source/Plugins/Process/MacOSX-Kernel/ThreadKDP.h b/source/Plugins/Process/MacOSX-Kernel/ThreadKDP.h new file mode 100644 index 000000000000..7dc373f03550 --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/ThreadKDP.h @@ -0,0 +1,98 @@ +//===-- ThreadKDP.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadKDP_h_ +#define liblldb_ThreadKDP_h_ + +#include <string> + +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" + +class ProcessKDP; + +class ThreadKDP : public lldb_private::Thread +{ +public: + ThreadKDP (lldb_private::Process &process, + lldb::tid_t tid); + + virtual + ~ThreadKDP (); + + virtual void + RefreshStateAfterStop(); + + virtual const char * + GetName (); + + virtual const char * + GetQueueName (); + + virtual lldb::RegisterContextSP + GetRegisterContext (); + + virtual lldb::RegisterContextSP + CreateRegisterContextForFrame (lldb_private::StackFrame *frame); + + void + Dump (lldb_private::Log *log, uint32_t index); + + static bool + ThreadIDIsValid (lldb::tid_t thread); + + bool + ShouldStop (bool &step_more); + + const char * + GetBasicInfoAsString (); + + void + SetName (const char *name) + { + if (name && name[0]) + m_thread_name.assign (name); + else + m_thread_name.clear(); + } + + lldb::addr_t + GetThreadDispatchQAddr () + { + return m_thread_dispatch_qaddr; + } + + void + SetThreadDispatchQAddr (lldb::addr_t thread_dispatch_qaddr) + { + m_thread_dispatch_qaddr = thread_dispatch_qaddr; + } + + void + SetStopInfoFrom_KDP_EXCEPTION (const lldb_private::DataExtractor &exc_reply_packet); + +protected: + + friend class ProcessKDP; + + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + std::string m_thread_name; + std::string m_dispatch_queue_name; + lldb::addr_t m_thread_dispatch_qaddr; + lldb::StopInfoSP m_cached_stop_info_sp; + //------------------------------------------------------------------ + // Protected member functions. + //------------------------------------------------------------------ + virtual bool + CalculateStopInfo (); +}; + +#endif // liblldb_ThreadKDP_h_ |