1 //===-- Communication.cpp -------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "lldb/Core/Communication.h"
10 
11 #include "lldb/Utility/Connection.h"
12 #include "lldb/Utility/LLDBLog.h"
13 #include "lldb/Utility/Log.h"
14 #include "lldb/Utility/Status.h"
15 
16 #include "llvm/Support/Compiler.h"
17 
18 #include <algorithm>
19 #include <cstring>
20 #include <memory>
21 
22 #include <cerrno>
23 #include <cinttypes>
24 #include <cstdio>
25 
26 using namespace lldb;
27 using namespace lldb_private;
28 
Communication()29 Communication::Communication()
30     : m_connection_sp(), m_write_mutex(), m_close_on_eof(true) {
31 }
32 
~Communication()33 Communication::~Communication() {
34   Clear();
35 }
36 
Clear()37 void Communication::Clear() {
38   Disconnect(nullptr);
39 }
40 
Connect(const char * url,Status * error_ptr)41 ConnectionStatus Communication::Connect(const char *url, Status *error_ptr) {
42   Clear();
43 
44   LLDB_LOG(GetLog(LLDBLog::Communication),
45            "{0} Communication::Connect (url = {1})", this, url);
46 
47   lldb::ConnectionSP connection_sp(m_connection_sp);
48   if (connection_sp)
49     return connection_sp->Connect(url, error_ptr);
50   if (error_ptr)
51     error_ptr->SetErrorString("Invalid connection.");
52   return eConnectionStatusNoConnection;
53 }
54 
Disconnect(Status * error_ptr)55 ConnectionStatus Communication::Disconnect(Status *error_ptr) {
56   LLDB_LOG(GetLog(LLDBLog::Communication), "{0} Communication::Disconnect ()",
57            this);
58 
59   lldb::ConnectionSP connection_sp(m_connection_sp);
60   if (connection_sp) {
61     ConnectionStatus status = connection_sp->Disconnect(error_ptr);
62     // We currently don't protect connection_sp with any mutex for multi-
63     // threaded environments. So lets not nuke our connection class without
64     // putting some multi-threaded protections in. We also probably don't want
65     // to pay for the overhead it might cause if every time we access the
66     // connection we have to take a lock.
67     //
68     // This unique pointer will cleanup after itself when this object goes
69     // away, so there is no need to currently have it destroy itself
70     // immediately upon disconnect.
71     // connection_sp.reset();
72     return status;
73   }
74   return eConnectionStatusNoConnection;
75 }
76 
IsConnected() const77 bool Communication::IsConnected() const {
78   lldb::ConnectionSP connection_sp(m_connection_sp);
79   return (connection_sp ? connection_sp->IsConnected() : false);
80 }
81 
HasConnection() const82 bool Communication::HasConnection() const {
83   return m_connection_sp.get() != nullptr;
84 }
85 
Read(void * dst,size_t dst_len,const Timeout<std::micro> & timeout,ConnectionStatus & status,Status * error_ptr)86 size_t Communication::Read(void *dst, size_t dst_len,
87                            const Timeout<std::micro> &timeout,
88                            ConnectionStatus &status, Status *error_ptr) {
89   Log *log = GetLog(LLDBLog::Communication);
90   LLDB_LOG(
91       log,
92       "this = {0}, dst = {1}, dst_len = {2}, timeout = {3}, connection = {4}",
93       this, dst, dst_len, timeout, m_connection_sp.get());
94 
95   return ReadFromConnection(dst, dst_len, timeout, status, error_ptr);
96 }
97 
Write(const void * src,size_t src_len,ConnectionStatus & status,Status * error_ptr)98 size_t Communication::Write(const void *src, size_t src_len,
99                             ConnectionStatus &status, Status *error_ptr) {
100   lldb::ConnectionSP connection_sp(m_connection_sp);
101 
102   std::lock_guard<std::mutex> guard(m_write_mutex);
103   LLDB_LOG(GetLog(LLDBLog::Communication),
104            "{0} Communication::Write (src = {1}, src_len = {2}"
105            ") connection = {3}",
106            this, src, (uint64_t)src_len, connection_sp.get());
107 
108   if (connection_sp)
109     return connection_sp->Write(src, src_len, status, error_ptr);
110 
111   if (error_ptr)
112     error_ptr->SetErrorString("Invalid connection.");
113   status = eConnectionStatusNoConnection;
114   return 0;
115 }
116 
WriteAll(const void * src,size_t src_len,ConnectionStatus & status,Status * error_ptr)117 size_t Communication::WriteAll(const void *src, size_t src_len,
118                                ConnectionStatus &status, Status *error_ptr) {
119   size_t total_written = 0;
120   do
121     total_written += Write(static_cast<const char *>(src) + total_written,
122                            src_len - total_written, status, error_ptr);
123   while (status == eConnectionStatusSuccess && total_written < src_len);
124   return total_written;
125 }
126 
ReadFromConnection(void * dst,size_t dst_len,const Timeout<std::micro> & timeout,ConnectionStatus & status,Status * error_ptr)127 size_t Communication::ReadFromConnection(void *dst, size_t dst_len,
128                                          const Timeout<std::micro> &timeout,
129                                          ConnectionStatus &status,
130                                          Status *error_ptr) {
131   lldb::ConnectionSP connection_sp(m_connection_sp);
132   if (connection_sp)
133     return connection_sp->Read(dst, dst_len, timeout, status, error_ptr);
134 
135   if (error_ptr)
136     error_ptr->SetErrorString("Invalid connection.");
137   status = eConnectionStatusNoConnection;
138   return 0;
139 }
140 
SetConnection(std::unique_ptr<Connection> connection)141 void Communication::SetConnection(std::unique_ptr<Connection> connection) {
142   Disconnect(nullptr);
143   m_connection_sp = std::move(connection);
144 }
145 
146 std::string
ConnectionStatusAsString(lldb::ConnectionStatus status)147 Communication::ConnectionStatusAsString(lldb::ConnectionStatus status) {
148   switch (status) {
149   case eConnectionStatusSuccess:
150     return "success";
151   case eConnectionStatusError:
152     return "error";
153   case eConnectionStatusTimedOut:
154     return "timed out";
155   case eConnectionStatusNoConnection:
156     return "no connection";
157   case eConnectionStatusLostConnection:
158     return "lost connection";
159   case eConnectionStatusEndOfFile:
160     return "end of file";
161   case eConnectionStatusInterrupted:
162     return "interrupted";
163   }
164 
165   return "@" + std::to_string(status);
166 }
167