diff --git a/src/debug_utils.cc b/src/debug_utils.cc index 6dac86f002b07d..434c9853c02a2e 100644 --- a/src/debug_utils.cc +++ b/src/debug_utils.cc @@ -282,20 +282,36 @@ void DumpBacktrace(FILE* fp) { void CheckedUvLoopClose(uv_loop_t* loop) { if (uv_loop_close(loop) == 0) return; - auto sym_ctx = NativeSymbolDebuggingContext::New(); + PrintLibuvHandleInformation(loop, stderr); + + fflush(stderr); + // Finally, abort. + CHECK(0 && "uv_loop_close() while having open handles"); +} - fprintf(stderr, "uv loop at [%p] has active handles\n", loop); +void PrintLibuvHandleInformation(uv_loop_t* loop, FILE* stream) { + struct Info { + std::unique_ptr ctx; + FILE* stream; + }; + + Info info { NativeSymbolDebuggingContext::New(), stream }; + + fprintf(stream, "uv loop at [%p] has %d active handles\n", + loop, loop->active_handles); uv_walk(loop, [](uv_handle_t* handle, void* arg) { - auto sym_ctx = static_cast(arg); + Info* info = static_cast(arg); + NativeSymbolDebuggingContext* sym_ctx = info->ctx.get(); + FILE* stream = info->stream; - fprintf(stderr, "[%p] %s\n", handle, uv_handle_type_name(handle->type)); + fprintf(stream, "[%p] %s\n", handle, uv_handle_type_name(handle->type)); void* close_cb = reinterpret_cast(handle->close_cb); - fprintf(stderr, "\tClose callback: %p %s\n", + fprintf(stream, "\tClose callback: %p %s\n", close_cb, sym_ctx->LookupSymbol(close_cb).Display().c_str()); - fprintf(stderr, "\tData: %p %s\n", + fprintf(stream, "\tData: %p %s\n", handle->data, sym_ctx->LookupSymbol(handle->data).Display().c_str()); // We are also interested in the first field of what `handle->data` @@ -309,14 +325,10 @@ void CheckedUvLoopClose(uv_loop_t* loop) { first_field = *reinterpret_cast(handle->data); if (first_field != nullptr) { - fprintf(stderr, "\t(First field): %p %s\n", + fprintf(stream, "\t(First field): %p %s\n", first_field, sym_ctx->LookupSymbol(first_field).Display().c_str()); } - }, sym_ctx.get()); - - fflush(stderr); - // Finally, abort. - CHECK(0 && "uv_loop_close() while having open handles"); + }, &info); } std::vector NativeSymbolDebuggingContext::GetLoadedLibraries() { diff --git a/src/debug_utils.h b/src/debug_utils.h index 7e8a23d3ae309e..05a9370e562f82 100644 --- a/src/debug_utils.h +++ b/src/debug_utils.h @@ -119,6 +119,7 @@ class NativeSymbolDebuggingContext { // about giving information on currently existing handles, if there are any, // but still aborts the process. void CheckedUvLoopClose(uv_loop_t* loop); +void PrintLibuvHandleInformation(uv_loop_t* loop, FILE* stream); } // namespace node diff --git a/test/abort/test-addon-uv-handle-leak.js b/test/abort/test-addon-uv-handle-leak.js index 58061b0532fb8a..0bfb6cdb94b72d 100644 --- a/test/abort/test-addon-uv-handle-leak.js +++ b/test/abort/test-addon-uv-handle-leak.js @@ -89,7 +89,7 @@ if (process.argv[2] === 'child') { switch (state) { case 'initial': - assert(/^uv loop at \[.+\] has active handles$/.test(line), line); + assert(/^uv loop at \[.+\] has \d+ active handles$/.test(line), line); state = 'handle-start'; break; case 'handle-start':