GDBRemoteCommunication.cpp revision 604f0d336f0d9390a0405022ef660ae922ef29bf
1//===-- GDBRemoteCommunication.cpp ------------------------------*- C++ -*-===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9 10 11#include "GDBRemoteCommunication.h" 12 13// C Includes 14#include <limits.h> 15#include <string.h> 16 17// C++ Includes 18// Other libraries and framework includes 19#include "lldb/Core/Log.h" 20#include "lldb/Core/StreamString.h" 21#include "lldb/Host/FileSpec.h" 22#include "lldb/Host/Host.h" 23#include "lldb/Host/TimeValue.h" 24#include "lldb/Target/Process.h" 25 26// Project includes 27#include "ProcessGDBRemoteLog.h" 28 29#define DEBUGSERVER_BASENAME "debugserver" 30 31using namespace lldb; 32using namespace lldb_private; 33 34//---------------------------------------------------------------------- 35// GDBRemoteCommunication constructor 36//---------------------------------------------------------------------- 37GDBRemoteCommunication::GDBRemoteCommunication(const char *comm_name, 38 const char *listener_name, 39 bool is_platform) : 40 Communication(comm_name), 41 m_packet_timeout (1), 42 m_sequence_mutex (Mutex::eMutexTypeRecursive), 43 m_public_is_running (false), 44 m_private_is_running (false), 45 m_send_acks (true), 46 m_is_platform (is_platform) 47{ 48} 49 50//---------------------------------------------------------------------- 51// Destructor 52//---------------------------------------------------------------------- 53GDBRemoteCommunication::~GDBRemoteCommunication() 54{ 55 if (IsConnected()) 56 { 57 Disconnect(); 58 } 59} 60 61char 62GDBRemoteCommunication::CalculcateChecksum (const char *payload, size_t payload_length) 63{ 64 int checksum = 0; 65 66 // We only need to compute the checksum if we are sending acks 67 if (GetSendAcks ()) 68 { 69 for (size_t i = 0; i < payload_length; ++i) 70 checksum += payload[i]; 71 } 72 return checksum & 255; 73} 74 75size_t 76GDBRemoteCommunication::SendAck () 77{ 78 LogSP log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS)); 79 if (log) 80 log->Printf ("send packet: +"); 81 ConnectionStatus status = eConnectionStatusSuccess; 82 char ack_char = '+'; 83 return Write (&ack_char, 1, status, NULL); 84} 85 86size_t 87GDBRemoteCommunication::SendNack () 88{ 89 LogSP log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS)); 90 if (log) 91 log->Printf ("send packet: -"); 92 ConnectionStatus status = eConnectionStatusSuccess; 93 char nack_char = '-'; 94 return Write (&nack_char, 1, status, NULL); 95} 96 97size_t 98GDBRemoteCommunication::SendPacket (lldb_private::StreamString &payload) 99{ 100 Mutex::Locker locker(m_sequence_mutex); 101 const std::string &p (payload.GetString()); 102 return SendPacketNoLock (p.c_str(), p.size()); 103} 104 105size_t 106GDBRemoteCommunication::SendPacket (const char *payload) 107{ 108 Mutex::Locker locker(m_sequence_mutex); 109 return SendPacketNoLock (payload, ::strlen (payload)); 110} 111 112size_t 113GDBRemoteCommunication::SendPacket (const char *payload, size_t payload_length) 114{ 115 Mutex::Locker locker(m_sequence_mutex); 116 return SendPacketNoLock (payload, payload_length); 117} 118 119size_t 120GDBRemoteCommunication::SendPacketNoLock (const char *payload, size_t payload_length) 121{ 122 if (IsConnected()) 123 { 124 StreamString packet(0, 4, eByteOrderBig); 125 126 packet.PutChar('$'); 127 packet.Write (payload, payload_length); 128 packet.PutChar('#'); 129 packet.PutHex8(CalculcateChecksum (payload, payload_length)); 130 131 LogSP log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS)); 132 if (log) 133 log->Printf ("send packet: %.*s", (int)packet.GetSize(), packet.GetData()); 134 ConnectionStatus status = eConnectionStatusSuccess; 135 size_t bytes_written = Write (packet.GetData(), packet.GetSize(), status, NULL); 136 if (bytes_written == packet.GetSize()) 137 { 138 if (GetSendAcks ()) 139 { 140 if (GetAck () != '+') 141 { 142 printf("get ack failed..."); 143 return 0; 144 } 145 } 146 } 147 else 148 { 149 LogSP log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS)); 150 if (log) 151 log->Printf ("error: failed to send packet: %.*s", (int)packet.GetSize(), packet.GetData()); 152 } 153 return bytes_written; 154 } 155 return 0; 156} 157 158char 159GDBRemoteCommunication::GetAck () 160{ 161 StringExtractorGDBRemote packet; 162 if (WaitForPacketWithTimeoutMicroSeconds (packet, GetPacketTimeoutInMicroSeconds ()) == 1) 163 return packet.GetChar(); 164 return 0; 165} 166 167bool 168GDBRemoteCommunication::GetSequenceMutex (Mutex::Locker& locker) 169{ 170 return locker.TryLock (m_sequence_mutex.GetMutex()); 171} 172 173 174bool 175GDBRemoteCommunication::WaitForNotRunningPrivate (const TimeValue *timeout_ptr) 176{ 177 return m_private_is_running.WaitForValueEqualTo (false, timeout_ptr, NULL); 178} 179 180size_t 181GDBRemoteCommunication::WaitForPacketWithTimeoutMicroSeconds (StringExtractorGDBRemote &packet, uint32_t timeout_usec) 182{ 183 Mutex::Locker locker(m_sequence_mutex); 184 return WaitForPacketWithTimeoutMicroSecondsNoLock (packet, timeout_usec); 185} 186 187size_t 188GDBRemoteCommunication::WaitForPacketWithTimeoutMicroSecondsNoLock (StringExtractorGDBRemote &packet, uint32_t timeout_usec) 189{ 190 uint8_t buffer[8192]; 191 Error error; 192 193 // Check for a packet from our cache first without trying any reading... 194 if (CheckForPacket (NULL, 0, packet)) 195 return packet.GetStringRef().size(); 196 197 bool timed_out = false; 198 while (IsConnected() && !timed_out) 199 { 200 lldb::ConnectionStatus status; 201 size_t bytes_read = Read (buffer, sizeof(buffer), timeout_usec, status, &error); 202 if (bytes_read > 0) 203 { 204 if (CheckForPacket (buffer, bytes_read, packet)) 205 return packet.GetStringRef().size(); 206 } 207 else 208 { 209 switch (status) 210 { 211 case eConnectionStatusSuccess: 212 break; 213 214 case eConnectionStatusEndOfFile: 215 case eConnectionStatusNoConnection: 216 case eConnectionStatusLostConnection: 217 case eConnectionStatusError: 218 Disconnect(); 219 break; 220 221 case eConnectionStatusTimedOut: 222 timed_out = true; 223 break; 224 } 225 } 226 } 227 packet.Clear (); 228 return 0; 229} 230 231bool 232GDBRemoteCommunication::CheckForPacket (const uint8_t *src, size_t src_len, StringExtractorGDBRemote &packet) 233{ 234 // Put the packet data into the buffer in a thread safe fashion 235 Mutex::Locker locker(m_bytes_mutex); 236 if (src && src_len > 0) 237 m_bytes.append ((const char *)src, src_len); 238 239 // Parse up the packets into gdb remote packets 240 while (!m_bytes.empty()) 241 { 242 // end_idx must be one past the last valid packet byte. Start 243 // it off with an invalid value that is the same as the current 244 // index. 245 size_t content_start = 0; 246 size_t content_length = 0; 247 size_t total_length = 0; 248 size_t checksum_idx = std::string::npos; 249 LogSP log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS)); 250 251 switch (m_bytes[0]) 252 { 253 case '+': // Look for ack 254 case '-': // Look for cancel 255 case '\x03': // ^C to halt target 256 content_length = total_length = 1; // The command is one byte long... 257 break; 258 259 case '$': 260 // Look for a standard gdb packet? 261 { 262 size_t hash_pos = m_bytes.find('#'); 263 if (hash_pos != std::string::npos) 264 { 265 if (hash_pos + 2 < m_bytes.size()) 266 { 267 checksum_idx = hash_pos + 1; 268 // Skip the dollar sign 269 content_start = 1; 270 // Don't include the # in the content or the $ in the content length 271 content_length = hash_pos - 1; 272 273 total_length = hash_pos + 3; // Skip the # and the two hex checksum bytes 274 } 275 else 276 { 277 // Checksum bytes aren't all here yet 278 content_length = std::string::npos; 279 } 280 } 281 } 282 break; 283 284 default: 285 { 286 if (log) 287 log->Printf ("GDBRemoteCommunication::%s tossing junk byte at %c",__FUNCTION__, m_bytes[0]); 288 m_bytes.erase(0, 1); 289 } 290 break; 291 } 292 293 if (content_length == std::string::npos) 294 { 295 packet.Clear(); 296 return false; 297 } 298 else if (total_length > 0) 299 { 300 301 // We have a valid packet... 302 assert (content_length <= m_bytes.size()); 303 assert (total_length <= m_bytes.size()); 304 assert (content_length <= total_length); 305 306 bool success = true; 307 std::string &packet_str = packet.GetStringRef(); 308 packet_str.assign (m_bytes, content_start, content_length); 309 if (m_bytes[0] == '$') 310 { 311 assert (checksum_idx < m_bytes.size()); 312 if (::isxdigit (m_bytes[checksum_idx+0]) || 313 ::isxdigit (m_bytes[checksum_idx+1])) 314 { 315 if (GetSendAcks ()) 316 { 317 const char *packet_checksum_cstr = &m_bytes[checksum_idx]; 318 char packet_checksum = strtol (packet_checksum_cstr, NULL, 16); 319 char actual_checksum = CalculcateChecksum (packet_str.c_str(), packet_str.size()); 320 success = packet_checksum == actual_checksum; 321 if (!success) 322 { 323 if (log) 324 log->Printf ("error: checksum mismatch: %.*s expected 0x%2.2x, got 0x%2.2x", 325 (int)(total_length), 326 m_bytes.c_str(), 327 (uint8_t)packet_checksum, 328 (uint8_t)actual_checksum); 329 } 330 // Send the ack or nack if needed 331 if (!success) 332 SendNack(); 333 else 334 SendAck(); 335 } 336 if (success) 337 { 338 if (log) 339 log->Printf ("read packet: %.*s", (int)(total_length), m_bytes.c_str()); 340 } 341 } 342 else 343 { 344 success = false; 345 if (log) 346 log->Printf ("error: invalid checksum in packet: '%s'\n", (int)(total_length), m_bytes.c_str()); 347 } 348 } 349 m_bytes.erase(0, total_length); 350 packet.SetFilePos(0); 351 return success; 352 } 353 } 354 packet.Clear(); 355 return false; 356} 357 358Error 359GDBRemoteCommunication::StartDebugserverProcess (const char *debugserver_url, 360 const char *unix_socket_name, // For handshaking 361 lldb_private::ProcessLaunchInfo &launch_info) 362{ 363 Error error; 364 // If we locate debugserver, keep that located version around 365 static FileSpec g_debugserver_file_spec; 366 367 // This function will fill in the launch information for the debugserver 368 // instance that gets launched. 369 launch_info.Clear(); 370 371 char debugserver_path[PATH_MAX]; 372 FileSpec &debugserver_file_spec = launch_info.GetExecutableFile(); 373 374 // Always check to see if we have an environment override for the path 375 // to the debugserver to use and use it if we do. 376 const char *env_debugserver_path = getenv("LLDB_DEBUGSERVER_PATH"); 377 if (env_debugserver_path) 378 debugserver_file_spec.SetFile (env_debugserver_path, false); 379 else 380 debugserver_file_spec = g_debugserver_file_spec; 381 bool debugserver_exists = debugserver_file_spec.Exists(); 382 if (!debugserver_exists) 383 { 384 // The debugserver binary is in the LLDB.framework/Resources 385 // directory. 386 if (Host::GetLLDBPath (ePathTypeSupportExecutableDir, debugserver_file_spec)) 387 { 388 debugserver_file_spec.GetFilename().SetCString(DEBUGSERVER_BASENAME); 389 debugserver_exists = debugserver_file_spec.Exists(); 390 if (debugserver_exists) 391 { 392 g_debugserver_file_spec = debugserver_file_spec; 393 } 394 else 395 { 396 g_debugserver_file_spec.Clear(); 397 debugserver_file_spec.Clear(); 398 } 399 } 400 } 401 402 if (debugserver_exists) 403 { 404 debugserver_file_spec.GetPath (debugserver_path, sizeof(debugserver_path)); 405 406 Args &debugserver_args = launch_info.GetArguments(); 407 debugserver_args.Clear(); 408 char arg_cstr[PATH_MAX]; 409 410 // Start args with "debugserver /file/path -r --" 411 debugserver_args.AppendArgument(debugserver_path); 412 debugserver_args.AppendArgument(debugserver_url); 413 // use native registers, not the GDB registers 414 debugserver_args.AppendArgument("--native-regs"); 415 // make debugserver run in its own session so signals generated by 416 // special terminal key sequences (^C) don't affect debugserver 417 debugserver_args.AppendArgument("--setsid"); 418 419 if (unix_socket_name && unix_socket_name[0]) 420 { 421 debugserver_args.AppendArgument("--unix-socket"); 422 debugserver_args.AppendArgument(unix_socket_name); 423 } 424 425 const char *env_debugserver_log_file = getenv("LLDB_DEBUGSERVER_LOG_FILE"); 426 if (env_debugserver_log_file) 427 { 428 ::snprintf (arg_cstr, sizeof(arg_cstr), "--log-file=%s", env_debugserver_log_file); 429 debugserver_args.AppendArgument(arg_cstr); 430 } 431 432 const char *env_debugserver_log_flags = getenv("LLDB_DEBUGSERVER_LOG_FLAGS"); 433 if (env_debugserver_log_flags) 434 { 435 ::snprintf (arg_cstr, sizeof(arg_cstr), "--log-flags=%s", env_debugserver_log_flags); 436 debugserver_args.AppendArgument(arg_cstr); 437 } 438 // debugserver_args.AppendArgument("--log-file=/tmp/debugserver.txt"); 439 // debugserver_args.AppendArgument("--log-flags=0x802e0e"); 440 441 // We currently send down all arguments, attach pids, or attach 442 // process names in dedicated GDB server packets, so we don't need 443 // to pass them as arguments. This is currently because of all the 444 // things we need to setup prior to launching: the environment, 445 // current working dir, file actions, etc. 446#if 0 447 // Now append the program arguments 448 if (inferior_argv) 449 { 450 // Terminate the debugserver args so we can now append the inferior args 451 debugserver_args.AppendArgument("--"); 452 453 for (int i = 0; inferior_argv[i] != NULL; ++i) 454 debugserver_args.AppendArgument (inferior_argv[i]); 455 } 456 else if (attach_pid != LLDB_INVALID_PROCESS_ID) 457 { 458 ::snprintf (arg_cstr, sizeof(arg_cstr), "--attach=%u", attach_pid); 459 debugserver_args.AppendArgument (arg_cstr); 460 } 461 else if (attach_name && attach_name[0]) 462 { 463 if (wait_for_launch) 464 debugserver_args.AppendArgument ("--waitfor"); 465 else 466 debugserver_args.AppendArgument ("--attach"); 467 debugserver_args.AppendArgument (attach_name); 468 } 469#endif 470 471 // Close STDIN, STDOUT and STDERR. We might need to redirect them 472 // to "/dev/null" if we run into any problems. 473// launch_info.AppendCloseFileAction (STDIN_FILENO); 474// launch_info.AppendCloseFileAction (STDOUT_FILENO); 475// launch_info.AppendCloseFileAction (STDERR_FILENO); 476 477 error = Host::LaunchProcess(launch_info); 478 } 479 else 480 { 481 error.SetErrorStringWithFormat ("Unable to locate " DEBUGSERVER_BASENAME ".\n"); 482 } 483 return error; 484} 485 486