1 #include "json_utils.h"
2 #include "node_internals.h"
3 #include "node_report.h"
4 #include "util-inl.h"
5 
6 namespace node {
7 namespace report {
8 
9 static constexpr auto null = JSONWriter::Null{};
10 
11 // Utility function to format socket information.
ReportEndpoint(uv_handle_t* h, struct sockaddr* addr, const char* name, JSONWriter* writer)12 static void ReportEndpoint(uv_handle_t* h,
13                            struct sockaddr* addr,
14                            const char* name,
15                            JSONWriter* writer) {
16   if (addr == nullptr) {
17     writer->json_keyvalue(name, null);
18     return;
19   }
20 
21   uv_getnameinfo_t endpoint;
22   char* host = nullptr;
23   char hostbuf[INET6_ADDRSTRLEN];
24   const int family = addr->sa_family;
25   const int port = ntohs(family == AF_INET ?
26                          reinterpret_cast<sockaddr_in*>(addr)->sin_port :
27                          reinterpret_cast<sockaddr_in6*>(addr)->sin6_port);
28 
29   if (uv_getnameinfo(h->loop, &endpoint, nullptr, addr, NI_NUMERICSERV) == 0) {
30     host = endpoint.host;
31     DCHECK_EQ(port, std::stoi(endpoint.service));
32   } else {
33     const void* src = family == AF_INET ?
34                       static_cast<void*>(
35                         &(reinterpret_cast<sockaddr_in*>(addr)->sin_addr)) :
36                       static_cast<void*>(
37                         &(reinterpret_cast<sockaddr_in6*>(addr)->sin6_addr));
38     if (uv_inet_ntop(family, src, hostbuf, sizeof(hostbuf)) == 0) {
39       host = hostbuf;
40     }
41   }
42   writer->json_objectstart(name);
43   if (host != nullptr) {
44     writer->json_keyvalue("host", host);
45   }
46   writer->json_keyvalue("port", port);
47   writer->json_objectend();
48 }
49 
50 // Utility function to format libuv socket information.
ReportEndpoints(uv_handle_t* h, JSONWriter* writer)51 static void ReportEndpoints(uv_handle_t* h, JSONWriter* writer) {
52   struct sockaddr_storage addr_storage;
53   struct sockaddr* addr = reinterpret_cast<sockaddr*>(&addr_storage);
54   uv_any_handle* handle = reinterpret_cast<uv_any_handle*>(h);
55   int addr_size = sizeof(addr_storage);
56   int rc = -1;
57 
58   switch (h->type) {
59     case UV_UDP:
60       rc = uv_udp_getsockname(&handle->udp, addr, &addr_size);
61       break;
62     case UV_TCP:
63       rc = uv_tcp_getsockname(&handle->tcp, addr, &addr_size);
64       break;
65     default:
66       break;
67   }
68   ReportEndpoint(h, rc == 0 ? addr : nullptr,  "localEndpoint", writer);
69 
70   switch (h->type) {
71     case UV_UDP:
72       rc = uv_udp_getpeername(&handle->udp, addr, &addr_size);
73       break;
74     case UV_TCP:
75       rc = uv_tcp_getpeername(&handle->tcp, addr, &addr_size);
76       break;
77     default:
78       break;
79   }
80   ReportEndpoint(h, rc == 0 ? addr : nullptr, "remoteEndpoint", writer);
81 }
82 
83 // Utility function to format libuv pipe information.
ReportPipeEndpoints(uv_handle_t* h, JSONWriter* writer)84 static void ReportPipeEndpoints(uv_handle_t* h, JSONWriter* writer) {
85   uv_any_handle* handle = reinterpret_cast<uv_any_handle*>(h);
86   MaybeStackBuffer<char> buffer;
87   size_t buffer_size = buffer.capacity();
88   int rc = -1;
89 
90   // First call to get required buffer size.
91   rc = uv_pipe_getsockname(&handle->pipe, buffer.out(), &buffer_size);
92   if (rc == UV_ENOBUFS) {
93     buffer.AllocateSufficientStorage(buffer_size);
94     rc = uv_pipe_getsockname(&handle->pipe, buffer.out(), &buffer_size);
95   }
96   if (rc == 0 && buffer_size != 0) {
97     buffer.SetLength(buffer_size);
98     writer->json_keyvalue("localEndpoint", buffer.ToStringView());
99   } else {
100     writer->json_keyvalue("localEndpoint", null);
101   }
102 
103   // First call to get required buffer size.
104   buffer_size = buffer.capacity();
105   rc = uv_pipe_getpeername(&handle->pipe, buffer.out(), &buffer_size);
106   if (rc == UV_ENOBUFS) {
107     buffer.AllocateSufficientStorage(buffer_size);
108     rc = uv_pipe_getpeername(&handle->pipe, buffer.out(), &buffer_size);
109   }
110   if (rc == 0 && buffer_size != 0) {
111     buffer.SetLength(buffer_size);
112     writer->json_keyvalue("remoteEndpoint", buffer.ToStringView());
113   } else {
114     writer->json_keyvalue("remoteEndpoint", null);
115   }
116 }
117 
118 // Utility function to format libuv path information.
ReportPath(uv_handle_t* h, JSONWriter* writer)119 static void ReportPath(uv_handle_t* h, JSONWriter* writer) {
120   MaybeStackBuffer<char> buffer;
121   int rc = -1;
122   size_t size = buffer.capacity();
123   uv_any_handle* handle = reinterpret_cast<uv_any_handle*>(h);
124   // First call to get required buffer size.
125   switch (h->type) {
126     case UV_FS_EVENT:
127       rc = uv_fs_event_getpath(&(handle->fs_event), buffer.out(), &size);
128       break;
129     case UV_FS_POLL:
130       rc = uv_fs_poll_getpath(&(handle->fs_poll), buffer.out(), &size);
131       break;
132     default:
133       break;
134   }
135   if (rc == UV_ENOBUFS) {
136     buffer.AllocateSufficientStorage(size);
137     switch (h->type) {
138       case UV_FS_EVENT:
139         rc = uv_fs_event_getpath(&(handle->fs_event), buffer.out(), &size);
140         break;
141       case UV_FS_POLL:
142         rc = uv_fs_poll_getpath(&(handle->fs_poll), buffer.out(), &size);
143         break;
144       default:
145         break;
146     }
147   }
148 
149   if (rc == 0 && size > 0) {
150     buffer.SetLength(size);
151     writer->json_keyvalue("filename", buffer.ToStringView());
152   } else {
153     writer->json_keyvalue("filename", null);
154   }
155 }
156 
157 // Utility function to walk libuv handles.
WalkHandle(uv_handle_t* h, void* arg)158 void WalkHandle(uv_handle_t* h, void* arg) {
159   const char* type = uv_handle_type_name(h->type);
160   JSONWriter* writer = static_cast<JSONWriter*>(arg);
161   uv_any_handle* handle = reinterpret_cast<uv_any_handle*>(h);
162 
163   writer->json_start();
164   writer->json_keyvalue("type", type);
165   writer->json_keyvalue("is_active", static_cast<bool>(uv_is_active(h)));
166   writer->json_keyvalue("is_referenced", static_cast<bool>(uv_has_ref(h)));
167   writer->json_keyvalue("address",
168                         ValueToHexString(reinterpret_cast<uint64_t>(h)));
169 
170   switch (h->type) {
171     case UV_FS_EVENT:
172     case UV_FS_POLL:
173       ReportPath(h, writer);
174       break;
175     case UV_PROCESS:
176       writer->json_keyvalue("pid", handle->process.pid);
177       break;
178     case UV_TCP:
179     case UV_UDP:
180       ReportEndpoints(h, writer);
181       break;
182     case UV_NAMED_PIPE:
183       ReportPipeEndpoints(h, writer);
184       break;
185     case UV_TIMER: {
186       uint64_t due = handle->timer.timeout;
187       uint64_t now = uv_now(handle->timer.loop);
188       writer->json_keyvalue("repeat", uv_timer_get_repeat(&handle->timer));
189       writer->json_keyvalue("firesInMsFromNow",
190                             static_cast<int64_t>(due - now));
191       writer->json_keyvalue("expired", now >= due);
192       break;
193     }
194     case UV_TTY: {
195       int height, width, rc;
196       rc = uv_tty_get_winsize(&(handle->tty), &width, &height);
197       if (rc == 0) {
198         writer->json_keyvalue("width", width);
199         writer->json_keyvalue("height", height);
200       }
201       break;
202     }
203     case UV_SIGNAL:
204       // SIGWINCH is used by libuv so always appears.
205       // See http://docs.libuv.org/en/v1.x/signal.html
206       writer->json_keyvalue("signum", handle->signal.signum);
207       writer->json_keyvalue("signal", signo_string(handle->signal.signum));
208       break;
209     default:
210       break;
211   }
212 
213   if (h->type == UV_TCP || h->type == UV_UDP
214 #ifndef _WIN32
215       || h->type == UV_NAMED_PIPE
216 #endif
217   ) {
218     // These *must* be 0 or libuv will set the buffer sizes to the non-zero
219     // values they contain.
220     int send_size = 0;
221     int recv_size = 0;
222     uv_send_buffer_size(h, &send_size);
223     uv_recv_buffer_size(h, &recv_size);
224     writer->json_keyvalue("sendBufferSize", send_size);
225     writer->json_keyvalue("recvBufferSize", recv_size);
226   }
227 
228 #ifndef _WIN32
229   if (h->type == UV_TCP || h->type == UV_NAMED_PIPE || h->type == UV_TTY ||
230       h->type == UV_UDP || h->type == UV_POLL) {
231     uv_os_fd_t fd_v;
232     int rc = uv_fileno(h, &fd_v);
233 
234     if (rc == 0) {
235       writer->json_keyvalue("fd", static_cast<int>(fd_v));
236       switch (fd_v) {
237         case STDIN_FILENO:
238           writer->json_keyvalue("stdio", "stdin");
239           break;
240         case STDOUT_FILENO:
241           writer->json_keyvalue("stdio", "stdout");
242           break;
243         case STDERR_FILENO:
244           writer->json_keyvalue("stdio", "stderr");
245           break;
246         default:
247           break;
248       }
249     }
250   }
251 #endif
252 
253   if (h->type == UV_TCP || h->type == UV_NAMED_PIPE || h->type == UV_TTY) {
254     writer->json_keyvalue("writeQueueSize", handle->stream.write_queue_size);
255     writer->json_keyvalue("readable",
256                           static_cast<bool>(uv_is_readable(&handle->stream)));
257     writer->json_keyvalue("writable",
258                           static_cast<bool>(uv_is_writable(&handle->stream)));
259   }
260   if (h->type == UV_UDP) {
261     writer->json_keyvalue(
262         "writeQueueSize",
263         uv_udp_get_send_queue_size(reinterpret_cast<uv_udp_t*>(h)));
264     writer->json_keyvalue(
265         "writeQueueCount",
266         uv_udp_get_send_queue_count(reinterpret_cast<uv_udp_t*>(h)));
267   }
268   writer->json_end();
269 }
270 
271 }  // namespace report
272 }  // namespace node
273