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