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