Hollowed out process plug-in to do KDP darwin kernel debugging.

llvm-svn: 135240
This commit is contained in:
Greg Clayton 2011-07-15 03:27:12 +00:00
parent 124342c467
commit f9765acddd
6 changed files with 1800 additions and 0 deletions

View File

@ -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;
}

View File

@ -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_

View File

@ -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;
}

View File

@ -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_

View File

@ -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);
}
}

View File

@ -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_