//===-- GDBRemoteCommunication.cpp ------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//


#include "GDBRemoteCommunication.h"

// C Includes
// C++ Includes
// Other libraries and framework includes
#include "lldb/Interpreter/Args.h"
#include "lldb/Core/ConnectionFileDescriptor.h"
#include "lldb/Core/Log.h"
#include "lldb/Core/State.h"
#include "lldb/Core/StreamString.h"
#include "lldb/Host/TimeValue.h"

// Project includes
#include "Utility/StringExtractorGDBRemote.h"
#include "ProcessGDBRemote.h"
#include "ProcessGDBRemoteLog.h"

using namespace lldb;
using namespace lldb_private;

//----------------------------------------------------------------------
// GDBRemoteCommunication constructor
//----------------------------------------------------------------------
GDBRemoteCommunication::GDBRemoteCommunication() :
    Communication("gdb-remote.packets"),
    m_send_acks (true),
    m_rx_packet_listener ("gdbremote.rx_packet"),
    m_sequence_mutex (Mutex::eMutexTypeRecursive),
    m_is_running (false),
    m_async_mutex (Mutex::eMutexTypeRecursive),
    m_async_packet_predicate (false),
    m_async_packet (),
    m_async_response (),
    m_async_timeout (UINT32_MAX),
    m_async_signal (-1),
    m_arch(),
    m_os(),
    m_vendor(),
    m_byte_order(eByteOrderHost),
    m_pointer_byte_size(0)
{
    m_rx_packet_listener.StartListeningForEvents(this,
                                                 Communication::eBroadcastBitPacketAvailable  |
                                                 Communication::eBroadcastBitReadThreadDidExit);
}

//----------------------------------------------------------------------
// Destructor
//----------------------------------------------------------------------
GDBRemoteCommunication::~GDBRemoteCommunication()
{
    m_rx_packet_listener.StopListeningForEvents(this,
                                                Communication::eBroadcastBitPacketAvailable  |
                                                Communication::eBroadcastBitReadThreadDidExit);
    if (IsConnected())
    {
        StopReadThread();
        Disconnect();
    }
}


char
GDBRemoteCommunication::CalculcateChecksum (const char *payload, size_t payload_length)
{
    int checksum = 0;

    // We only need to compute the checksum if we are sending acks
    if (m_send_acks)
    {
        for (size_t i = 0; i < payload_length; ++i)
            checksum += payload[i];
    }
    return checksum & 255;
}

size_t
GDBRemoteCommunication::SendAck (char ack_char)
{
    Mutex::Locker locker(m_sequence_mutex);
    ProcessGDBRemoteLog::LogIf (GDBR_LOG_PACKETS, "send packet: %c", ack_char);
    ConnectionStatus status = eConnectionStatusSuccess;
    return Write (&ack_char, 1, status, NULL) == 1;
}

size_t
GDBRemoteCommunication::SendPacketAndWaitForResponse
(
    const char *payload,
    StringExtractorGDBRemote &response,
    uint32_t timeout_seconds,
    bool send_async
)
{
    return SendPacketAndWaitForResponse (payload, 
                                         ::strlen (payload),
                                         response,
                                         timeout_seconds,
                                         send_async);
}

size_t
GDBRemoteCommunication::SendPacketAndWaitForResponse
(
    const char *payload,
    size_t payload_length,
    StringExtractorGDBRemote &response,
    uint32_t timeout_seconds,
    bool send_async
)
{
    Mutex::Locker locker;
    TimeValue timeout_time;
    timeout_time = TimeValue::Now();
    timeout_time.OffsetWithSeconds (timeout_seconds);

    if (GetSequenceMutex (locker))
    {
        if (SendPacketNoLock (payload, strlen(payload)))
            return WaitForPacketNoLock (response, &timeout_time);
    }
    else
    {
        if (send_async)
        {
            Mutex::Locker async_locker (m_async_mutex);
            m_async_packet.assign(payload, payload_length);
            m_async_timeout = timeout_seconds;
            m_async_packet_predicate.SetValue (true, eBroadcastNever);
            
            bool timed_out = false;
            if (SendInterrupt(locker, 1, &timed_out))
            {
                if (m_async_packet_predicate.WaitForValueEqualTo (false, &timeout_time, &timed_out))
                {
                    response = m_async_response;
                    return response.GetStringRef().size();
                }
            }
//            if (timed_out)
//                m_error.SetErrorString("Timeout.");
//            else
//                m_error.SetErrorString("Unknown error.");
        }
        else
        {
//            m_error.SetErrorString("Sequence mutex is locked.");
        }
    }
    return 0;
}

