Files
TheRock/rocm-systems/projects/rccl/test/PluginCompatV11_test.cpp
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

273 lines
15 KiB
C++

/*************************************************************************
* Copyright (c) 2026 Advanced Micro Devices, Inc. All rights reserved.
*
* See LICENSE.txt for license information
************************************************************************/
// Regression test for AICOMRCCL-1532: the v11 network-plugin compat layer must
// initialize lazily. getNcclNet_v11() must populate only ncclNet.name/.init;
// the remaining entry points must stay null until ncclNet.init() runs (the old
// bug wired them up eagerly, exposing an uninitialized plugin).
//
// Uses a purely in-process mock: it exports the ncclNetPlugin_v11 /
// ncclCollNetPlugin_v11 symbols the compat layer resolves via dlsym, hands it a
// dlopen(NULL) handle, and checks that only ->init is set before init() and the
// rest become non-null after. No GPU or real plugin required.
#include <gtest/gtest.h>
#include <dlfcn.h>
#include "nccl.h"
#include "nccl_net.h"
// Compat-layer entry points from librccl.so (net_v11.cc); global-namespace C++
// functions, so this prototype matches the (Debug) library's mangled symbol.
ncclNet_t* getNcclNet_v11(void* lib);
ncclCollNet_t* getNcclCollNet_v11(void* lib);
namespace RcclUnitTesting {
namespace {
// Mock v11 net plugin: each entry point is a distinct non-null stub so we can
// tell "wired up" from "still null"; init() just returns success.
ncclResult_t mockNetInit(void** ctx, uint64_t, ncclNetCommConfig_v11_t*,
ncclDebugLogger_t, ncclProfilerCallback_t) {
if (ctx) { *ctx = nullptr; }
return ncclSuccess;
}
ncclResult_t mockNetDevices(int* ndev) { if (ndev) { *ndev = 1; } return ncclSuccess; }
ncclResult_t mockNetGetProperties(int, ncclNetProperties_v11_t*) { return ncclSuccess; }
ncclResult_t mockNetListen(void*, int, void*, void**) { return ncclSuccess; }
ncclResult_t mockNetConnect(void*, int, void*, void**, ncclNetDeviceHandle_v11_t**) { return ncclSuccess; }
ncclResult_t mockNetAccept(void*, void**, ncclNetDeviceHandle_v11_t**) { return ncclSuccess; }
ncclResult_t mockNetRegMr(void*, void*, size_t, int, void**) { return ncclSuccess; }
ncclResult_t mockNetRegMrDmaBuf(void*, void*, size_t, int, uint64_t, int, void**) { return ncclSuccess; }
ncclResult_t mockNetDeregMr(void*, void*) { return ncclSuccess; }
ncclResult_t mockNetIsend(void*, void*, size_t, int, void*, void*, void**) { return ncclSuccess; }
ncclResult_t mockNetIrecv(void*, int, void**, size_t*, int*, void**, void**, void**) { return ncclSuccess; }
ncclResult_t mockNetIflush(void*, int, void**, int*, void**, void**) { return ncclSuccess; }
ncclResult_t mockNetTest(void*, int*, int*) { return ncclSuccess; }
ncclResult_t mockNetCloseSend(void*) { return ncclSuccess; }
ncclResult_t mockNetCloseRecv(void*) { return ncclSuccess; }
ncclResult_t mockNetCloseListen(void*) { return ncclSuccess; }
ncclResult_t mockNetGetDeviceMr(void*, void*, void**) { return ncclSuccess; }
ncclResult_t mockNetIrecvConsumed(void*, int, void*) { return ncclSuccess; }
ncclResult_t mockNetMakeVDevice(int*, ncclNetVDeviceProps_v11_t*) { return ncclSuccess; }
ncclResult_t mockNetFinalize(void*) { return ncclSuccess; }
ncclResult_t mockNetSetNetAttr(void*, ncclNetAttr_v11_t*) { return ncclSuccess; }
// ---------------------------------------------------------------------------
// Mock v11 collnet plugin (same lazy-init contract as the net plugin).
// ---------------------------------------------------------------------------
ncclResult_t mockCollInit(void** ctx, uint64_t, ncclDebugLogger_t) {
if (ctx) { *ctx = nullptr; }
return ncclSuccess;
}
ncclResult_t mockCollDevices(int* ndev) { if (ndev) { *ndev = 1; } return ncclSuccess; }
ncclResult_t mockCollGetProperties(int, ncclNetProperties_v11_t*) { return ncclSuccess; }
ncclResult_t mockCollListen(void*, int, void*, void**) { return ncclSuccess; }
ncclResult_t mockCollConnect(void**, int, int, void*, void**) { return ncclSuccess; }
ncclResult_t mockCollReduceSupport(ncclDataType_t, ncclRedOp_t, int*) { return ncclSuccess; }
ncclResult_t mockCollRegMr(void*, void*, size_t, int, void**) { return ncclSuccess; }
ncclResult_t mockCollRegMrDmaBuf(void*, void*, size_t, int, uint64_t, int, void**) { return ncclSuccess; }
ncclResult_t mockCollDeregMr(void*, void*) { return ncclSuccess; }
ncclResult_t mockCollIallreduce(void*, void*, void*, size_t, ncclDataType_t, ncclRedOp_t, void*, void*, void**) { return ncclSuccess; }
ncclResult_t mockCollIallgather(void*, void*, int, ncclNetSGE_v11_t*, size_t, size_t, size_t, void*, void**) { return ncclSuccess; }
ncclResult_t mockCollIreducescatter(void*, int, ncclNetSGE_v11_t*, void*, size_t, size_t, size_t, ncclDataType_t, ncclRedOp_t, void*, void**) { return ncclSuccess; }
ncclResult_t mockCollIflush(void*, void*, int, void*, void**) { return ncclSuccess; }
ncclResult_t mockCollTest(void*, int*, int*) { return ncclSuccess; }
ncclResult_t mockCollCloseColl(void*) { return ncclSuccess; }
ncclResult_t mockCollCloseListen(void*) { return ncclSuccess; }
ncclResult_t mockCollMakeVDevice(int*, ncclNetVDeviceProps_v11_t*) { return ncclSuccess; }
ncclResult_t mockCollFinalize(void*) { return ncclSuccess; }
} // namespace
} // namespace RcclUnitTesting
// These symbols are what getNcclNet_v11()/getNcclCollNet_v11() look up via
// dlsym(). The CMake target exports them (--export-dynamic-symbol) so they land
// in the test executable's dynamic symbol table; they must be named exactly, so
// we place them at global scope with C linkage and default visibility.
extern "C" {
__attribute__((visibility("default")))
ncclNet_v11_t ncclNetPlugin_v11 = {
"mock_v11_net", // name
RcclUnitTesting::mockNetInit, // init
RcclUnitTesting::mockNetDevices, // devices
RcclUnitTesting::mockNetGetProperties, // getProperties
RcclUnitTesting::mockNetListen, // listen
RcclUnitTesting::mockNetConnect, // connect
RcclUnitTesting::mockNetAccept, // accept
RcclUnitTesting::mockNetRegMr, // regMr
RcclUnitTesting::mockNetRegMrDmaBuf, // regMrDmaBuf
RcclUnitTesting::mockNetDeregMr, // deregMr
RcclUnitTesting::mockNetIsend, // isend
RcclUnitTesting::mockNetIrecv, // irecv
RcclUnitTesting::mockNetIflush, // iflush
RcclUnitTesting::mockNetTest, // test
RcclUnitTesting::mockNetCloseSend, // closeSend
RcclUnitTesting::mockNetCloseRecv, // closeRecv
RcclUnitTesting::mockNetCloseListen, // closeListen
RcclUnitTesting::mockNetGetDeviceMr, // getDeviceMr
RcclUnitTesting::mockNetIrecvConsumed, // irecvConsumed
RcclUnitTesting::mockNetMakeVDevice, // makeVDevice
RcclUnitTesting::mockNetFinalize, // finalize
RcclUnitTesting::mockNetSetNetAttr, // setNetAttr
};
__attribute__((visibility("default")))
ncclCollNet_v11_t ncclCollNetPlugin_v11 = {
"mock_v11_collnet", // name
RcclUnitTesting::mockCollInit, // init
RcclUnitTesting::mockCollDevices, // devices
RcclUnitTesting::mockCollGetProperties, // getProperties
RcclUnitTesting::mockCollListen, // listen
RcclUnitTesting::mockCollConnect, // connect
RcclUnitTesting::mockCollReduceSupport, // reduceSupport
RcclUnitTesting::mockCollRegMr, // regMr
RcclUnitTesting::mockCollRegMrDmaBuf, // regMrDmaBuf
RcclUnitTesting::mockCollDeregMr, // deregMr
RcclUnitTesting::mockCollIallreduce, // iallreduce
RcclUnitTesting::mockCollIallgather, // iallgather
RcclUnitTesting::mockCollIreducescatter, // ireducescatter
RcclUnitTesting::mockCollIflush, // iflush
RcclUnitTesting::mockCollTest, // test
RcclUnitTesting::mockCollCloseColl, // closeColl
RcclUnitTesting::mockCollCloseListen, // closeListen
RcclUnitTesting::mockCollMakeVDevice, // makeVDevice
RcclUnitTesting::mockCollFinalize, // finalize
};
} // extern "C"
namespace RcclUnitTesting {
// getNcclNet_v11() must hand back a real vtable with ONLY ->init populated;
// every other entry point stays null until init() runs, and becomes non-null
// afterwards. This is the core AICOMRCCL-1532 contract - the pre-fix code
// populated the whole vtable eagerly and would fail the "before init()" block.
TEST(PluginCompatV11, NetLazyInitialization) {
void* self = dlopen(nullptr, RTLD_NOW | RTLD_GLOBAL);
ASSERT_NE(self, nullptr) << "dlopen(NULL) failed: " << dlerror();
ncclNet_t* net = getNcclNet_v11(self);
ASSERT_NE(net, nullptr)
<< "getNcclNet_v11 returned null; the in-process ncclNetPlugin_v11 symbol "
"was not resolved (is the test linked with -rdynamic?)";
// init() is the single entry point the compat layer is allowed to populate up
// front, because it is the callback that performs the lazy initialization.
EXPECT_NE(net->init, nullptr) << "compat layer must set ncclNet.init";
// Everything else must still be null: the plugin has not been init()'d, so no
// per-device entry point may be exposed yet.
EXPECT_EQ(net->devices, nullptr) << "devices set before init()";
EXPECT_EQ(net->getProperties, nullptr) << "getProperties set before init()";
EXPECT_EQ(net->listen, nullptr) << "listen set before init()";
EXPECT_EQ(net->connect, nullptr) << "connect set before init()";
EXPECT_EQ(net->accept, nullptr) << "accept set before init()";
EXPECT_EQ(net->regMr, nullptr) << "regMr set before init()";
EXPECT_EQ(net->regMrDmaBuf, nullptr) << "regMrDmaBuf set before init()";
EXPECT_EQ(net->deregMr, nullptr) << "deregMr set before init()";
EXPECT_EQ(net->isend, nullptr) << "isend set before init()";
EXPECT_EQ(net->irecv, nullptr) << "irecv set before init()";
EXPECT_EQ(net->iflush, nullptr) << "iflush set before init()";
EXPECT_EQ(net->test, nullptr) << "test set before init()";
EXPECT_EQ(net->closeSend, nullptr) << "closeSend set before init()";
EXPECT_EQ(net->closeRecv, nullptr) << "closeRecv set before init()";
EXPECT_EQ(net->closeListen, nullptr) << "closeListen set before init()";
EXPECT_EQ(net->getDeviceMr, nullptr) << "getDeviceMr set before init()";
EXPECT_EQ(net->irecvConsumed, nullptr) << "irecvConsumed set before init()";
EXPECT_EQ(net->makeVDevice, nullptr) << "makeVDevice set before init()";
EXPECT_EQ(net->finalize, nullptr) << "finalize set before init()";
EXPECT_EQ(net->setNetAttr, nullptr) << "setNetAttr set before init()";
// Run the lazy init. The compat layer now wires up the remaining entry points
// from the (mock) v11 plugin struct.
void* ctx = nullptr;
ASSERT_EQ(net->init(&ctx, /*commId=*/0, /*config=*/nullptr,
/*logFunction=*/nullptr, /*profFunction=*/nullptr),
ncclSuccess);
EXPECT_NE(net->devices, nullptr) << "devices null after init()";
EXPECT_NE(net->getProperties, nullptr) << "getProperties null after init()";
EXPECT_NE(net->listen, nullptr) << "listen null after init()";
EXPECT_NE(net->connect, nullptr) << "connect null after init()";
EXPECT_NE(net->accept, nullptr) << "accept null after init()";
EXPECT_NE(net->regMr, nullptr) << "regMr null after init()";
EXPECT_NE(net->regMrDmaBuf, nullptr) << "regMrDmaBuf null after init()";
EXPECT_NE(net->deregMr, nullptr) << "deregMr null after init()";
EXPECT_NE(net->isend, nullptr) << "isend null after init()";
EXPECT_NE(net->irecv, nullptr) << "irecv null after init()";
EXPECT_NE(net->iflush, nullptr) << "iflush null after init()";
EXPECT_NE(net->test, nullptr) << "test null after init()";
EXPECT_NE(net->closeSend, nullptr) << "closeSend null after init()";
EXPECT_NE(net->closeRecv, nullptr) << "closeRecv null after init()";
EXPECT_NE(net->closeListen, nullptr) << "closeListen null after init()";
EXPECT_NE(net->getDeviceMr, nullptr) << "getDeviceMr null after init()";
EXPECT_NE(net->irecvConsumed, nullptr) << "irecvConsumed null after init()";
EXPECT_NE(net->makeVDevice, nullptr) << "makeVDevice null after init()";
EXPECT_NE(net->finalize, nullptr) << "finalize null after init()";
EXPECT_NE(net->setNetAttr, nullptr) << "setNetAttr null after init()";
dlclose(self);
}
// Same lazy-init contract for the collnet compat layer.
TEST(PluginCompatV11, CollNetLazyInitialization) {
void* self = dlopen(nullptr, RTLD_NOW | RTLD_GLOBAL);
ASSERT_NE(self, nullptr) << "dlopen(NULL) failed: " << dlerror();
ncclCollNet_t* coll = getNcclCollNet_v11(self);
ASSERT_NE(coll, nullptr)
<< "getNcclCollNet_v11 returned null; the in-process ncclCollNetPlugin_v11 "
"symbol was not resolved (is the test linked with -rdynamic?)";
EXPECT_NE(coll->init, nullptr) << "compat layer must set ncclCollNet.init";
EXPECT_EQ(coll->devices, nullptr) << "devices set before init()";
EXPECT_EQ(coll->getProperties, nullptr) << "getProperties set before init()";
EXPECT_EQ(coll->listen, nullptr) << "listen set before init()";
EXPECT_EQ(coll->connect, nullptr) << "connect set before init()";
EXPECT_EQ(coll->reduceSupport, nullptr) << "reduceSupport set before init()";
EXPECT_EQ(coll->regMr, nullptr) << "regMr set before init()";
EXPECT_EQ(coll->regMrDmaBuf, nullptr) << "regMrDmaBuf set before init()";
EXPECT_EQ(coll->deregMr, nullptr) << "deregMr set before init()";
EXPECT_EQ(coll->iallreduce, nullptr) << "iallreduce set before init()";
EXPECT_EQ(coll->iallgather, nullptr) << "iallgather set before init()";
EXPECT_EQ(coll->ireducescatter, nullptr) << "ireducescatter set before init()";
EXPECT_EQ(coll->iflush, nullptr) << "iflush set before init()";
EXPECT_EQ(coll->test, nullptr) << "test set before init()";
EXPECT_EQ(coll->closeColl, nullptr) << "closeColl set before init()";
EXPECT_EQ(coll->closeListen, nullptr) << "closeListen set before init()";
EXPECT_EQ(coll->makeVDevice, nullptr) << "makeVDevice set before init()";
EXPECT_EQ(coll->finalize, nullptr) << "finalize set before init()";
void* ctx = nullptr;
ASSERT_EQ(coll->init(&ctx, /*commId=*/0, /*logFunction=*/nullptr), ncclSuccess);
EXPECT_NE(coll->devices, nullptr) << "devices null after init()";
EXPECT_NE(coll->getProperties, nullptr) << "getProperties null after init()";
EXPECT_NE(coll->listen, nullptr) << "listen null after init()";
EXPECT_NE(coll->connect, nullptr) << "connect null after init()";
EXPECT_NE(coll->reduceSupport, nullptr) << "reduceSupport null after init()";
EXPECT_NE(coll->regMr, nullptr) << "regMr null after init()";
EXPECT_NE(coll->regMrDmaBuf, nullptr) << "regMrDmaBuf null after init()";
EXPECT_NE(coll->deregMr, nullptr) << "deregMr null after init()";
EXPECT_NE(coll->iallreduce, nullptr) << "iallreduce null after init()";
EXPECT_NE(coll->iallgather, nullptr) << "iallgather null after init()";
EXPECT_NE(coll->ireducescatter, nullptr) << "ireducescatter null after init()";
EXPECT_NE(coll->iflush, nullptr) << "iflush null after init()";
EXPECT_NE(coll->test, nullptr) << "test null after init()";
EXPECT_NE(coll->closeColl, nullptr) << "closeColl null after init()";
EXPECT_NE(coll->closeListen, nullptr) << "closeListen null after init()";
EXPECT_NE(coll->makeVDevice, nullptr) << "makeVDevice null after init()";
EXPECT_NE(coll->finalize, nullptr) << "finalize null after init()";
dlclose(self);
}
} // namespace RcclUnitTesting