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("<%4" PRIu64 "> read packet: %.*s", (uint64_t)total_length, (int)(total_length), m_bytes.c_str()); } m_history.AddPacket (m_bytes.c_str(), total_length, History::ePacketTypeRecv, total_length); // Clear packet_str in case there is some existing data in it. packet_str.clear(); // Copy the packet from m_bytes to packet_str expanding the // run-length encoding in the process. // Reserve enough byte for the most common case (no RLE used) packet_str.reserve(m_bytes.length()); for (std::string::const_iterator c = m_bytes.begin() + content_start; c != m_bytes.begin() + content_start + content_length; ++c) { if (*c == '*') { // '*' indicates RLE. Next character will give us the // repeat count and previous character is what is to be // repeated. char char_to_repeat = packet_str.back(); // Number of time the previous character is repeated int repeat_count = *++c + 3 - ' '; // We have the char_to_repeat and repeat_count. Now push // it in the packet. for (int i = 0; i < repeat_count; ++i) packet_str.push_back(char_to_repeat); } else { packet_str.push_back(*c); } } 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; }
StateType GDBRemoteClientBase::SendContinuePacketAndWaitForResponse( ContinueDelegate &delegate, const UnixSignals &signals, llvm::StringRef payload, StringExtractorGDBRemote &response) { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); response.Clear(); { std::lock_guard<std::mutex> lock(m_mutex); m_continue_packet = payload; m_should_stop = false; } ContinueLock cont_lock(*this); if (!cont_lock) return eStateInvalid; OnRunPacketSent(true); for (;;) { PacketResult read_result = ReadPacket(response, kInterruptTimeout, false); switch (read_result) { case PacketResult::ErrorReplyTimeout: { std::lock_guard<std::mutex> lock(m_mutex); if (m_async_count == 0) continue; if (steady_clock::now() >= m_interrupt_time + kInterruptTimeout) return eStateInvalid; } case PacketResult::Success: break; default: if (log) log->Printf("GDBRemoteClientBase::%s () ReadPacket(...) => false", __FUNCTION__); return eStateInvalid; } if (response.Empty()) return eStateInvalid; const char stop_type = response.GetChar(); if (log) log->Printf("GDBRemoteClientBase::%s () got packet: %s", __FUNCTION__, response.GetStringRef().c_str()); switch (stop_type) { case 'W': case 'X': return eStateExited; case 'E': // ERROR return eStateInvalid; default: if (log) log->Printf("GDBRemoteClientBase::%s () unrecognized async packet", __FUNCTION__); return eStateInvalid; case 'O': { std::string inferior_stdout; response.GetHexByteString(inferior_stdout); delegate.HandleAsyncStdout(inferior_stdout); break; } case 'A': delegate.HandleAsyncMisc( llvm::StringRef(response.GetStringRef()).substr(1)); break; case 'J': delegate.HandleAsyncStructuredDataPacket(response.GetStringRef()); break; case 'T': case 'S': // Do this with the continue lock held. const bool should_stop = ShouldStop(signals, response); response.SetFilePos(0); // The packet we should resume with. In the future // we should check our thread list and "do the right thing" // for new threads that show up while we stop and run async // packets. Setting the packet to 'c' to continue all threads // is the right thing to do 99.99% of the time because if a // thread was single stepping, and we sent an interrupt, we // will notice above that we didn't stop due to an interrupt // but stopped due to stepping and we would _not_ continue. // This packet may get modified by the async actions (e.g. to send a // signal). m_continue_packet = 'c'; cont_lock.unlock(); delegate.HandleStopReply(); if (should_stop) return eStateStopped; switch (cont_lock.lock()) { case ContinueLock::LockResult::Success: break; case ContinueLock::LockResult::Failed: return eStateInvalid; case ContinueLock::LockResult::Cancelled: return eStateStopped; } OnRunPacketSent(false); break; } } }