15ffd83dbSDimitry Andric //===-- Communication.cpp -------------------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
90b57cec5SDimitry Andric #include "lldb/Core/Communication.h"
100b57cec5SDimitry Andric 
110b57cec5SDimitry Andric #include "lldb/Utility/Connection.h"
1281ad6265SDimitry Andric #include "lldb/Utility/LLDBLog.h"
130b57cec5SDimitry Andric #include "lldb/Utility/Log.h"
140b57cec5SDimitry Andric #include "lldb/Utility/Status.h"
150b57cec5SDimitry Andric 
160b57cec5SDimitry Andric #include "llvm/Support/Compiler.h"
170b57cec5SDimitry Andric 
180b57cec5SDimitry Andric #include <algorithm>
190b57cec5SDimitry Andric #include <cstring>
200b57cec5SDimitry Andric #include <memory>
210b57cec5SDimitry Andric 
22fe6060f1SDimitry Andric #include <cerrno>
23fe6060f1SDimitry Andric #include <cinttypes>
24fe6060f1SDimitry Andric #include <cstdio>
250b57cec5SDimitry Andric 
260b57cec5SDimitry Andric using namespace lldb;
270b57cec5SDimitry Andric using namespace lldb_private;
280b57cec5SDimitry Andric 
Communication()29bdd1243dSDimitry Andric Communication::Communication()
30bdd1243dSDimitry Andric     : m_connection_sp(), m_write_mutex(), m_close_on_eof(true) {
310b57cec5SDimitry Andric }
320b57cec5SDimitry Andric 
~Communication()330b57cec5SDimitry Andric Communication::~Communication() {
340b57cec5SDimitry Andric   Clear();
350b57cec5SDimitry Andric }
360b57cec5SDimitry Andric 
Clear()370b57cec5SDimitry Andric void Communication::Clear() {
385ffd83dbSDimitry Andric   Disconnect(nullptr);
390b57cec5SDimitry Andric }
400b57cec5SDimitry Andric 
Connect(const char * url,Status * error_ptr)410b57cec5SDimitry Andric ConnectionStatus Communication::Connect(const char *url, Status *error_ptr) {
420b57cec5SDimitry Andric   Clear();
430b57cec5SDimitry Andric 
4481ad6265SDimitry Andric   LLDB_LOG(GetLog(LLDBLog::Communication),
459dba64beSDimitry Andric            "{0} Communication::Connect (url = {1})", this, url);
460b57cec5SDimitry Andric 
470b57cec5SDimitry Andric   lldb::ConnectionSP connection_sp(m_connection_sp);
480b57cec5SDimitry Andric   if (connection_sp)
490b57cec5SDimitry Andric     return connection_sp->Connect(url, error_ptr);
500b57cec5SDimitry Andric   if (error_ptr)
510b57cec5SDimitry Andric     error_ptr->SetErrorString("Invalid connection.");
520b57cec5SDimitry Andric   return eConnectionStatusNoConnection;
530b57cec5SDimitry Andric }
540b57cec5SDimitry Andric 
Disconnect(Status * error_ptr)550b57cec5SDimitry Andric ConnectionStatus Communication::Disconnect(Status *error_ptr) {
5681ad6265SDimitry Andric   LLDB_LOG(GetLog(LLDBLog::Communication), "{0} Communication::Disconnect ()",
5781ad6265SDimitry Andric            this);
580b57cec5SDimitry Andric 
590b57cec5SDimitry Andric   lldb::ConnectionSP connection_sp(m_connection_sp);
600b57cec5SDimitry Andric   if (connection_sp) {
610b57cec5SDimitry Andric     ConnectionStatus status = connection_sp->Disconnect(error_ptr);
620b57cec5SDimitry Andric     // We currently don't protect connection_sp with any mutex for multi-
630b57cec5SDimitry Andric     // threaded environments. So lets not nuke our connection class without
640b57cec5SDimitry Andric     // putting some multi-threaded protections in. We also probably don't want
650b57cec5SDimitry Andric     // to pay for the overhead it might cause if every time we access the
660b57cec5SDimitry Andric     // connection we have to take a lock.
670b57cec5SDimitry Andric     //
680b57cec5SDimitry Andric     // This unique pointer will cleanup after itself when this object goes
690b57cec5SDimitry Andric     // away, so there is no need to currently have it destroy itself
700b57cec5SDimitry Andric     // immediately upon disconnect.
710b57cec5SDimitry Andric     // connection_sp.reset();
720b57cec5SDimitry Andric     return status;
730b57cec5SDimitry Andric   }
740b57cec5SDimitry Andric   return eConnectionStatusNoConnection;
750b57cec5SDimitry Andric }
760b57cec5SDimitry Andric 
IsConnected() const770b57cec5SDimitry Andric bool Communication::IsConnected() const {
780b57cec5SDimitry Andric   lldb::ConnectionSP connection_sp(m_connection_sp);
790b57cec5SDimitry Andric   return (connection_sp ? connection_sp->IsConnected() : false);
800b57cec5SDimitry Andric }
810b57cec5SDimitry Andric 
HasConnection() const820b57cec5SDimitry Andric bool Communication::HasConnection() const {
830b57cec5SDimitry Andric   return m_connection_sp.get() != nullptr;
840b57cec5SDimitry Andric }
850b57cec5SDimitry Andric 
Read(void * dst,size_t dst_len,const Timeout<std::micro> & timeout,ConnectionStatus & status,Status * error_ptr)860b57cec5SDimitry Andric size_t Communication::Read(void *dst, size_t dst_len,
870b57cec5SDimitry Andric                            const Timeout<std::micro> &timeout,
880b57cec5SDimitry Andric                            ConnectionStatus &status, Status *error_ptr) {
8981ad6265SDimitry Andric   Log *log = GetLog(LLDBLog::Communication);
900b57cec5SDimitry Andric   LLDB_LOG(
910b57cec5SDimitry Andric       log,
920b57cec5SDimitry Andric       "this = {0}, dst = {1}, dst_len = {2}, timeout = {3}, connection = {4}",
930b57cec5SDimitry Andric       this, dst, dst_len, timeout, m_connection_sp.get());
940b57cec5SDimitry Andric 
950b57cec5SDimitry Andric   return ReadFromConnection(dst, dst_len, timeout, status, error_ptr);
960b57cec5SDimitry Andric }
970b57cec5SDimitry Andric 
Write(const void * src,size_t src_len,ConnectionStatus & status,Status * error_ptr)980b57cec5SDimitry Andric size_t Communication::Write(const void *src, size_t src_len,
990b57cec5SDimitry Andric                             ConnectionStatus &status, Status *error_ptr) {
1000b57cec5SDimitry Andric   lldb::ConnectionSP connection_sp(m_connection_sp);
1010b57cec5SDimitry Andric 
1020b57cec5SDimitry Andric   std::lock_guard<std::mutex> guard(m_write_mutex);
10381ad6265SDimitry Andric   LLDB_LOG(GetLog(LLDBLog::Communication),
104349cc55cSDimitry Andric            "{0} Communication::Write (src = {1}, src_len = {2}"
105349cc55cSDimitry Andric            ") connection = {3}",
1060b57cec5SDimitry Andric            this, src, (uint64_t)src_len, connection_sp.get());
1070b57cec5SDimitry Andric 
1080b57cec5SDimitry Andric   if (connection_sp)
1090b57cec5SDimitry Andric     return connection_sp->Write(src, src_len, status, error_ptr);
1100b57cec5SDimitry Andric 
1110b57cec5SDimitry Andric   if (error_ptr)
1120b57cec5SDimitry Andric     error_ptr->SetErrorString("Invalid connection.");
1130b57cec5SDimitry Andric   status = eConnectionStatusNoConnection;
1140b57cec5SDimitry Andric   return 0;
1150b57cec5SDimitry Andric }
1160b57cec5SDimitry Andric 
WriteAll(const void * src,size_t src_len,ConnectionStatus & status,Status * error_ptr)117349cc55cSDimitry Andric size_t Communication::WriteAll(const void *src, size_t src_len,
118349cc55cSDimitry Andric                                ConnectionStatus &status, Status *error_ptr) {
119349cc55cSDimitry Andric   size_t total_written = 0;
120349cc55cSDimitry Andric   do
121349cc55cSDimitry Andric     total_written += Write(static_cast<const char *>(src) + total_written,
122349cc55cSDimitry Andric                            src_len - total_written, status, error_ptr);
123349cc55cSDimitry Andric   while (status == eConnectionStatusSuccess && total_written < src_len);
124349cc55cSDimitry Andric   return total_written;
125349cc55cSDimitry Andric }
126349cc55cSDimitry Andric 
ReadFromConnection(void * dst,size_t dst_len,const Timeout<std::micro> & timeout,ConnectionStatus & status,Status * error_ptr)1270b57cec5SDimitry Andric size_t Communication::ReadFromConnection(void *dst, size_t dst_len,
1280b57cec5SDimitry Andric                                          const Timeout<std::micro> &timeout,
1290b57cec5SDimitry Andric                                          ConnectionStatus &status,
1300b57cec5SDimitry Andric                                          Status *error_ptr) {
1310b57cec5SDimitry Andric   lldb::ConnectionSP connection_sp(m_connection_sp);
1320b57cec5SDimitry Andric   if (connection_sp)
1330b57cec5SDimitry Andric     return connection_sp->Read(dst, dst_len, timeout, status, error_ptr);
1340b57cec5SDimitry Andric 
1350b57cec5SDimitry Andric   if (error_ptr)
1360b57cec5SDimitry Andric     error_ptr->SetErrorString("Invalid connection.");
1370b57cec5SDimitry Andric   status = eConnectionStatusNoConnection;
1380b57cec5SDimitry Andric   return 0;
1390b57cec5SDimitry Andric }
1400b57cec5SDimitry Andric 
SetConnection(std::unique_ptr<Connection> connection)1415ffd83dbSDimitry Andric void Communication::SetConnection(std::unique_ptr<Connection> connection) {
1420b57cec5SDimitry Andric   Disconnect(nullptr);
1435ffd83dbSDimitry Andric   m_connection_sp = std::move(connection);
1440b57cec5SDimitry Andric }
1450b57cec5SDimitry Andric 
146e8d8bef9SDimitry Andric std::string
ConnectionStatusAsString(lldb::ConnectionStatus status)147e8d8bef9SDimitry Andric Communication::ConnectionStatusAsString(lldb::ConnectionStatus status) {
1480b57cec5SDimitry Andric   switch (status) {
1490b57cec5SDimitry Andric   case eConnectionStatusSuccess:
1500b57cec5SDimitry Andric     return "success";
1510b57cec5SDimitry Andric   case eConnectionStatusError:
1520b57cec5SDimitry Andric     return "error";
1530b57cec5SDimitry Andric   case eConnectionStatusTimedOut:
1540b57cec5SDimitry Andric     return "timed out";
1550b57cec5SDimitry Andric   case eConnectionStatusNoConnection:
1560b57cec5SDimitry Andric     return "no connection";
1570b57cec5SDimitry Andric   case eConnectionStatusLostConnection:
1580b57cec5SDimitry Andric     return "lost connection";
1590b57cec5SDimitry Andric   case eConnectionStatusEndOfFile:
1600b57cec5SDimitry Andric     return "end of file";
1610b57cec5SDimitry Andric   case eConnectionStatusInterrupted:
1620b57cec5SDimitry Andric     return "interrupted";
1630b57cec5SDimitry Andric   }
1640b57cec5SDimitry Andric 
165e8d8bef9SDimitry Andric   return "@" + std::to_string(status);
1660b57cec5SDimitry Andric }
167