#include "node_internals.h" #include "node_report.h" namespace report { using node::MallocedBuffer; // Utility function to format libuv socket information. void ReportEndpoints(uv_handle_t* h, std::ostringstream& out) { struct sockaddr_storage addr_storage; struct sockaddr* addr = reinterpret_cast(&addr_storage); uv_any_handle* handle = reinterpret_cast(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; } if (rc == 0) { // uv_getnameinfo will format host and port and handle IPv4/IPv6. uv_getnameinfo_t local; rc = uv_getnameinfo(h->loop, &local, nullptr, addr, NI_NUMERICSERV); if (rc == 0) out << local.host << ":" << local.service; if (h->type == UV_TCP) { // Get the remote end of the connection. rc = uv_tcp_getpeername(&(handle->tcp), addr, &addr_size); if (rc == 0) { uv_getnameinfo_t remote; rc = uv_getnameinfo(h->loop, &remote, nullptr, addr, NI_NUMERICSERV); if (rc == 0) out << " connected to " << remote.host << ":" << remote.service; } else if (rc == UV_ENOTCONN) { out << " (not connected)"; } } } } // Utility function to format libuv path information. void ReportPath(uv_handle_t* h, std::ostringstream& out) { MallocedBuffer buffer(0); int rc = -1; size_t size = 0; uv_any_handle* handle = reinterpret_cast(h); // 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(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 == 0) { // buffer is not null terminated. std::string name(buffer.data, size); out << "filename: " << name; } } } // Utility function to walk libuv handles. void WalkHandle(uv_handle_t* h, void* arg) { const char* type = uv_handle_type_name(h->type); std::ostringstream data; JSONWriter* writer = static_cast(arg); uv_any_handle* handle = reinterpret_cast(h); switch (h->type) { case UV_FS_EVENT: case UV_FS_POLL: ReportPath(h, data); break; case UV_PROCESS: data << "pid: " << handle->process.pid; break; case UV_TCP: case UV_UDP: ReportEndpoints(h, data); break; case UV_TIMER: { uint64_t due = handle->timer.timeout; uint64_t now = uv_now(handle->timer.loop); data << "repeat: " << uv_timer_get_repeat(&(handle->timer)); if (due > now) { data << ", timeout in: " << (due - now) << " ms"; } else { data << ", timeout expired: " << (now - due) << " ms ago"; } break; } case UV_TTY: { int height, width, rc; rc = uv_tty_get_winsize(&(handle->tty), &width, &height); if (rc == 0) { data << "width: " << width << ", height: " << height; } break; } case UV_SIGNAL: // SIGWINCH is used by libuv so always appears. // See http://docs.libuv.org/en/v1.x/signal.html data << "signum: " << handle->signal.signum << " (" << 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; if (h->type == UV_TCP || h->type == UV_UDP) { data << ", "; } uv_send_buffer_size(h, &send_size); uv_recv_buffer_size(h, &recv_size); data << "send buffer size: " << send_size << ", recv buffer size: " << 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) { switch (fd_v) { case 0: data << ", stdin"; break; case 1: data << ", stdout"; break; case 2: data << ", stderr"; break; default: data << ", file descriptor: " << static_cast(fd_v); break; } } } #endif if (h->type == UV_TCP || h->type == UV_NAMED_PIPE || h->type == UV_TTY) { data << ", write queue size: " << handle->stream.write_queue_size; data << (uv_is_readable(&handle->stream) ? ", readable" : "") << (uv_is_writable(&handle->stream) ? ", writable" : ""); } writer->json_start(); writer->json_keyvalue("type", type); writer->json_keyvalue("is_active", static_cast(uv_is_active(h))); writer->json_keyvalue("is_referenced", static_cast(uv_has_ref(h))); writer->json_keyvalue("address", ValueToHexString(reinterpret_cast(h))); writer->json_keyvalue("details", data.str()); writer->json_end(); } std::string EscapeJsonChars(const std::string& str) { const std::string control_symbols[0x20] = { "\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006", "\\u0007", "\\b", "\\t", "\\n", "\\v", "\\f", "\\r", "\\u000e", "\\u000f", "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014", "\\u0015", "\\u0016", "\\u0017", "\\u0018", "\\u0019", "\\u001a", "\\u001b", "\\u001c", "\\u001d", "\\u001e", "\\u001f" }; std::string ret = ""; size_t last_pos = 0; size_t pos = 0; for (; pos < str.size(); ++pos) { std::string replace; char ch = str[pos]; if (ch == '\\') { replace = "\\\\"; } else if (ch == '\"') { replace = "\\\""; } else { size_t num = static_cast(ch); if (num < 0x20) replace = control_symbols[num]; } if (!replace.empty()) { if (pos > last_pos) { ret += str.substr(last_pos, pos - last_pos); } last_pos = pos + 1; ret += replace; } } // Append any remaining symbols. if (last_pos < str.size()) { ret += str.substr(last_pos, pos - last_pos); } return ret; } } // namespace report