//template<typename _Tp>
//class ScopedValueChanger
//{
//public:
//    // Take a value reference and the value to assing it to when this class
//    // instance goes out of scope.
//    ScopedValueChanger (_Tp &value_ref, _Tp value) :
//        m_value_ref (value_ref),
//        m_value (value)
//    {
//    }
//
//    // This object is going out of scope, change the value pointed to by
//    // m_value_ref to the value we got during construction which was stored in
//    // m_value;
//    ~ScopedValueChanger ()
//    {
//        m_value_ref = m_value;
//    }
//protected:
//    _Tp &m_value_ref;   // A reference to the value we wil change when this object destructs
//    _Tp m_value;        // The value to assign to m_value_ref when this goes out of scope.
//};

StateType
GDBRemoteCommunication::SendContinuePacketAndWaitForResponse
(
    ProcessGDBRemote *process,
    const char *payload,
    size_t packet_length,
    StringExtractorGDBRemote &response
)
{
    Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS);
    Log *async_log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_ASYNC);
    if (log)
        log->Printf ("GDBRemoteCommunication::%s ()", __FUNCTION__);

    Mutex::Locker locker(m_sequence_mutex);
    m_is_running.SetValue (true, eBroadcastNever);

//    ScopedValueChanger<bool> restore_running_to_false (m_is_running, false);
    StateType state = eStateRunning;

    if (SendPacket(payload, packet_length) == 0)
        state = eStateInvalid;

    while (state == eStateRunning)
    {
        if (log)
            log->Printf ("GDBRemoteCommunication::%s () WaitForPacket(...)", __FUNCTION__);

        if (WaitForPacket (response, (TimeValue*)NULL))
        {
            if (response.Empty())
                state = eStateInvalid;
            else
            {
                const char stop_type = response.GetChar();
                if (log)
                    log->Printf ("GDBRemoteCommunication::%s () got '%c' packet", __FUNCTION__, stop_type);
                switch (stop_type)
                {
                case 'T':
                case 'S':
                    if (m_async_signal != -1)
                    {
                        if (async_log) 
                            async_log->Printf ("async: send signo = %s", Host::GetSignalAsCString (m_async_signal));

                        // Save off the async signal we are supposed to send
                        const int async_signal = m_async_signal;
                        // Clear the async signal member so we don't end up
                        // sending the signal multiple times...
                        m_async_signal = -1;
                        // Check which signal we stopped with
                        uint8_t signo = response.GetHexU8(255);
                        if (signo == async_signal)
                        {
                            if (async_log) 
                                async_log->Printf ("async: stopped with signal %s, we are done running", Host::GetSignalAsCString (signo));

                            // We already stopped with a signal that we wanted
                            // to stop with, so we are done
                            response.SetFilePos (0);
                        }
                        else
                        {
                            // We stopped with a different signal that the one
                            // we wanted to stop with, so now we must resume
                            // with the signal we want
                            char signal_packet[32];
                            int signal_packet_len = 0;
                            signal_packet_len = ::snprintf (signal_packet,
                                                            sizeof (signal_packet),
                                                            "C%2.2x",
                                                            async_signal);

                            if (async_log) 
                                async_log->Printf ("async: stopped with signal %s, resume with %s", 
                                                   Host::GetSignalAsCString (signo),
                                                   Host::GetSignalAsCString (async_signal));

                            if (SendPacket(signal_packet, signal_packet_len) == 0)
                            {
                                if (async_log) 
                                    async_log->Printf ("async: error: failed to resume with %s", 
                                                       Host::GetSignalAsCString (async_signal));
                                state = eStateInvalid;
                                break;
                            }
                            else
                                continue;
                        }
                    }
                    else if (m_async_packet_predicate.GetValue())
                    {
                        if (async_log) 
                            async_log->Printf ("async: send async packet: %s", 
                                               m_async_packet.c_str());

                        // We are supposed to send an asynchronous packet while
                        // we are running. 
                        m_async_response.Clear();
                        if (!m_async_packet.empty())
                        {
                            SendPacketAndWaitForResponse (&m_async_packet[0], 
                                                          m_async_packet.size(),
                                                          m_async_response,
                                                          m_async_timeout,
                                                          false);
                        }
                        // Let the other thread that was trying to send the async
                        // packet know that the packet has been sent.
                        m_async_packet_predicate.SetValue(false, eBroadcastAlways);

                        if (async_log) 
                            async_log->Printf ("async: resume after async response received: %s", 
                                               m_async_response.GetStringRef().c_str());

                        // Continue again
                        if (SendPacket("c", 1) == 0)
                        {
                            state = eStateInvalid;
                            break;
                        }
                        else
                            continue;
                    }
                    // Stop with signal and thread info
                    state = eStateStopped;
                    break;

                case 'W':
                    // process exited
                    state = eStateExited;
                    break;

                case 'O':
                    // STDOUT
                    {
                        std::string inferior_stdout;
                        inferior_stdout.reserve(response.GetBytesLeft () / 2);
                        char ch;
                        while ((ch = response.GetHexU8()) != '\0')
                            inferior_stdout.append(1, ch);
                        process->AppendSTDOUT (inferior_stdout.c_str(), inferior_stdout.size());
                    }
                    break;

                case 'E':
                    // ERROR
                    state = eStateInvalid;
                    break;

                default:
                    if (log)
                        log->Printf ("GDBRemoteCommunication::%s () got unrecognized async packet: '%s'", __FUNCTION__, stop_type);
                    break;
                }
            }
        }
        else
        {
            if (log)
                log->Printf ("GDBRemoteCommunication::%s () WaitForPacket(...) => false", __FUNCTION__);
            state = eStateInvalid;
        }
    }
    if (log)
        log->Printf ("GDBRemoteCommunication::%s () => %s", __FUNCTION__, StateAsCString(state));
    response.SetFilePos(0);
    m_is_running.SetValue (false, eBroadcastOnChange);
    return state;
}

