1 //===-- SelectHelper.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 #if defined(__APPLE__)
10 // Enable this special support for Apple builds where we can have unlimited
11 // select bounds. We tried switching to poll() and kqueue and we were panicing
12 // the kernel, so we have to stick with select for now.
13 #define _DARWIN_UNLIMITED_SELECT
14 #endif
15 
16 #include "lldb/Utility/SelectHelper.h"
17 #include "lldb/Utility/LLDBAssert.h"
18 #include "lldb/Utility/Status.h"
19 #include "lldb/lldb-enumerations.h"
20 #include "lldb/lldb-types.h"
21 
22 #include "llvm/ADT/DenseMap.h"
23 
24 #include <algorithm>
25 #include <chrono>
26 #include <optional>
27 
28 #include <cerrno>
29 #if defined(_WIN32)
30 // Define NOMINMAX to avoid macros that conflict with std::min and std::max
31 #define NOMINMAX
32 #include <winsock2.h>
33 #else
34 #include <sys/time.h>
35 #include <sys/select.h>
36 #endif
37 
38 
SelectHelper()39 SelectHelper::SelectHelper()
40     : m_fd_map(), m_end_time() // Infinite timeout unless
41                                // SelectHelper::SetTimeout() gets called
42 {}
43 
SetTimeout(const std::chrono::microseconds & timeout)44 void SelectHelper::SetTimeout(const std::chrono::microseconds &timeout) {
45   using namespace std::chrono;
46   m_end_time = steady_clock::time_point(steady_clock::now() + timeout);
47 }
48 
FDSetRead(lldb::socket_t fd)49 void SelectHelper::FDSetRead(lldb::socket_t fd) {
50   m_fd_map[fd].read_set = true;
51 }
52 
FDSetWrite(lldb::socket_t fd)53 void SelectHelper::FDSetWrite(lldb::socket_t fd) {
54   m_fd_map[fd].write_set = true;
55 }
56 
FDSetError(lldb::socket_t fd)57 void SelectHelper::FDSetError(lldb::socket_t fd) {
58   m_fd_map[fd].error_set = true;
59 }
60 
FDIsSetRead(lldb::socket_t fd) const61 bool SelectHelper::FDIsSetRead(lldb::socket_t fd) const {
62   auto pos = m_fd_map.find(fd);
63   if (pos != m_fd_map.end())
64     return pos->second.read_is_set;
65   else
66     return false;
67 }
68 
FDIsSetWrite(lldb::socket_t fd) const69 bool SelectHelper::FDIsSetWrite(lldb::socket_t fd) const {
70   auto pos = m_fd_map.find(fd);
71   if (pos != m_fd_map.end())
72     return pos->second.write_is_set;
73   else
74     return false;
75 }
76 
FDIsSetError(lldb::socket_t fd) const77 bool SelectHelper::FDIsSetError(lldb::socket_t fd) const {
78   auto pos = m_fd_map.find(fd);
79   if (pos != m_fd_map.end())
80     return pos->second.error_is_set;
81   else
82     return false;
83 }
84 
updateMaxFd(std::optional<lldb::socket_t> & vold,lldb::socket_t vnew)85 static void updateMaxFd(std::optional<lldb::socket_t> &vold,
86                         lldb::socket_t vnew) {
87   if (!vold)
88     vold = vnew;
89   else
90     vold = std::max(*vold, vnew);
91 }
92 
Select()93 lldb_private::Status SelectHelper::Select() {
94   lldb_private::Status error;
95 #ifdef _WIN32
96   // On windows FD_SETSIZE limits the number of file descriptors, not their
97   // numeric value.
98   lldbassert(m_fd_map.size() <= FD_SETSIZE);
99   if (m_fd_map.size() > FD_SETSIZE)
100     return lldb_private::Status("Too many file descriptors for select()");
101 #endif
102 
103   std::optional<lldb::socket_t> max_read_fd;
104   std::optional<lldb::socket_t> max_write_fd;
105   std::optional<lldb::socket_t> max_error_fd;
106   std::optional<lldb::socket_t> max_fd;
107   for (auto &pair : m_fd_map) {
108     pair.second.PrepareForSelect();
109     const lldb::socket_t fd = pair.first;
110 #if !defined(__APPLE__) && !defined(_WIN32)
111     lldbassert(fd < static_cast<int>(FD_SETSIZE));
112     if (fd >= static_cast<int>(FD_SETSIZE)) {
113       error.SetErrorStringWithFormat("%i is too large for select()", fd);
114       return error;
115     }
116 #endif
117     if (pair.second.read_set)
118       updateMaxFd(max_read_fd, fd);
119     if (pair.second.write_set)
120       updateMaxFd(max_write_fd, fd);
121     if (pair.second.error_set)
122       updateMaxFd(max_error_fd, fd);
123     updateMaxFd(max_fd, fd);
124   }
125 
126   if (!max_fd) {
127     error.SetErrorString("no valid file descriptors");
128     return error;
129   }
130 
131   const unsigned nfds = static_cast<unsigned>(*max_fd) + 1;
132   fd_set *read_fdset_ptr = nullptr;
133   fd_set *write_fdset_ptr = nullptr;
134   fd_set *error_fdset_ptr = nullptr;
135 // Initialize and zero out the fdsets
136 #if defined(__APPLE__)
137   llvm::SmallVector<fd_set, 1> read_fdset;
138   llvm::SmallVector<fd_set, 1> write_fdset;
139   llvm::SmallVector<fd_set, 1> error_fdset;
140 
141   if (max_read_fd.has_value()) {
142     read_fdset.resize((nfds / FD_SETSIZE) + 1);
143     read_fdset_ptr = read_fdset.data();
144   }
145   if (max_write_fd.has_value()) {
146     write_fdset.resize((nfds / FD_SETSIZE) + 1);
147     write_fdset_ptr = write_fdset.data();
148   }
149   if (max_error_fd.has_value()) {
150     error_fdset.resize((nfds / FD_SETSIZE) + 1);
151     error_fdset_ptr = error_fdset.data();
152   }
153   for (auto &fd_set : read_fdset)
154     FD_ZERO(&fd_set);
155   for (auto &fd_set : write_fdset)
156     FD_ZERO(&fd_set);
157   for (auto &fd_set : error_fdset)
158     FD_ZERO(&fd_set);
159 #else
160   fd_set read_fdset;
161   fd_set write_fdset;
162   fd_set error_fdset;
163 
164   if (max_read_fd) {
165     FD_ZERO(&read_fdset);
166     read_fdset_ptr = &read_fdset;
167   }
168   if (max_write_fd) {
169     FD_ZERO(&write_fdset);
170     write_fdset_ptr = &write_fdset;
171   }
172   if (max_error_fd) {
173     FD_ZERO(&error_fdset);
174     error_fdset_ptr = &error_fdset;
175   }
176 #endif
177   // Set the FD bits in the fdsets for read/write/error
178   for (auto &pair : m_fd_map) {
179     const lldb::socket_t fd = pair.first;
180 
181     if (pair.second.read_set)
182       FD_SET(fd, read_fdset_ptr);
183 
184     if (pair.second.write_set)
185       FD_SET(fd, write_fdset_ptr);
186 
187     if (pair.second.error_set)
188       FD_SET(fd, error_fdset_ptr);
189   }
190 
191   // Setup our timeout time value if needed
192   struct timeval *tv_ptr = nullptr;
193   struct timeval tv = {0, 0};
194 
195   while (true) {
196     using namespace std::chrono;
197     // Setup out relative timeout based on the end time if we have one
198     if (m_end_time) {
199       tv_ptr = &tv;
200       const auto remaining_dur =
201           duration_cast<microseconds>(*m_end_time - steady_clock::now());
202       if (remaining_dur.count() > 0) {
203         // Wait for a specific amount of time
204         const auto dur_secs = duration_cast<seconds>(remaining_dur);
205         const auto dur_usecs = remaining_dur % seconds(1);
206         tv.tv_sec = dur_secs.count();
207         tv.tv_usec = dur_usecs.count();
208       } else {
209         // Just poll once with no timeout
210         tv.tv_sec = 0;
211         tv.tv_usec = 0;
212       }
213     }
214     const int num_set_fds = ::select(nfds, read_fdset_ptr, write_fdset_ptr,
215                                      error_fdset_ptr, tv_ptr);
216     if (num_set_fds < 0) {
217       // We got an error
218       error.SetErrorToErrno();
219       if (error.GetError() == EINTR) {
220         error.Clear();
221         continue; // Keep calling select if we get EINTR
222       } else
223         return error;
224     } else if (num_set_fds == 0) {
225       // Timeout
226       error.SetError(ETIMEDOUT, lldb::eErrorTypePOSIX);
227       error.SetErrorString("timed out");
228       return error;
229     } else {
230       // One or more descriptors were set, update the FDInfo::select_is_set
231       // mask so users can ask the SelectHelper class so clients can call one
232       // of:
233 
234       for (auto &pair : m_fd_map) {
235         const int fd = pair.first;
236 
237         if (pair.second.read_set) {
238           if (FD_ISSET(fd, read_fdset_ptr))
239             pair.second.read_is_set = true;
240         }
241         if (pair.second.write_set) {
242           if (FD_ISSET(fd, write_fdset_ptr))
243             pair.second.write_is_set = true;
244         }
245         if (pair.second.error_set) {
246           if (FD_ISSET(fd, error_fdset_ptr))
247             pair.second.error_is_set = true;
248         }
249       }
250       break;
251     }
252   }
253   return error;
254 }
255