diff options
Diffstat (limited to 'lldb/source/Plugins')
6 files changed, 1800 insertions, 0 deletions
diff --git a/lldb/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.cpp b/lldb/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.cpp new file mode 100644 index 00000000000..c3ac577febd --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.cpp @@ -0,0 +1,403 @@ +//===-- 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 <limits.h> +#include <string.h> + +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/TimeValue.h" +#include "lldb/Target/Process.h" +#include "Utility/StringExtractor.h" + +// Project includes +#include "ProcessKDPLog.h" + +#define DEBUGSERVER_BASENAME "debugserver" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// CommunicationKDP constructor +//---------------------------------------------------------------------- +CommunicationKDP::CommunicationKDP (const char *comm_name) : + Communication(comm_name), + m_packet_timeout (1), + m_sequence_mutex (Mutex::eMutexTypeRecursive), + m_public_is_running (false), + m_private_is_running (false), + m_send_acks (true) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CommunicationKDP::~CommunicationKDP() +{ + if (IsConnected()) + { + Disconnect(); + } +} + +char +CommunicationKDP::CalculcateChecksum (const char *payload, size_t payload_length) +{ + int checksum = 0; + + // We only need to compute the checksum if we are sending acks + if (GetSendAcks ()) + { + for (size_t i = 0; i < payload_length; ++i) + checksum += payload[i]; + } + return checksum & 255; +} + +size_t +CommunicationKDP::SendAck () +{ + LogSP log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PACKETS)); + if (log) + log->Printf ("send packet: +"); + ConnectionStatus status = eConnectionStatusSuccess; + char ack_char = '+'; + return Write (&ack_char, 1, status, NULL); +} + +size_t +CommunicationKDP::SendNack () +{ + LogSP log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PACKETS)); + if (log) + log->Printf ("send packet: -"); + ConnectionStatus status = eConnectionStatusSuccess; + char nack_char = '-'; + return Write (&nack_char, 1, status, NULL); +} + +size_t +CommunicationKDP::SendPacket (lldb_private::StreamString &payload) +{ + Mutex::Locker locker(m_sequence_mutex); + const std::string &p (payload.GetString()); + return SendPacketNoLock (p.c_str(), p.size()); +} + +size_t +CommunicationKDP::SendPacket (const char *payload) +{ + Mutex::Locker locker(m_sequence_mutex); + return SendPacketNoLock (payload, ::strlen (payload)); +} + +size_t +CommunicationKDP::SendPacket (const char *payload, size_t payload_length) +{ + Mutex::Locker locker(m_sequence_mutex); + return SendPacketNoLock (payload, payload_length); +} + +size_t +CommunicationKDP::SendPacketNoLock (const char *payload, size_t payload_length) +{ + if (IsConnected()) + { + StreamString packet(0, 4, eByteOrderBig); + + packet.PutChar('$'); + packet.Write (payload, payload_length); + packet.PutChar('#'); + packet.PutHex8(CalculcateChecksum (payload, payload_length)); + + LogSP log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PACKETS)); + if (log) + log->Printf ("send packet: %.*s", (int)packet.GetSize(), packet.GetData()); + ConnectionStatus status = eConnectionStatusSuccess; + size_t bytes_written = Write (packet.GetData(), packet.GetSize(), status, NULL); + if (bytes_written == packet.GetSize()) + { + if (GetSendAcks ()) + { + if (GetAck () != '+') + { + printf("get ack failed..."); + return 0; + } + } + } + else + { + LogSP log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PACKETS)); + if (log) + log->Printf ("error: failed to send packet: %.*s", (int)packet.GetSize(), packet.GetData()); + } + return bytes_written; + } + return 0; +} + +char +CommunicationKDP::GetAck () +{ + StringExtractor packet; + if (WaitForPacketWithTimeoutMicroSeconds (packet, GetPacketTimeoutInMicroSeconds ()) == 1) + return packet.GetChar(); + return 0; +} + +bool +CommunicationKDP::GetSequenceMutex (Mutex::Locker& locker) +{ + return locker.TryLock (m_sequence_mutex.GetMutex()); +} + + +bool +CommunicationKDP::WaitForNotRunningPrivate (const TimeValue *timeout_ptr) +{ + return m_private_is_running.WaitForValueEqualTo (false, timeout_ptr, NULL); +} + +size_t +CommunicationKDP::WaitForPacketWithTimeoutMicroSeconds (StringExtractor &packet, uint32_t timeout_usec) +{ + Mutex::Locker locker(m_sequence_mutex); + return WaitForPacketWithTimeoutMicroSecondsNoLock (packet, timeout_usec); +} + +size_t +CommunicationKDP::WaitForPacketWithTimeoutMicroSecondsNoLock (StringExtractor &packet, uint32_t timeout_usec) +{ + uint8_t buffer[8192]; + Error error; + + LogSP 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.GetStringRef().size(); + + bool timed_out = false; + while (IsConnected() && !timed_out) + { + lldb::ConnectionStatus status; + 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 = %zu", + __PRETTY_FUNCTION__, + timeout_usec, + Communication::ConnectionStatusAsCString (status), + error.AsCString(), + bytes_read); + + if (bytes_read > 0) + { + if (CheckForPacket (buffer, bytes_read, packet)) + return packet.GetStringRef().size(); + } + else + { + switch (status) + { + 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, StringExtractor &packet) +{ + // Put the packet data into the buffer in a thread safe fashion + Mutex::Locker locker(m_bytes_mutex); + + LogSP log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PACKETS)); + + if (src && src_len > 0) + { + if (log && log->GetVerbose()) + { + StreamString s; + log->Printf ("CommunicationKDP::%s adding %u bytes: %.*s", + __FUNCTION__, + (uint32_t)src_len, + (uint32_t)src_len, + src); + } + m_bytes.append ((const char *)src, src_len); + } + + // Parse up the packets into gdb remote packets + if (!m_bytes.empty()) + { + // end_idx must be one past the last valid packet byte. Start + // it off with an invalid value that is the same as the current + // index. + size_t content_start = 0; + size_t content_length = 0; + size_t total_length = 0; + size_t checksum_idx = std::string::npos; + + switch (m_bytes[0]) + { + case '+': // Look for ack + case '-': // Look for cancel + case '\x03': // ^C to halt target + content_length = total_length = 1; // The command is one byte long... + break; + + case '$': + // Look for a standard gdb packet? + { + size_t hash_pos = m_bytes.find('#'); + if (hash_pos != std::string::npos) + { + if (hash_pos + 2 < m_bytes.size()) + { + checksum_idx = hash_pos + 1; + // Skip the dollar sign + content_start = 1; + // Don't include the # in the content or the $ in the content length + content_length = hash_pos - 1; + + total_length = hash_pos + 3; // Skip the # and the two hex checksum bytes + } + else + { + // Checksum bytes aren't all here yet + content_length = std::string::npos; + } + } + } + break; + + default: + { + // We have an unexpected byte and we need to flush all bad + // data that is in m_bytes, so we need to find the first + // byte that is a '+' (ACK), '-' (NACK), \x03 (CTRL+C interrupt), + // or '$' character (start of packet header) or of course, + // the end of the data in m_bytes... + const size_t bytes_len = m_bytes.size(); + bool done = false; + uint32_t idx; + for (idx = 1; !done && idx < bytes_len; ++idx) + { + switch (m_bytes[idx]) + { + case '+': + case '-': + case '\x03': + case '$': + done = true; + break; + + default: + break; + } + } + if (log) + log->Printf ("CommunicationKDP::%s tossing %u junk bytes: '%.*s'", + __FUNCTION__, idx, idx, m_bytes.c_str()); + m_bytes.erase(0, idx); + } + break; + } + + if (content_length == std::string::npos) + { + packet.Clear(); + return false; + } + else if (total_length > 0) + { + + // We have a valid packet... + assert (content_length <= m_bytes.size()); + assert (total_length <= m_bytes.size()); + assert (content_length <= total_length); + + bool success = true; + std::string &packet_str = packet.GetStringRef(); + packet_str.assign (m_bytes, content_start, content_length); + if (m_bytes[0] == '$') + { + assert (checksum_idx < m_bytes.size()); + if (::isxdigit (m_bytes[checksum_idx+0]) || + ::isxdigit (m_bytes[checksum_idx+1])) + { + if (GetSendAcks ()) + { + const char *packet_checksum_cstr = &m_bytes[checksum_idx]; + char packet_checksum = strtol (packet_checksum_cstr, NULL, 16); + char actual_checksum = CalculcateChecksum (packet_str.c_str(), packet_str.size()); + success = packet_checksum == actual_checksum; + if (!success) + { + if (log) + log->Printf ("error: checksum mismatch: %.*s expected 0x%2.2x, got 0x%2.2x", + (int)(total_length), + m_bytes.c_str(), + (uint8_t)packet_checksum, + (uint8_t)actual_checksum); + } + // Send the ack or nack if needed + if (!success) + SendNack(); + else + SendAck(); + } + if (success) + { + if (log) + log->Printf ("read packet: %.*s", (int)(total_length), m_bytes.c_str()); + } + } + else + { + success = false; + if (log) + log->Printf ("error: invalid checksum in packet: '%s'\n", (int)(total_length), m_bytes.c_str()); + } + } + m_bytes.erase(0, total_length); + packet.SetFilePos(0); + return success; + } + } + packet.Clear(); + return false; +} + diff --git a/lldb/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.h b/lldb/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.h new file mode 100644 index 00000000000..ee626a69824 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.h @@ -0,0 +1,152 @@ +//===-- 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/Host/Mutex.h" +#include "lldb/Host/Predicate.h" +#include "lldb/Host/TimeValue.h" + +class StringExtractor; + +class CommunicationKDP : public lldb_private::Communication +{ +public: + enum + { + eBroadcastBitRunPacketSent = kLoUserBroadcastBit + }; + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CommunicationKDP (const char *comm_name); + + virtual + ~CommunicationKDP(); + + size_t + SendPacket (const char *payload); + + size_t + SendPacket (const char *payload, + size_t payload_length); + + size_t + SendPacket (lldb_private::StreamString &response); + + // Wait for a packet within 'nsec' seconds + size_t + WaitForPacketWithTimeoutMicroSeconds (StringExtractor &response, + uint32_t usec); + + char + GetAck (); + + size_t + SendAck (); + + size_t + SendNack (); + + char + CalculcateChecksum (const char *payload, + size_t payload_length); + + bool + GetSequenceMutex(lldb_private::Mutex::Locker& locker); + + bool + CheckForPacket (const uint8_t *src, + size_t src_len, + StringExtractor &packet); + bool + IsRunning() const + { + return m_public_is_running.GetValue(); + } + + bool + GetSendAcks () + { + return m_send_acks; + } + + //------------------------------------------------------------------ + // 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; + } + //------------------------------------------------------------------ + // Start a debugserver instance on the current host using the + // supplied connection URL. + //------------------------------------------------------------------ + lldb_private::Error + StartDebugserverProcess (const char *connect_url, + const char *unix_socket_name, + lldb_private::ProcessLaunchInfo &launch_info); + + +protected: + typedef std::list<std::string> packet_collection; + + size_t + SendPacketNoLock (const char *payload, + size_t payload_length); + + size_t + WaitForPacketWithTimeoutMicroSecondsNoLock (StringExtractor &response, + uint32_t timeout_usec); + + bool + WaitForNotRunningPrivate (const lldb_private::TimeValue *timeout_ptr); + + //------------------------------------------------------------------ + // Classes that inherit from CommunicationKDP can see and modify these + //------------------------------------------------------------------ + 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_public_is_running; + lldb_private::Predicate<bool> m_private_is_running; + bool m_send_acks; + +private: + //------------------------------------------------------------------ + // For CommunicationKDP only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (CommunicationKDP); +}; + +#endif // liblldb_CommunicationKDP_h_ diff --git a/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp b/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp new file mode 100644 index 00000000000..9ae965ec84f --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp @@ -0,0 +1,725 @@ +//===-- 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 +// Other libraries and framework includes +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/State.h" +#include "lldb/Host/Host.h" + +// Project includes +#include "ProcessKDP.h" +#include "ProcessKDPLog.h" +//#include "ThreadKDP.h" +#include "StopInfoMachException.h" + +using namespace lldb; +using namespace lldb_private; + +const char * +ProcessKDP::GetPluginNameStatic() +{ + return "kdp-remote"; +} + +const char * +ProcessKDP::GetPluginDescriptionStatic() +{ + return "KDP Remote protocol based debugging plug-in for darwin kernel debugging."; +} + +void +ProcessKDP::Terminate() +{ + PluginManager::UnregisterPlugin (ProcessKDP::CreateInstance); +} + + +Process* +ProcessKDP::CreateInstance (Target &target, Listener &listener) +{ + return new ProcessKDP (target, listener); +} + +bool +ProcessKDP::CanDebug(Target &target) +{ + // For now we are just making sure the file exists for a given module + ModuleSP exe_module_sp(target.GetExecutableModule()); + if (exe_module_sp.get()) + { + const llvm::Triple &triple_ref = target.GetArchitecture().GetTriple(); + if (triple_ref.getOS() == llvm::Triple::Darwin && + triple_ref.getVendor() == llvm::Triple::Apple) + { + + ObjectFile *exe_objfile = exe_module_sp->GetObjectFile(); + if (exe_objfile->GetType() == ObjectFile::eTypeExecutable && + exe_objfile->GetStrata() == ObjectFile::eStrataKernel) + return true; + } + } + return false; +} + +//---------------------------------------------------------------------- +// ProcessKDP constructor +//---------------------------------------------------------------------- +ProcessKDP::ProcessKDP(Target& target, Listener &listener) : + Process (target, listener), + m_comm("lldb.process.kdp-remote.communication"), + m_async_broadcaster ("lldb.process.kdp-remote.async-broadcaster"), + m_async_thread (LLDB_INVALID_HOST_THREAD) +{ +// m_async_broadcaster.SetEventName (eBroadcastBitAsyncThreadShouldExit, "async thread should exit"); +// m_async_broadcaster.SetEventName (eBroadcastBitAsyncContinue, "async thread continue"); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ProcessKDP::~ProcessKDP() +{ + Clear(); +} + +//---------------------------------------------------------------------- +// PluginInterface +//---------------------------------------------------------------------- +const char * +ProcessKDP::GetPluginName() +{ + return "Process debugging plug-in that uses the Darwin KDP remote protocol"; +} + +const char * +ProcessKDP::GetShortPluginName() +{ + 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; +} + +Error +ProcessKDP::DoConnectRemote (const char *remote_url) +{ + // TODO: fill in the remote connection to the remote KDP here! + Error error; + error.SetErrorString ("attaching to a by process name not supported in kdp-remote plug-in"); + return error; +} + +//---------------------------------------------------------------------- +// Process Control +//---------------------------------------------------------------------- +Error +ProcessKDP::DoLaunch (Module* module, + char const *argv[], + char const *envp[], + uint32_t launch_flags, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + const char *working_dir) +{ + Error error; + error.SetErrorString ("launching not supported in kdp-remote plug-in"); + return error; +} + + +Error +ProcessKDP::DoAttachToProcessWithID (lldb::pid_t attach_pid) +{ + Error error; + error.SetErrorString ("attach to process by ID is not suppported in kdp remote debugging"); + return error; +} + +size_t +ProcessKDP::AttachInputReaderCallback (void *baton, + InputReader *reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len) +{ + if (notification == eInputReaderGotToken) + { +// ProcessKDP *process = (ProcessKDP *)baton; +// if (process->m_waiting_for_attach) +// process->m_waiting_for_attach = false; + reader->SetIsDone(true); + return 1; + } + return 0; +} + +Error +ProcessKDP::DoAttachToProcessWithName (const char *process_name, bool wait_for_launch) +{ + Error error; + error.SetErrorString ("attach to process by name is not suppported in kdp remote debugging"); + return error; +} + + +void +ProcessKDP::DidAttach () +{ + LogSP log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PROCESS)); + if (log) + log->Printf ("ProcessKDP::DidLaunch()"); + if (GetID() != LLDB_INVALID_PROCESS_ID) + { + // TODO: figure out the register context that we will use + } +} + +Error +ProcessKDP::WillResume () +{ + return Error(); +} + +Error +ProcessKDP::DoResume () +{ + Error error; + error.SetErrorString ("ProcessKDP::DoResume () is not implemented yet"); + return error; +} + +uint32_t +ProcessKDP::UpdateThreadListIfNeeded () +{ + // locker will keep a mutex locked until it goes out of scope + LogSP log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_THREAD)); + if (log && log->GetMask().Test(KDP_LOG_VERBOSE)) + log->Printf ("ProcessKDP::%s (pid = %i)", __FUNCTION__, GetID()); + + Mutex::Locker locker (m_thread_list.GetMutex ()); + // TODO: get the thread list here! + const uint32_t stop_id = GetStopID(); + if (m_thread_list.GetSize(false) == 0 || stop_id != m_thread_list.GetStopID()) + { + // Update the thread list's stop id immediately so we don't recurse into this function. +// ThreadList curr_thread_list (this); +// curr_thread_list.SetStopID(stop_id); +// +// std::vector<lldb::tid_t> thread_ids; +// bool sequence_mutex_unavailable = false; +// const size_t num_thread_ids = m_comm.GetCurrentThreadIDs (thread_ids, sequence_mutex_unavailable); +// if (num_thread_ids > 0) +// { +// for (size_t i=0; i<num_thread_ids; ++i) +// { +// tid_t tid = thread_ids[i]; +// ThreadSP thread_sp (GetThreadList().FindThreadByID (tid, false)); +// if (!thread_sp) +// thread_sp.reset (new ThreadGDBRemote (*this, tid)); +// curr_thread_list.AddThread(thread_sp); +// } +// } +// +// if (sequence_mutex_unavailable == false) +// { +// m_thread_list = curr_thread_list; +// SetThreadStopInfo (m_last_stop_packet); +// } + } + return GetThreadList().GetSize(false); +} + + +StateType +ProcessKDP::SetThreadStopInfo (StringExtractor& stop_packet) +{ + // TODO: figure out why we stopped given the packet that tells us we stopped... + return eStateStopped; +} + +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(); + //SetThreadStopInfo (m_last_stop_packet); +} + +Error +ProcessKDP::DoHalt (bool &caused_stop) +{ + Error error; + +// bool timed_out = false; + Mutex::Locker locker; + + if (m_public_state.GetValue() == eStateAttaching) + { + // We are being asked to halt during an attach. We need to just close + // our file handle and debugserver will go away, and we can be done... + m_comm.Disconnect(); + } + else + { + // TODO: add the ability to halt a running kernel + error.SetErrorString ("halt not supported in kdp-remote plug-in"); +// if (!m_comm.SendInterrupt (locker, 2, caused_stop, timed_out)) +// { +// if (timed_out) +// error.SetErrorString("timed out sending interrupt packet"); +// else +// error.SetErrorString("unknown error sending interrupt packet"); +// } + } + return error; +} + +Error +ProcessKDP::InterruptIfRunning (bool discard_thread_plans, + bool catch_stop_event, + EventSP &stop_event_sp) +{ + Error error; + + LogSP log (ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_PROCESS)); + + bool paused_private_state_thread = false; + const bool is_running = m_comm.IsRunning(); + if (log) + log->Printf ("ProcessKDP::InterruptIfRunning(discard_thread_plans=%i, catch_stop_event=%i) is_running=%i", + discard_thread_plans, + catch_stop_event, + is_running); + + if (discard_thread_plans) + { + if (log) + log->Printf ("ProcessKDP::InterruptIfRunning() discarding all thread plans"); + m_thread_list.DiscardThreadPlans(); + } + if (is_running) + { + if (catch_stop_event) + { + if (log) + log->Printf ("ProcessKDP::InterruptIfRunning() pausing private state thread"); + PausePrivateStateThread(); + paused_private_state_thread = true; + } + + bool timed_out = false; +// bool sent_interrupt = false; + Mutex::Locker locker; + + // TODO: implement halt in CommunicationKDP +// if (!m_comm.SendInterrupt (locker, 1, sent_interrupt, timed_out)) +// { +// if (timed_out) +// error.SetErrorString("timed out sending interrupt packet"); +// else +// error.SetErrorString("unknown error sending interrupt packet"); +// if (paused_private_state_thread) +// ResumePrivateStateThread(); +// return error; +// } + + if (catch_stop_event) + { + // LISTEN HERE + TimeValue timeout_time; + timeout_time = TimeValue::Now(); + timeout_time.OffsetWithSeconds(5); + StateType state = WaitForStateChangedEventsPrivate (&timeout_time, stop_event_sp); + + timed_out = state == eStateInvalid; + if (log) + log->Printf ("ProcessKDP::InterruptIfRunning() catch stop event: state = %s, timed-out=%i", StateAsCString(state), timed_out); + + if (timed_out) + error.SetErrorString("unable to verify target stopped"); + } + + if (paused_private_state_thread) + { + if (log) + log->Printf ("ProcessKDP::InterruptIfRunning() resuming private state thread"); + ResumePrivateStateThread(); + } + } + return error; +} + +Error +ProcessKDP::WillDetach () +{ + LogSP log (ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_PROCESS)); + if (log) + log->Printf ("ProcessKDP::WillDetach()"); + + bool discard_thread_plans = true; + bool catch_stop_event = true; + EventSP event_sp; + return InterruptIfRunning (discard_thread_plans, catch_stop_event, event_sp); +} + +Error +ProcessKDP::DoDetach() +{ + Error error; + LogSP log (ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_PROCESS)); + if (log) + log->Printf ("ProcessKDP::DoDetach()"); + + DisableAllBreakpointSites (); + + m_thread_list.DiscardThreadPlans(); + + size_t response_size = m_comm.SendPacket ("D", 1); + if (log) + { + if (response_size) + log->PutCString ("ProcessKDP::DoDetach() detach packet sent successfully"); + else + log->PutCString ("ProcessKDP::DoDetach() detach packet send failed"); + } + // Sleep for one second to let the process get all detached... + StopAsyncThread (); + + m_comm.StopReadThread(); + m_comm.Disconnect(); // Disconnect from the debug server. + + SetPrivateState (eStateDetached); + ResumePrivateStateThread(); + + //KillDebugserverProcess (); + return error; +} + +Error +ProcessKDP::DoDestroy () +{ + Error error; + LogSP log (ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_PROCESS)); + if (log) + log->Printf ("ProcessKDP::DoDestroy()"); + + // Interrupt if our inferior is running... + if (m_comm.IsConnected()) + { + if (m_public_state.GetValue() == eStateAttaching) + { + // We are being asked to halt during an attach. We need to just close + // our file handle and debugserver will go away, and we can be done... + m_comm.Disconnect(); + } + else + { + + StringExtractor response; + // TODO: Send kill packet? + SetExitStatus(SIGABRT, NULL); + } + } + StopAsyncThread (); + m_comm.StopReadThread(); + m_comm.Disconnect(); // Disconnect from the debug server. + return error; +} + +//------------------------------------------------------------------ +// Process Queries +//------------------------------------------------------------------ + +bool +ProcessKDP::IsAlive () +{ + return m_comm.IsConnected() && m_private_state.GetValue() != eStateExited; +} + +//------------------------------------------------------------------ +// Process Memory +//------------------------------------------------------------------ +size_t +ProcessKDP::DoReadMemory (addr_t addr, void *buf, size_t size, Error &error) +{ + error.SetErrorString ("ProcessKDP::DoReadMemory not implemented"); + return 0; +} + +size_t +ProcessKDP::DoWriteMemory (addr_t addr, const void *buf, size_t size, Error &error) +{ + error.SetErrorString ("ProcessKDP::DoReadMemory not implemented"); + 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::EnableBreakpoint (BreakpointSite *bp_site) +{ + return EnableSoftwareBreakpoint (bp_site); +} + +Error +ProcessKDP::DisableBreakpoint (BreakpointSite *bp_site) +{ + return DisableSoftwareBreakpoint (bp_site); +} + +Error +ProcessKDP::EnableWatchpoint (WatchpointLocation *wp) +{ + Error error; + error.SetErrorString ("watchpoints are not suppported in kdp remote debugging"); + return error; +} + +Error +ProcessKDP::DisableWatchpoint (WatchpointLocation *wp) +{ + Error error; + error.SetErrorString ("watchpoints are not suppported in kdp remote debugging"); + return error; +} + +void +ProcessKDP::Clear() +{ + Mutex::Locker locker (m_thread_list.GetMutex ()); + 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 bool g_initialized = false; + + if (g_initialized == false) + { + g_initialized = true; + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); + + Log::Callbacks log_callbacks = { + ProcessKDPLog::DisableLog, + ProcessKDPLog::EnableLog, + ProcessKDPLog::ListLogCategories + }; + + Log::RegisterLogChannel (ProcessKDP::GetPluginNameStatic(), log_callbacks); + } +} + +bool +ProcessKDP::StartAsyncThread () +{ + LogSP log (ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_PROCESS)); + + if (log) + log->Printf ("ProcessKDP::%s ()", __FUNCTION__); + + // Create a thread that watches our internal state and controls which + // events make it to clients (into the DCProcess event queue). + m_async_thread = Host::ThreadCreate ("<lldb.process.kdp-remote.async>", ProcessKDP::AsyncThread, this, NULL); + return IS_VALID_LLDB_HOST_THREAD(m_async_thread); +} + +void +ProcessKDP::StopAsyncThread () +{ + LogSP log (ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_PROCESS)); + + if (log) + log->Printf ("ProcessKDP::%s ()", __FUNCTION__); + + m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncThreadShouldExit); + + // Stop the stdio thread + if (IS_VALID_LLDB_HOST_THREAD(m_async_thread)) + { + Host::ThreadJoin (m_async_thread, NULL, NULL); + } +} + + +void * +ProcessKDP::AsyncThread (void *arg) +{ + ProcessKDP *process = (ProcessKDP*) arg; + + LogSP log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PROCESS)); + if (log) + log->Printf ("ProcessKDP::%s (arg = %p, pid = %i) thread starting...", __FUNCTION__, arg, process->GetID()); + + 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) + { + listener.StartListeningForEvents (&process->m_comm, Communication::eBroadcastBitReadThreadDidExit); + + bool done = false; + while (!done) + { + if (log) + log->Printf ("ProcessKDP::%s (arg = %p, pid = %i) listener.WaitForEvent (NULL, event_sp)...", __FUNCTION__, arg, process->GetID()); + if (listener.WaitForEvent (NULL, event_sp)) + { + const uint32_t event_type = event_sp->GetType(); + if (event_sp->BroadcasterIs (&process->m_async_broadcaster)) + { + if (log) + log->Printf ("ProcessKDP::%s (arg = %p, pid = %i) Got an event of type: %d...", __FUNCTION__, arg, process->GetID(), event_type); + + switch (event_type) + { + case eBroadcastBitAsyncContinue: + { + const EventDataBytes *continue_packet = EventDataBytes::GetEventDataFromEvent(event_sp.get()); + + if (continue_packet) + { + // TODO: do continue support here + +// const char *continue_cstr = (const char *)continue_packet->GetBytes (); +// const size_t continue_cstr_len = continue_packet->GetByteSize (); +// if (log) +// log->Printf ("ProcessKDP::%s (arg = %p, pid = %i) got eBroadcastBitAsyncContinue: %s", __FUNCTION__, arg, process->GetID(), continue_cstr); +// +// if (::strstr (continue_cstr, "vAttach") == NULL) +// process->SetPrivateState(eStateRunning); +// StringExtractor response; +// StateType stop_state = process->GetCommunication().SendContinuePacketAndWaitForResponse (process, continue_cstr, continue_cstr_len, response); +// +// switch (stop_state) +// { +// case eStateStopped: +// case eStateCrashed: +// case eStateSuspended: +// process->m_last_stop_packet = response; +// process->SetPrivateState (stop_state); +// break; +// +// case eStateExited: +// process->m_last_stop_packet = response; +// response.SetFilePos(1); +// process->SetExitStatus(response.GetHexU8(), NULL); +// done = true; +// break; +// +// case eStateInvalid: +// process->SetExitStatus(-1, "lost connection"); +// break; +// +// default: +// process->SetPrivateState (stop_state); +// break; +// } + } + } + break; + + case eBroadcastBitAsyncThreadShouldExit: + if (log) + log->Printf ("ProcessKDP::%s (arg = %p, pid = %i) got eBroadcastBitAsyncThreadShouldExit...", __FUNCTION__, arg, process->GetID()); + done = true; + break; + + default: + if (log) + log->Printf ("ProcessKDP::%s (arg = %p, pid = %i) got unknown event 0x%8.8x", __FUNCTION__, arg, process->GetID(), event_type); + done = true; + break; + } + } + else if (event_sp->BroadcasterIs (&process->m_comm)) + { + if (event_type & Communication::eBroadcastBitReadThreadDidExit) + { + process->SetExitStatus (-1, "lost connection"); + done = true; + } + } + } + else + { + if (log) + log->Printf ("ProcessKDP::%s (arg = %p, pid = %i) listener.WaitForEvent (NULL, event_sp) => false", __FUNCTION__, arg, process->GetID()); + done = true; + } + } + } + + if (log) + log->Printf ("ProcessKDP::%s (arg = %p, pid = %i) thread exiting...", __FUNCTION__, arg, process->GetID()); + + process->m_async_thread = LLDB_INVALID_HOST_THREAD; + return NULL; +} + + diff --git a/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h b/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h new file mode 100644 index 00000000000..31907dd11ab --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h @@ -0,0 +1,283 @@ +//===-- 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/Error.h" +#include "lldb/Core/InputReader.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/StringList.h" +#include "lldb/Core/ThreadSafeValue.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" + +#include "CommunicationKDP.h" +#include "Utility/StringExtractor.h" + +class ThreadKDP; + +class ProcessKDP : public lldb_private::Process +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + static Process* + CreateInstance (lldb_private::Target& target, lldb_private::Listener &listener); + + static void + Initialize(); + + static void + Terminate(); + + static const char * + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + ProcessKDP(lldb_private::Target& target, lldb_private::Listener &listener); + + virtual + ~ProcessKDP(); + + //------------------------------------------------------------------ + // Check if a given Process + //------------------------------------------------------------------ + virtual bool + CanDebug (lldb_private::Target &target); + + // virtual uint32_t + // ListProcessesMatchingName (const char *name, lldb_private::StringList &matches, std::vector<lldb::pid_t> &pids); + + //------------------------------------------------------------------ + // 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* module, + char const *argv[], // Can be NULL + char const *envp[], // Can be NULL + uint32_t flags, + const char *stdin_path, // Can be NULL + const char *stdout_path, // Can be NULL + const char *stderr_path, // Can be NULL + const char *working_dir); // Can be NULL + + 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 (const char *remote_url); + + virtual lldb_private::Error + DoAttachToProcessWithID (lldb::pid_t pid); + + virtual lldb_private::Error + DoAttachToProcessWithName (const char *process_name, bool wait_for_launch); + + virtual void + DidAttach (); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + 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 + WillDetach (); + + virtual lldb_private::Error + DoDetach (); + + 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 + EnableBreakpoint (lldb_private::BreakpointSite *bp_site); + + virtual lldb_private::Error + DisableBreakpoint (lldb_private::BreakpointSite *bp_site); + + //---------------------------------------------------------------------- + // Process Watchpoints + //---------------------------------------------------------------------- + virtual lldb_private::Error + EnableWatchpoint (lldb_private::WatchpointLocation *wp_loc); + + virtual lldb_private::Error + DisableWatchpoint (lldb_private::WatchpointLocation *wp_loc); + +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 + ProcessIDIsValid ( ) const; + + // static void + // STDIOReadThreadBytesReceived (void *baton, const void *src, size_t src_len); + + // void + // AppendSTDOUT (const char* s, size_t len); + + void + Clear ( ); + + uint32_t + UpdateThreadListIfNeeded (); + + CommunicationKDP & + GetCommunication() + { + return m_comm; + } + + enum + { + eBroadcastBitAsyncContinue = (1 << 0), + eBroadcastBitAsyncThreadShouldExit = (1 << 1) + }; + + lldb_private::Error + InterruptIfRunning (bool discard_thread_plans, + bool catch_stop_event, + lldb::EventSP &stop_event_sp); + + //------------------------------------------------------------------ + /// Broadcaster event bits definitions. + //------------------------------------------------------------------ + CommunicationKDP m_comm; + lldb_private::Broadcaster m_async_broadcaster; + lldb::thread_t m_async_thread; + + bool + StartAsyncThread (); + + void + StopAsyncThread (); + + static void * + AsyncThread (void *arg); + + lldb::StateType + SetThreadStopInfo (StringExtractor& stop_packet); + + static size_t + AttachInputReaderCallback (void *baton, + lldb_private::InputReader *reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len); + +private: + //------------------------------------------------------------------ + // For ProcessKDP only + //------------------------------------------------------------------ + + DISALLOW_COPY_AND_ASSIGN (ProcessKDP); + +}; + +#endif // liblldb_ProcessKDP_h_ diff --git a/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDPLog.cpp b/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDPLog.cpp new file mode 100644 index 00000000000..ce25c3869ad --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDPLog.cpp @@ -0,0 +1,183 @@ +//===-- 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_lob_sp the first time this function is +// called. +static LogSP & +GetLog () +{ + static LogSP g_log_sp; + return g_log_sp; +} + +LogSP +ProcessKDPLog::GetLogIfAllCategoriesSet (uint32_t mask) +{ + LogSP log(GetLog ()); + if (log && mask) + { + uint32_t log_mask = log->GetMask().Get(); + if ((log_mask & mask) != mask) + return LogSP(); + } + return log; +} + +void +ProcessKDPLog::DisableLog (Args &args, Stream *feedback_strm) +{ + LogSP log (GetLog ()); + if (log) + { + uint32_t flag_bits = 0; + + const size_t argc = args.GetArgumentCount (); + if (argc > 0) + { + flag_bits = log->GetMask().Get(); + for (size_t i = 0; i < argc; ++i) + { + const char *arg = args.GetArgumentAtIndex (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); + } + + } + } + + if (flag_bits == 0) + GetLog ().reset(); + else + log->GetMask().Reset (flag_bits); + } + + return; +} + +LogSP +ProcessKDPLog::EnableLog (StreamSP &log_stream_sp, uint32_t log_options, Args &args, 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; + LogSP log(GetLog ()); + if (log) + flag_bits = log->GetMask().Get(); + + // Now make a new log with this stream if one was provided + if (log_stream_sp) + { + log = make_shared<Log>(log_stream_sp); + GetLog () = log; + } + + if (log) + { + bool got_unknown_category = false; + const size_t argc = args.GetArgumentCount(); + for (size_t i=0; i<argc; ++i) + { + const char *arg = args.GetArgumentAtIndex(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; + log->GetMask().Reset(flag_bits); + log->GetOptions().Reset(log_options); + } + return log; +} + +void +ProcessKDPLog::ListLogCategories (Stream *strm) +{ + strm->Printf("Logging categories for '%s':\n" + "\tall - turn on all available logging categories\n" + "\tasync - log asynchronous activity\n" + "\tbreak - log breakpoints\n" + "\tcommunication - log communication activity\n" + "\tdefault - enable the default set of logging categories for liblldb\n" + "\tpackets - log gdb remote packets\n" + "\tmemory - log memory reads and writes\n" + "\tdata-short - log memory bytes for memory reads and writes for short transactions only\n" + "\tdata-long - log memory bytes for memory reads and writes for all transactions\n" + "\tprocess - log process events and activities\n" + "\tthread - log thread events and activities\n" + "\tstep - log step related activities\n" + "\tverbose - enable verbose logging\n" + "\twatch - log watchpoint related activities\n", ProcessKDP::GetPluginNameStatic()); +} + + +void +ProcessKDPLog::LogIf (uint32_t mask, const char *format, ...) +{ + LogSP log (ProcessKDPLog::GetLogIfAllCategoriesSet (mask)); + if (log) + { + va_list args; + va_start (args, format); + log->VAPrintf (format, args); + va_end (args); + } +} diff --git a/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDPLog.h b/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDPLog.h new file mode 100644 index 00000000000..2fd55f789ad --- /dev/null +++ b/lldb/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::LogSP + GetLogIfAllCategoriesSet(uint32_t mask = 0); + + static void + DisableLog (lldb_private::Args &args, lldb_private::Stream *feedback_strm); + + static lldb::LogSP + EnableLog (lldb::StreamSP &log_stream_sp, uint32_t log_options, lldb_private::Args &args, 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_ |