size_t
GDBRemoteCommunication::SendPacket (const char *payload)
{
    Mutex::Locker locker(m_sequence_mutex);
    return SendPacketNoLock (payload, ::strlen (payload));
}

size_t
GDBRemoteCommunication::SendPacket (const char *payload, size_t payload_length)
{
    Mutex::Locker locker(m_sequence_mutex);
    return SendPacketNoLock (payload, payload_length);
}

size_t
GDBRemoteCommunication::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));

        ProcessGDBRemoteLog::LogIf (GDBR_LOG_PACKETS, "send packet: %s", packet.GetData());
        ConnectionStatus status = eConnectionStatusSuccess;
        size_t bytes_written = Write (packet.GetData(), packet.GetSize(), status, NULL);
        if (bytes_written == packet.GetSize())
        {
            if (m_send_acks)
            {
                if (GetAck (1) != '+')
                    return 0;
            }
        }
        return bytes_written;
   }
    //m_error.SetErrorString("Not connected.");
    return 0;
}

char
GDBRemoteCommunication::GetAck (uint32_t timeout_seconds)
{
    StringExtractorGDBRemote response;
    if (WaitForPacket (response, timeout_seconds) == 1)
        return response.GetChar();
    return 0;
}

bool
GDBRemoteCommunication::GetSequenceMutex (Mutex::Locker& locker)
{
    return locker.TryLock (m_sequence_mutex.GetMutex());
}

bool
GDBRemoteCommunication::SendAsyncSignal (int signo)
{
    m_async_signal = signo;
    bool timed_out = false;
    Mutex::Locker locker;
    if (SendInterrupt (locker, 1, &timed_out))
        return true;
    m_async_signal = -1;
    return false;
}

// This function takes a mutex locker as a parameter in case the GetSequenceMutex
// actually succeeds. If it doesn't succeed in acquiring the sequence mutex 
// (the expected result), then it will send the halt packet. If it does succeed
// then the caller that requested the interrupt will want to keep the sequence
// locked down so that no one else can send packets while the caller has control.
// This function usually gets called when we are running and need to stop the 
// target. It can also be used when we are running and and we need to do something
// else (like read/write memory), so we need to interrupt the running process
// (gdb remote protocol requires this), and do what we need to do, then resume.

