diff options
author | emaste <emaste@FreeBSD.org> | 2013-08-23 18:06:42 +0000 |
---|---|---|
committer | emaste <emaste@FreeBSD.org> | 2013-08-23 18:06:42 +0000 |
commit | 424d4dadd208e2a1e9a43c3d55f47f03ba0c4509 (patch) | |
tree | 05d762b98a499804ce690e6ce04033f1ddf4dee6 /contrib/llvm/tools/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp | |
parent | cde487f27a84e02a560384f75178fddca68740f6 (diff) | |
parent | dcd15f81789e389c1cb27d264fcdddfd0a6002bd (diff) | |
download | FreeBSD-src-424d4dadd208e2a1e9a43c3d55f47f03ba0c4509.zip FreeBSD-src-424d4dadd208e2a1e9a43c3d55f47f03ba0c4509.tar.gz |
Merge lldb r188801 to contrib/llvm/tools/lldb/
Diffstat (limited to 'contrib/llvm/tools/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp')
-rw-r--r-- | contrib/llvm/tools/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp | 643 |
1 files changed, 643 insertions, 0 deletions
diff --git a/contrib/llvm/tools/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp b/contrib/llvm/tools/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp new file mode 100644 index 0000000..d7efdf2 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp @@ -0,0 +1,643 @@ +//===-- 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 +#include <limits.h> +#include <string.h> + +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamFile.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" + +// Project includes +#include "ProcessGDBRemoteLog.h" + +#define DEBUGSERVER_BASENAME "debugserver" + +using namespace lldb; +using namespace lldb_private; + +GDBRemoteCommunication::History::History (uint32_t size) : + m_packets(), + m_curr_idx (0), + m_total_packet_count (0), + m_dumped_to_log (false) +{ + m_packets.resize(size); +} + +GDBRemoteCommunication::History::~History () +{ +} + +void +GDBRemoteCommunication::History::AddPacket (char packet_char, + PacketType type, + uint32_t bytes_transmitted) +{ + const size_t size = m_packets.size(); + if (size > 0) + { + const uint32_t idx = GetNextIndex(); + m_packets[idx].packet.assign (1, packet_char); + m_packets[idx].type = type; + m_packets[idx].bytes_transmitted = bytes_transmitted; + m_packets[idx].packet_idx = m_total_packet_count; + m_packets[idx].tid = Host::GetCurrentThreadID(); + } +} + +void +GDBRemoteCommunication::History::AddPacket (const std::string &src, + uint32_t src_len, + PacketType type, + uint32_t bytes_transmitted) +{ + const size_t size = m_packets.size(); + if (size > 0) + { + const uint32_t idx = GetNextIndex(); + m_packets[idx].packet.assign (src, 0, src_len); + m_packets[idx].type = type; + m_packets[idx].bytes_transmitted = bytes_transmitted; + m_packets[idx].packet_idx = m_total_packet_count; + m_packets[idx].tid = Host::GetCurrentThreadID(); + } +} + +void +GDBRemoteCommunication::History::Dump (lldb_private::Stream &strm) const +{ + const uint32_t size = GetNumPacketsInHistory (); + const uint32_t first_idx = GetFirstSavedPacketIndex (); + const uint32_t stop_idx = m_curr_idx + size; + for (uint32_t i = first_idx; i < stop_idx; ++i) + { + const uint32_t idx = NormalizeIndex (i); + const Entry &entry = m_packets[idx]; + if (entry.type == ePacketTypeInvalid || entry.packet.empty()) + break; + strm.Printf ("history[%u] tid=0x%4.4" PRIx64 " <%4u> %s packet: %s\n", + entry.packet_idx, + entry.tid, + entry.bytes_transmitted, + (entry.type == ePacketTypeSend) ? "send" : "read", + entry.packet.c_str()); + } +} + +void +GDBRemoteCommunication::History::Dump (lldb_private::Log *log) const +{ + if (log && !m_dumped_to_log) + { + m_dumped_to_log = true; + const uint32_t size = GetNumPacketsInHistory (); + const uint32_t first_idx = GetFirstSavedPacketIndex (); + const uint32_t stop_idx = m_curr_idx + size; + for (uint32_t i = first_idx; i < stop_idx; ++i) + { + const uint32_t idx = NormalizeIndex (i); + const Entry &entry = m_packets[idx]; + if (entry.type == ePacketTypeInvalid || entry.packet.empty()) + break; + log->Printf ("history[%u] tid=0x%4.4" PRIx64 " <%4u> %s packet: %s", + entry.packet_idx, + entry.tid, + entry.bytes_transmitted, + (entry.type == ePacketTypeSend) ? "send" : "read", + entry.packet.c_str()); + } + } +} + +//---------------------------------------------------------------------- +// GDBRemoteCommunication constructor +//---------------------------------------------------------------------- +GDBRemoteCommunication::GDBRemoteCommunication(const char *comm_name, + const char *listener_name, + bool is_platform) : + Communication(comm_name), + m_packet_timeout (1), + m_sequence_mutex (Mutex::eMutexTypeRecursive), + m_public_is_running (false), + m_private_is_running (false), + m_history (512), + m_send_acks (true), + m_is_platform (is_platform) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +GDBRemoteCommunication::~GDBRemoteCommunication() +{ + if (IsConnected()) + { + Disconnect(); + } +} + +char +GDBRemoteCommunication::CalculcateChecksum (const char *payload, size_t payload_length) +{ + int checksum = 0; + + for (size_t i = 0; i < payload_length; ++i) + checksum += payload[i]; + + return checksum & 255; +} + +size_t +GDBRemoteCommunication::SendAck () +{ + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS)); + ConnectionStatus status = eConnectionStatusSuccess; + char ch = '+'; + const size_t bytes_written = Write (&ch, 1, status, NULL); + if (log) + log->Printf ("<%4zu> send packet: %c", bytes_written, ch); + m_history.AddPacket (ch, History::ePacketTypeSend, bytes_written); + return bytes_written; +} + +size_t +GDBRemoteCommunication::SendNack () +{ + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS)); + ConnectionStatus status = eConnectionStatusSuccess; + char ch = '-'; + const size_t bytes_written = Write (&ch, 1, status, NULL); + if (log) + log->Printf ("<%4zu> send packet: %c", bytes_written, ch); + m_history.AddPacket (ch, History::ePacketTypeSend, bytes_written); + return bytes_written; +} + +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)); + + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS)); + ConnectionStatus status = eConnectionStatusSuccess; + size_t bytes_written = Write (packet.GetData(), packet.GetSize(), status, NULL); + if (log) + { + // If logging was just enabled and we have history, then dump out what + // we have to the log so we get the historical context. The Dump() call that + // logs all of the packet will set a boolean so that we don't dump this more + // than once + if (!m_history.DidDumpToLog ()) + m_history.Dump (log); + + log->Printf ("<%4zu> send packet: %.*s", bytes_written, (int)packet.GetSize(), packet.GetData()); + } + + m_history.AddPacket (packet.GetString(), packet.GetSize(), History::ePacketTypeSend, bytes_written); + + + if (bytes_written == packet.GetSize()) + { + if (GetSendAcks ()) + { + if (GetAck () != '+') + { + if (log) + log->Printf("get ack failed..."); + return 0; + } + } + } + else + { + if (log) + log->Printf ("error: failed to send packet: %.*s", (int)packet.GetSize(), packet.GetData()); + } + return bytes_written; + } + return 0; +} + +char +GDBRemoteCommunication::GetAck () +{ + StringExtractorGDBRemote packet; + if (WaitForPacketWithTimeoutMicroSecondsNoLock (packet, GetPacketTimeoutInMicroSeconds ()) == 1) + return packet.GetChar(); + return 0; +} + +bool +GDBRemoteCommunication::GetSequenceMutex (Mutex::Locker& locker, const char *failure_message) +{ + if (IsRunning()) + return locker.TryLock (m_sequence_mutex, failure_message); + + locker.Lock (m_sequence_mutex); + return true; +} + + +bool +GDBRemoteCommunication::WaitForNotRunningPrivate (const TimeValue *timeout_ptr) +{ + return m_private_is_running.WaitForValueEqualTo (false, timeout_ptr, NULL); +} + +size_t +GDBRemoteCommunication::WaitForPacketWithTimeoutMicroSecondsNoLock (StringExtractorGDBRemote &packet, uint32_t timeout_usec) +{ + uint8_t buffer[8192]; + Error error; + + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS | GDBR_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 = eConnectionStatusNoConnection; + size_t bytes_read = Read (buffer, sizeof(buffer), timeout_usec, status, &error); + + if (log) + log->Printf ("%s: Read (buffer, (sizeof(buffer), timeout_usec = 0x%x, status = %s, error = %s) => bytes_read = %" PRIu64, + __PRETTY_FUNCTION__, + timeout_usec, + Communication::ConnectionStatusAsCString (status), + error.AsCString(), + (uint64_t)bytes_read); + + if (bytes_read > 0) + { + if (CheckForPacket (buffer, bytes_read, packet)) + return packet.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 +GDBRemoteCommunication::CheckForPacket (const uint8_t *src, size_t src_len, StringExtractorGDBRemote &packet) +{ + // Put the packet data into the buffer in a thread safe fashion + Mutex::Locker locker(m_bytes_mutex); + + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS)); + + if (src && src_len > 0) + { + if (log && log->GetVerbose()) + { + StreamString s; + log->Printf ("GDBRemoteCommunication::%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 ("GDBRemoteCommunication::%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(); + + + if (log) + { + // If logging was just enabled and we have history, then dump out what + // we have to the log so we get the historical context. The Dump() call that + // logs all of the packet will set a boolean so that we don't dump this more + // than once + if (!m_history.DidDumpToLog ()) + m_history.Dump (log); + + log->Printf ("<%4zu> read packet: %.*s", total_length, (int)(total_length), m_bytes.c_str()); + } + + m_history.AddPacket (m_bytes.c_str(), total_length, History::ePacketTypeRecv, total_length); + + 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(); + } + } + else + { + success = false; + if (log) + log->Printf ("error: invalid checksum in packet: '%s'\n", m_bytes.c_str()); + } + } + + m_bytes.erase(0, total_length); + packet.SetFilePos(0); + return success; + } + } + packet.Clear(); + return false; +} + +Error +GDBRemoteCommunication::StartDebugserverProcess (const char *debugserver_url, + const char *unix_socket_name, // For handshaking + lldb_private::ProcessLaunchInfo &launch_info) +{ + Error error; + // If we locate debugserver, keep that located version around + static FileSpec g_debugserver_file_spec; + + // This function will fill in the launch information for the debugserver + // instance that gets launched. + launch_info.Clear(); + + char debugserver_path[PATH_MAX]; + FileSpec &debugserver_file_spec = launch_info.GetExecutableFile(); + + // Always check to see if we have an environment override for the path + // to the debugserver to use and use it if we do. + const char *env_debugserver_path = getenv("LLDB_DEBUGSERVER_PATH"); + if (env_debugserver_path) + debugserver_file_spec.SetFile (env_debugserver_path, false); + else + debugserver_file_spec = g_debugserver_file_spec; + bool debugserver_exists = debugserver_file_spec.Exists(); + if (!debugserver_exists) + { + // The debugserver binary is in the LLDB.framework/Resources + // directory. + if (Host::GetLLDBPath (ePathTypeSupportExecutableDir, debugserver_file_spec)) + { + debugserver_file_spec.GetFilename().SetCString(DEBUGSERVER_BASENAME); + debugserver_exists = debugserver_file_spec.Exists(); + if (debugserver_exists) + { + g_debugserver_file_spec = debugserver_file_spec; + } + else + { + g_debugserver_file_spec.Clear(); + debugserver_file_spec.Clear(); + } + } + } + + if (debugserver_exists) + { + debugserver_file_spec.GetPath (debugserver_path, sizeof(debugserver_path)); + + Args &debugserver_args = launch_info.GetArguments(); + debugserver_args.Clear(); + char arg_cstr[PATH_MAX]; + + // Start args with "debugserver /file/path -r --" + debugserver_args.AppendArgument(debugserver_path); + debugserver_args.AppendArgument(debugserver_url); + // use native registers, not the GDB registers + debugserver_args.AppendArgument("--native-regs"); + // make debugserver run in its own session so signals generated by + // special terminal key sequences (^C) don't affect debugserver + debugserver_args.AppendArgument("--setsid"); + + if (unix_socket_name && unix_socket_name[0]) + { + debugserver_args.AppendArgument("--unix-socket"); + debugserver_args.AppendArgument(unix_socket_name); + } + + const char *env_debugserver_log_file = getenv("LLDB_DEBUGSERVER_LOG_FILE"); + if (env_debugserver_log_file) + { + ::snprintf (arg_cstr, sizeof(arg_cstr), "--log-file=%s", env_debugserver_log_file); + debugserver_args.AppendArgument(arg_cstr); + } + + const char *env_debugserver_log_flags = getenv("LLDB_DEBUGSERVER_LOG_FLAGS"); + if (env_debugserver_log_flags) + { + ::snprintf (arg_cstr, sizeof(arg_cstr), "--log-flags=%s", env_debugserver_log_flags); + debugserver_args.AppendArgument(arg_cstr); + } + // debugserver_args.AppendArgument("--log-file=/tmp/debugserver.txt"); + // debugserver_args.AppendArgument("--log-flags=0x802e0e"); + + // We currently send down all arguments, attach pids, or attach + // process names in dedicated GDB server packets, so we don't need + // to pass them as arguments. This is currently because of all the + // things we need to setup prior to launching: the environment, + // current working dir, file actions, etc. +#if 0 + // Now append the program arguments + if (inferior_argv) + { + // Terminate the debugserver args so we can now append the inferior args + debugserver_args.AppendArgument("--"); + + for (int i = 0; inferior_argv[i] != NULL; ++i) + debugserver_args.AppendArgument (inferior_argv[i]); + } + else if (attach_pid != LLDB_INVALID_PROCESS_ID) + { + ::snprintf (arg_cstr, sizeof(arg_cstr), "--attach=%u", attach_pid); + debugserver_args.AppendArgument (arg_cstr); + } + else if (attach_name && attach_name[0]) + { + if (wait_for_launch) + debugserver_args.AppendArgument ("--waitfor"); + else + debugserver_args.AppendArgument ("--attach"); + debugserver_args.AppendArgument (attach_name); + } +#endif + + // Close STDIN, STDOUT and STDERR. We might need to redirect them + // to "/dev/null" if we run into any problems. +// launch_info.AppendCloseFileAction (STDIN_FILENO); +// launch_info.AppendCloseFileAction (STDOUT_FILENO); +// launch_info.AppendCloseFileAction (STDERR_FILENO); + + error = Host::LaunchProcess(launch_info); + } + else + { + error.SetErrorStringWithFormat ("unable to locate " DEBUGSERVER_BASENAME ); + } + return error; +} + +void +GDBRemoteCommunication::DumpHistory(Stream &strm) +{ + m_history.Dump (strm); +} |