forked from OSchip/llvm-project
Hollowed out process plug-in to do KDP darwin kernel debugging.
llvm-svn: 135240
This commit is contained in:
parent
124342c467
commit
f9765acddd
|
@ -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;
|
||||
}
|
||||
|
|
@ -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_
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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_
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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_
|
Loading…
Reference in New Issue