bool
GDBRemoteCommunication::SendInterrupt (Mutex::Locker& locker, uint32_t seconds_to_wait_for_stop, bool *timed_out)
{
    if (timed_out)
        *timed_out = false;

    if (IsConnected() && IsRunning())
    {
        // Only send an interrupt if our debugserver is running...
        if (GetSequenceMutex (locker) == false)
        {
            // Someone has the mutex locked waiting for a response or for the
            // inferior to stop, so send the interrupt on the down low...
            char ctrl_c = '\x03';
            ConnectionStatus status = eConnectionStatusSuccess;
            TimeValue timeout;
            if (seconds_to_wait_for_stop)
            {
                timeout = TimeValue::Now();
                timeout.OffsetWithSeconds (seconds_to_wait_for_stop);
            }
            ProcessGDBRemoteLog::LogIf (GDBR_LOG_PACKETS, "send packet: \\x03");
            if (Write (&ctrl_c, 1, status, NULL) > 0)
            {
                if (seconds_to_wait_for_stop)
                    m_is_running.WaitForValueEqualTo (false, &timeout, timed_out);
                return true;
            }
        }
    }
    return false;
}

size_t
GDBRemoteCommunication::WaitForPacket (StringExtractorGDBRemote &response, uint32_t timeout_seconds)
{
    TimeValue timeout_time;
    timeout_time = TimeValue::Now();
    timeout_time.OffsetWithSeconds (timeout_seconds);
    return WaitForPacketNoLock (response, &timeout_time);
}

size_t
GDBRemoteCommunication::WaitForPacket (StringExtractorGDBRemote &response, TimeValue* timeout_time_ptr)
{
    Mutex::Locker locker(m_sequence_mutex);
    return WaitForPacketNoLock (response, timeout_time_ptr);
}

size_t
GDBRemoteCommunication::WaitForPacketNoLock (StringExtractorGDBRemote &response, TimeValue* timeout_time_ptr)
{
    bool checksum_error = false;
    response.Clear ();

    EventSP event_sp;

    if (m_rx_packet_listener.WaitForEvent (timeout_time_ptr, event_sp))
    {
        const uint32_t event_type = event_sp->GetType();
        if (event_type | Communication::eBroadcastBitPacketAvailable)
        {
            const EventDataBytes *event_bytes = EventDataBytes::GetEventDataFromEvent(event_sp.get());
            if (event_bytes)
            {
                const char * packet_data =  (const char *)event_bytes->GetBytes();
                ProcessGDBRemoteLog::LogIf (GDBR_LOG_PACKETS, "read packet: %s", packet_data);
                const size_t packet_size =  event_bytes->GetByteSize();
                if (packet_data && packet_size > 0)
                {
                    std::string &response_str = response.GetStringRef();
                    if (packet_data[0] == '$')
                    {
                        assert (packet_size >= 4);  // Must have at least '$#CC' where CC is checksum
                        assert (packet_data[packet_size-3] == '#');
                        assert (::isxdigit (packet_data[packet_size-2]));  // Must be checksum hex byte
                        assert (::isxdigit (packet_data[packet_size-1]));  // Must be checksum hex byte
                        response_str.assign (packet_data + 1, packet_size - 4);
                        if (m_send_acks)
                        {
                            char packet_checksum = strtol (&packet_data[packet_size-2], NULL, 16);
                            char actual_checksum = CalculcateChecksum (&response_str[0], response_str.size());
                            checksum_error = packet_checksum != actual_checksum;
                            // Send the ack or nack if needed
                            if (checksum_error)
                                SendAck('-');
                            else
                                SendAck('+');
                        }
                    }
                    else
                    {
                        response_str.assign (packet_data, packet_size);
                    }
                    return response_str.size();
                }
            }
        }
        else if (Communication::eBroadcastBitReadThreadDidExit)
        {
            // Our read thread exited on us so just fall through and return zero...
        }
    }
    return 0;
}

