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