blob: abbf6d529d0ef73df95444e134c8ec7eaff30db7 [file] [log] [blame]
#include "json_utils.h"
#include "node_internals.h"
#include "node_report.h"
#include "util-inl.h"
namespace report {
using node::JSONWriter;
using node::MallocedBuffer;
static constexpr auto null = JSONWriter::Null{};
// Utility function to format socket information.
static void ReportEndpoint(uv_handle_t* h,
struct sockaddr* addr,
const char* name,
JSONWriter* writer) {
if (addr == nullptr) {
writer->json_keyvalue(name, null);
return;
}
uv_getnameinfo_t endpoint;
char* host = nullptr;
char hostbuf[INET6_ADDRSTRLEN];
const int family = addr->sa_family;
const int port = ntohs(family == AF_INET ?
reinterpret_cast<sockaddr_in*>(addr)->sin_port :
reinterpret_cast<sockaddr_in6*>(addr)->sin6_port);
if (uv_getnameinfo(h->loop, &endpoint, nullptr, addr, NI_NUMERICSERV) == 0) {
host = endpoint.host;
DCHECK_EQ(port, std::stoi(endpoint.service));
} else {
const void* src = family == AF_INET ?
static_cast<void*>(
&(reinterpret_cast<sockaddr_in*>(addr)->sin_addr)) :
static_cast<void*>(
&(reinterpret_cast<sockaddr_in6*>(addr)->sin6_addr));
if (uv_inet_ntop(family, src, hostbuf, sizeof(hostbuf)) == 0) {
host = hostbuf;
}
}
writer->json_objectstart(name);
if (host != nullptr) {
writer->json_keyvalue("host", host);
}
writer->json_keyvalue("port", port);
writer->json_objectend();
}
// Utility function to format libuv socket information.
static void ReportEndpoints(uv_handle_t* h, JSONWriter* writer) {
struct sockaddr_storage addr_storage;
struct sockaddr* addr = reinterpret_cast<sockaddr*>(&addr_storage);
uv_any_handle* handle = reinterpret_cast<uv_any_handle*>(h);
int addr_size = sizeof(addr_storage);
int rc = -1;
switch (h->type) {
case UV_UDP:
rc = uv_udp_getsockname(&handle->udp, addr, &addr_size);
break;
case UV_TCP:
rc = uv_tcp_getsockname(&handle->tcp, addr, &addr_size);
break;
default:
break;
}
ReportEndpoint(h, rc == 0 ? addr : nullptr, "localEndpoint", writer);
switch (h->type) {
case UV_UDP:
rc = uv_udp_getpeername(&handle->udp, addr, &addr_size);
break;
case UV_TCP:
rc = uv_tcp_getpeername(&handle->tcp, addr, &addr_size);
break;
default:
break;
}
ReportEndpoint(h, rc == 0 ? addr : nullptr, "remoteEndpoint", writer);
}
// Utility function to format libuv path information.
static void ReportPath(uv_handle_t* h, JSONWriter* writer) {
MallocedBuffer<char> buffer(0);
int rc = -1;
size_t size = 0;
uv_any_handle* handle = reinterpret_cast<uv_any_handle*>(h);
bool wrote_filename = false;
// First call to get required buffer size.
switch (h->type) {
case UV_FS_EVENT:
rc = uv_fs_event_getpath(&(handle->fs_event), buffer.data, &size);
break;
case UV_FS_POLL:
rc = uv_fs_poll_getpath(&(handle->fs_poll), buffer.data, &size);
break;
default:
break;
}
if (rc == UV_ENOBUFS) {
buffer = MallocedBuffer<char>(size + 1);
switch (h->type) {
case UV_FS_EVENT:
rc = uv_fs_event_getpath(&(handle->fs_event), buffer.data, &size);
break;
case UV_FS_POLL:
rc = uv_fs_poll_getpath(&(handle->fs_poll), buffer.data, &size);
break;
default:
break;
}
if (rc == 0) {
// buffer is not null terminated.
buffer.data[size] = '\0';
writer->json_keyvalue("filename", buffer.data);
wrote_filename = true;
}
}
if (!wrote_filename) writer->json_keyvalue("filename", null);
}
// Utility function to walk libuv handles.
void WalkHandle(uv_handle_t* h, void* arg) {
const char* type = uv_handle_type_name(h->type);
JSONWriter* writer = static_cast<JSONWriter*>(arg);
uv_any_handle* handle = reinterpret_cast<uv_any_handle*>(h);
writer->json_start();
writer->json_keyvalue("type", type);
writer->json_keyvalue("is_active", static_cast<bool>(uv_is_active(h)));
writer->json_keyvalue("is_referenced", static_cast<bool>(uv_has_ref(h)));
writer->json_keyvalue("address",
ValueToHexString(reinterpret_cast<uint64_t>(h)));
switch (h->type) {
case UV_FS_EVENT:
case UV_FS_POLL:
ReportPath(h, writer);
break;
case UV_PROCESS:
writer->json_keyvalue("pid", handle->process.pid);
break;
case UV_TCP:
case UV_UDP:
ReportEndpoints(h, writer);
break;
case UV_TIMER: {
uint64_t due = handle->timer.timeout;
uint64_t now = uv_now(handle->timer.loop);
writer->json_keyvalue("repeat", uv_timer_get_repeat(&handle->timer));
writer->json_keyvalue("firesInMsFromNow",
static_cast<int64_t>(due - now));
writer->json_keyvalue("expired", now >= due);
break;
}
case UV_TTY: {
int height, width, rc;
rc = uv_tty_get_winsize(&(handle->tty), &width, &height);
if (rc == 0) {
writer->json_keyvalue("width", width);
writer->json_keyvalue("height", height);
}
break;
}
case UV_SIGNAL:
// SIGWINCH is used by libuv so always appears.
// See http://docs.libuv.org/en/v1.x/signal.html
writer->json_keyvalue("signum", handle->signal.signum);
writer->json_keyvalue("signal",
node::signo_string(handle->signal.signum));
break;
default:
break;
}
if (h->type == UV_TCP || h->type == UV_UDP
#ifndef _WIN32
|| h->type == UV_NAMED_PIPE
#endif
) {
// These *must* be 0 or libuv will set the buffer sizes to the non-zero
// values they contain.
int send_size = 0;
int recv_size = 0;
uv_send_buffer_size(h, &send_size);
uv_recv_buffer_size(h, &recv_size);
writer->json_keyvalue("sendBufferSize", send_size);
writer->json_keyvalue("recvBufferSize", recv_size);
}
#ifndef _WIN32
if (h->type == UV_TCP || h->type == UV_NAMED_PIPE || h->type == UV_TTY ||
h->type == UV_UDP || h->type == UV_POLL) {
uv_os_fd_t fd_v;
int rc = uv_fileno(h, &fd_v);
if (rc == 0) {
writer->json_keyvalue("fd", static_cast<int>(fd_v));
switch (fd_v) {
case STDIN_FILENO:
writer->json_keyvalue("stdio", "stdin");
break;
case STDOUT_FILENO:
writer->json_keyvalue("stdio", "stdout");
break;
case STDERR_FILENO:
writer->json_keyvalue("stdio", "stderr");
break;
default:
break;
}
}
}
#endif
if (h->type == UV_TCP || h->type == UV_NAMED_PIPE || h->type == UV_TTY) {
writer->json_keyvalue("writeQueueSize", handle->stream.write_queue_size);
writer->json_keyvalue("readable",
static_cast<bool>(uv_is_readable(&handle->stream)));
writer->json_keyvalue("writable",
static_cast<bool>(uv_is_writable(&handle->stream)));
}
writer->json_end();
}
} // namespace report