diff --git a/ddprof-lib/src/main/cpp/libraryPatcher.h b/ddprof-lib/src/main/cpp/libraryPatcher.h index f54e2dc1d..a1d7829c0 100644 --- a/ddprof-lib/src/main/cpp/libraryPatcher.h +++ b/ddprof-lib/src/main/cpp/libraryPatcher.h @@ -40,7 +40,8 @@ typedef struct _socketPatchedLibrary { enum SocketPatchTarget : u8 { SOCKET_PATCH_NONE = 0, SOCKET_PATCH_STANDARD_JDK_NETWORK, - SOCKET_PATCH_IBM_JCL_BRIDGE + SOCKET_PATCH_IBM_JCL_BRIDGE, + SOCKET_PATCH_NETTY_NATIVE_EPOLL }; const int SOCKET_BASE_TABLE_SIZE = MAX_NATIVE_LIBS * 2; diff --git a/ddprof-lib/src/main/cpp/libraryPatcher_linux.cpp b/ddprof-lib/src/main/cpp/libraryPatcher_linux.cpp index 194925216..1d674a3ed 100644 --- a/ddprof-lib/src/main/cpp/libraryPatcher_linux.cpp +++ b/ddprof-lib/src/main/cpp/libraryPatcher_linux.cpp @@ -52,7 +52,21 @@ static const char* library_basename(const char* path) { static SocketPatchTarget socket_patch_target_for_library( CodeCache* lib, const char* name, bool in_jdk_directory) { - if (lib == nullptr || name == nullptr || !in_jdk_directory) { + if (lib == nullptr || name == nullptr) { + return SOCKET_PATCH_NONE; + } + + // Netty's native epoll transport ships its own .so outside the JDK's lib + // directory, so it is checked ahead of the in_jdk_directory gate below. + // Detection is by exported symbol rather than basename because the .so + // filename is relocated under shading (e.g. grpc-netty-shaded), while the + // JNI_OnLoad_/JNI_OnUnload_ suffix is not. + if (lib->findSymbol("JNI_OnLoad_netty_transport_native_epoll") != nullptr && + lib->findSymbol("JNI_OnUnload_netty_transport_native_epoll") != nullptr) { + return SOCKET_PATCH_NETTY_NATIVE_EPOLL; + } + + if (!in_jdk_directory) { return SOCKET_PATCH_NONE; } diff --git a/ddprof-lib/src/test/cpp/nativeSocketInterposer_ut.cpp b/ddprof-lib/src/test/cpp/nativeSocketInterposer_ut.cpp index ff1f8cb5d..d90313bf3 100644 --- a/ddprof-lib/src/test/cpp/nativeSocketInterposer_ut.cpp +++ b/ddprof-lib/src/test/cpp/nativeSocketInterposer_ut.cpp @@ -1103,6 +1103,36 @@ TEST(LibraryPatcherSocketStateTest, } } +TEST(LibraryPatcherSocketStateTest, AdmitsNettyNativeEpollBySymbolOutsideJdkDirectory) { + CodeCache netty("libnetty_transport_native_epoll_x86_64.so"); + CodeCache netty_shaded("libio_grpc_netty_shaded_netty_transport_native_epoll_x86_64.so"); + CodeCache netty_partial("libnetty_transport_native_epoll_x86_64.so"); + + char marker_addresses[2] = {}; + netty.add(&marker_addresses[0], 1, "JNI_OnLoad_netty_transport_native_epoll"); + netty.add(&marker_addresses[1], 1, "JNI_OnUnload_netty_transport_native_epoll"); + netty_shaded.add(&marker_addresses[0], 1, "JNI_OnLoad_netty_transport_native_epoll"); + netty_shaded.add(&marker_addresses[1], 1, "JNI_OnUnload_netty_transport_native_epoll"); + netty_partial.add(&marker_addresses[0], 1, "JNI_OnLoad_netty_transport_native_epoll"); + + // Admitted even though it is not located in the JDK's own lib directory, + // and regardless of the .so's basename (shading relocates the filename). + EXPECT_EQ(SOCKET_PATCH_NETTY_NATIVE_EPOLL, + LibraryPatcher::socket_patch_target_for_test( + &netty, "libnetty_transport_native_epoll_x86_64.so", false)); + EXPECT_EQ(SOCKET_PATCH_NETTY_NATIVE_EPOLL, + LibraryPatcher::socket_patch_target_for_test( + &netty_shaded, + "libio_grpc_netty_shaded_netty_transport_native_epoll_x86_64.so", + false)); + + // Requires both symbols, mirroring the IBM JCL bridge's all-or-nothing check. + EXPECT_EQ(SOCKET_PATCH_NONE, + LibraryPatcher::socket_patch_target_for_test( + &netty_partial, + "libnetty_transport_native_epoll_x86_64.so", false)); +} + TEST_F(LibraryPatcherImportTest, PatchesAndRestoresEveryImportLocation) { initializeImports(3); void* originals[3] = {imports[0], imports[1], imports[2]};