void
GDBRemoteCommunication::AppendBytesToCache (const uint8_t *src, size_t src_len, bool broadcast)
{
    // Put the packet data into the buffer in a thread safe fashion
    Mutex::Locker locker(m_bytes_mutex);
    m_bytes.append ((const char *)src, src_len);

    // Parse up the packets into gdb remote packets
    while (!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 end_idx = 0;

        switch (m_bytes[0])
        {
            case '+':       // Look for ack
            case '-':       // Look for cancel
            case '\x03':    // ^C to halt target
                end_idx = 1;  // The command is one byte long...
                break;

            case '$':
                // Look for a standard gdb packet?
                end_idx = m_bytes.find('#');
                if (end_idx != std::string::npos)
                {
                    if (end_idx + 2 < m_bytes.size())
                    {
                        end_idx += 3;
                    }
                    else
                    {
                        // Checksum bytes aren't all here yet
                        end_idx = std::string::npos;
                    }
                }
                break;

            default:
                break;
        }

        if (end_idx == std::string::npos)
        {
            //ProcessGDBRemoteLog::LogIf (GDBR_LOG_PACKETS | GDBR_LOG_VERBOSE, "GDBRemoteCommunication::%s packet not yet complete: '%s'",__FUNCTION__, m_bytes.c_str());
            return;
        }
        else if (end_idx > 0)
        {
            // We have a valid packet...
            assert (end_idx <= m_bytes.size());
            std::auto_ptr<EventDataBytes> event_bytes_ap (new EventDataBytes (&m_bytes[0], end_idx));
            ProcessGDBRemoteLog::LogIf (GDBR_LOG_COMM, "got full packet: %s", event_bytes_ap->GetBytes());
            BroadcastEvent (eBroadcastBitPacketAvailable, event_bytes_ap.release());
            m_bytes.erase(0, end_idx);
        }
        else
        {
            assert (1 <= m_bytes.size());
            ProcessGDBRemoteLog::LogIf (GDBR_LOG_COMM, "GDBRemoteCommunication::%s tossing junk byte at %c",__FUNCTION__, m_bytes[0]);
            m_bytes.erase(0, 1);
        }
    }
}

lldb::pid_t
GDBRemoteCommunication::GetCurrentProcessID (uint32_t timeout_seconds)
{
    StringExtractorGDBRemote response;
    if (SendPacketAndWaitForResponse("qC", strlen("qC"), response, timeout_seconds, false))
    {
        if (response.GetChar() == 'Q')
            if (response.GetChar() == 'C')
                return response.GetHexMaxU32 (false, LLDB_INVALID_PROCESS_ID);
    }
    return LLDB_INVALID_PROCESS_ID;
}

bool
GDBRemoteCommunication::GetLaunchSuccess (uint32_t timeout_seconds, std::string &error_str)
{
    error_str.clear();
    StringExtractorGDBRemote response;
    if (SendPacketAndWaitForResponse("qLaunchSuccess", strlen("qLaunchSuccess"), response, timeout_seconds, false))
    {
        if (response.IsOKPacket())
            return true;
        if (response.GetChar() == 'E')
        {
            // A string the describes what failed when launching...
            error_str = response.GetStringRef().substr(1);
        }
        else
        {
            error_str.assign ("unknown error occurred launching process");
        }
    }
    else
    {
        error_str.assign ("failed to send the qLaunchSuccess packet");
    }
    return false;
}

int
GDBRemoteCommunication::SendArgumentsPacket (char const *argv[], uint32_t timeout_seconds)
{
    if (argv && argv[0])
    {
        StreamString packet;
        packet.PutChar('A');
        const char *arg;
        for (uint32_t i = 0; (arg = argv[i]) != NULL; ++i)
        {
            const int arg_len = strlen(arg);
            if (i > 0)
                packet.PutChar(',');
            packet.Printf("%i,%i,", arg_len * 2, i);
            packet.PutBytesAsRawHex8(arg, arg_len, eByteOrderHost, eByteOrderHost);
        }

        StringExtractorGDBRemote response;
        if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, timeout_seconds, false))
        {
            if (response.IsOKPacket())
                return 0;
            uint8_t error = response.GetError();
            if (error)
                return error;
        }
    }
    return -1;
}

int
GDBRemoteCommunication::SendEnvironmentPacket (char const *name_equal_value, uint32_t timeout_seconds)
{
    if (name_equal_value && name_equal_value[0])
    {
        StreamString packet;
        packet.Printf("QEnvironment:%s", name_equal_value);
        StringExtractorGDBRemote response;
        if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, timeout_seconds, false))
        {
            if (response.IsOKPacket())
                return 0;
            uint8_t error = response.GetError();
            if (error)
                return error;
        }
    }
    return -1;
}

bool
GDBRemoteCommunication::GetHostInfo (uint32_t timeout_seconds)
{
    m_arch.Clear();
    m_os.Clear();
    m_vendor.Clear();
    m_byte_order = eByteOrderHost;
    m_pointer_byte_size = 0;

    StringExtractorGDBRemote response;
    if (SendPacketAndWaitForResponse ("qHostInfo", response, timeout_seconds, false))
    {
        if (response.IsUnsupportedPacket())
            return false;

        
        std::string name;
        std::string value;
        while (response.GetNameColonValue(name, value))
        {
            if (name.compare("cputype") == 0)
            {
                // exception type in big endian hex
                m_arch.SetCPUType(Args::StringToUInt32 (value.c_str(), LLDB_INVALID_CPUTYPE, 0));
            }
            else if (name.compare("cpusubtype") == 0)
            {
                // exception count in big endian hex
                m_arch.SetCPUSubtype(Args::StringToUInt32 (value.c_str(), 0, 0));
            }
            else if (name.compare("ostype") == 0)
            {
                // exception data in big endian hex
                m_os.SetCString(value.c_str());
            }
            else if (name.compare("vendor") == 0)
            {
                m_vendor.SetCString(value.c_str());
            }
            else if (name.compare("endian") == 0)
            {
                if (value.compare("little") == 0)
                    m_byte_order = eByteOrderLittle;
                else if (value.compare("big") == 0)
                    m_byte_order = eByteOrderBig;
                else if (value.compare("pdp") == 0)
                    m_byte_order = eByteOrderPDP;
            }
            else if (name.compare("ptrsize") == 0)
            {
                m_pointer_byte_size = Args::StringToUInt32 (value.c_str(), 0, 0);
            }
        }
    }
    return HostInfoIsValid();
}

int
GDBRemoteCommunication::SendAttach 
(
    lldb::pid_t pid, 
    uint32_t timeout_seconds, 
    StringExtractorGDBRemote& response
)
{
    if (pid != LLDB_INVALID_PROCESS_ID)
    {
        StreamString packet;
        packet.Printf("vAttach;%x", pid);
        
        if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, timeout_seconds, false))
        {
            if (response.IsErrorPacket())
                return response.GetError();
            return 0;
        }
    }
    return -1;
}

const lldb_private::ArchSpec &
GDBRemoteCommunication::GetHostArchitecture ()
{
    if (!HostInfoIsValid ())
        GetHostInfo (1);
    return m_arch;
}

const lldb_private::ConstString &
GDBRemoteCommunication::GetOSString ()
{
    if (!HostInfoIsValid ())
        GetHostInfo (1);
    return m_os;
}

const lldb_private::ConstString &
GDBRemoteCommunication::GetVendorString()
{
    if (!HostInfoIsValid ())
        GetHostInfo (1);
    return m_vendor;
}

lldb::ByteOrder
GDBRemoteCommunication::GetByteOrder ()
{
    if (!HostInfoIsValid ())
        GetHostInfo (1);
    return m_byte_order;
}

uint32_t
GDBRemoteCommunication::GetAddressByteSize ()
{
    if (!HostInfoIsValid ())
        GetHostInfo (1);
    return m_pointer_byte_size;
}

addr_t
GDBRemoteCommunication::AllocateMemory (size_t size, uint32_t permissions, uint32_t timeout_seconds)
{
    char packet[64];
    ::snprintf (packet, sizeof(packet), "_M%zx,%s%s%s", size,
                permissions & lldb::ePermissionsReadable ? "r" : "",
                permissions & lldb::ePermissionsWritable ? "w" : "",
                permissions & lldb::ePermissionsExecutable ? "x" : "");
    StringExtractorGDBRemote response;
    if (SendPacketAndWaitForResponse (packet, response, timeout_seconds, false))
    {
        if (!response.IsErrorPacket())
            return response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS);
    }
    return LLDB_INVALID_ADDRESS;
}

bool
GDBRemoteCommunication::DeallocateMemory (addr_t addr, uint32_t timeout_seconds)
{
    char packet[64];
    snprintf(packet, sizeof(packet), "_m%llx", (uint64_t)addr);
    StringExtractorGDBRemote response;
    if (SendPacketAndWaitForResponse (packet, response, timeout_seconds, false))
    {
        if (!response.IsOKPacket())
            return true;
    }
    return false;
}
