Skip to content

chore: replace Corrade plugin manager with hand-rolled in-process registry - #770

Merged
leoparente merged 13 commits into
developfrom
chore/drop-corrade
May 9, 2026
Merged

leoparente merged 13 commits into
developfrom
chore/drop-corrade

Conversation

@leoparente

@leoparente leoparente commented May 8, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Drops the Corrade dependency entirely from pktvisor and replaces its PluginManager (which was being used as a glorified static-init registry, not for actual dynamic loading) with a small explicit factory list owned by a new tiny static lib Visor::BuiltinPlugins.

This eliminates a long tail of build-system pain we'd been working around for months:

  • The macOS Clang compile failure with corrade/2020.06
  • The <vector> include workaround in CI
  • The vendored conan/corrade/ recipe (created in chore: upgrade corrade to cci.20260327 to fix macOS build #769) and the musl-libc strerror_r patch
  • The Conan export step in every CI workflow + Dockerfile + README
  • The Corrade-specific pluginInterface global-StringView runtime requirement (_s literal)
  • The Containers::StringView/String/Array<String>/Pointer API churn at every plugin boundary
  • All corrade_add_static_plugin / corrade_add_plugin cmake calls and 16 plugin .conf metadata files

Architecture

The build now has a strictly linear archive dependency chain — no platform/compiler/linker conditionals anywhere:

pktvisord  /  pktvisor-reader  /  unit-tests-visor-core
        └─ Visor::BuiltinPlugins      (new tiny static lib, src/BuiltinPlugins.cpp)
               ├─ plugin libs          (Visor::Handler::Net, Visor::Input::Pcap, …)
               │      └─ Visor::Core
               └─ Visor::Core

CoreRegistry doesn't know which plugins exist. It exposes:

  • add_input_plugin(alias, version, std::unique_ptr<InputModulePlugin>) and the handler counterpart — called before start() to queue plugins for initialization
  • pending_input_plugins() / pending_handler_plugins() — metadata enumeration for --module-list (without instantiating)
  • start(svr) — initializes the queued plugins and moves them into the active map

The new visor-builtin-plugins static lib (single source file src/BuiltinPlugins.cpp) is the only translation unit that pulls in plugin headers. It defines void load_builtin_plugins(CoreRegistry&), which calls add_*_plugin() with std::make_unique<T>(...) for each of the 16 built-in plugins. Both pktvisord and pktvisor-reader (and the test binary) call load_builtin_plugins(registry) once after constructing the registry.

This breaks the cyclic visor-core ↔ plugin libs dependency that earlier iterations had, so no --start-group/--end-group linker tricks are needed on any platform.

Built-in plugin set (16 total)

Inputs (5): mock, pcap, dnstap, flow, netprobe — plus sflow as a secondary alias of the flow input.
Handlers (10): net (v1, v2), dns (v1, v2), bgp, flow, dhcp, pcap, netprobe, input_resources.

The previous Corrade-era mock_dyn handler was removed in this PR — it was a dynamic-loading test artifact (built with corrade_add_plugin not _static, exercised only by a visor_dyn_mod_int_test gated on DYNAMIC_LIB_SUPPORT that invoked pktvisord --module-dir … --module-list). With Corrade gone and dynamic loading dropped, it tested nothing.

Other cleanups

  • find_package(Corrade REQUIRED) removed; corrade/2020.06 removed from conanfile.py
  • --module-dir CLI option logs a deprecation warning (was a no-op anyway); --module-list and --module-dir help text updated to reflect the new reality
  • NOMINMAX + WIN32_LEAN_AND_MEAN added to root CMakeLists.txt for Windows (Corrade's headers used to set these for us transitively)
  • macOS CI unit-tests-mac profile now appends tools.apple:sdk_path=$(xcrun --sdk macosx --show-sdk-path) to fix the bare-macosx sysroot issue Conan's CMakeToolchain emits
  • All 16 plugin .conf metadata files deleted; metadata now lives inline in BuiltinPlugins.cpp
  • All corrade_add_static_plugin / corrade_add_plugin calls replaced with add_library(... STATIC ...)
  • Plugin constructors take std::string alias instead of (Corrade::PluginManager::AbstractManager&, const std::string&)

Test plan

  • Local build on macOS arm64 (apple-clang 17): clean
  • pktvisord --module-list reports all 16 plugins (5 inputs + 1 alias = 6 lines, 10 handlers)
  • unit-tests-visor-core passes
  • CI: unit-tests-linux, unit-tests-mac, build-win64, cross-compile (musl)
  • CodeQL workflow

The pre-existing unit-tests-input-dnstap, unit-tests-input-flow, unit-tests-input-netprobe, and unit-tests-handler-netprobe Bus/SEGFAULT failures on macOS persist on this branch — verified they reproduce on develop before the changes (unrelated to Corrade or this refactor).

…istry

Corrade's PluginManager was being used purely for static-linked plugin
registration — pluginSearchPaths() returned {""}, no .so files were ever
loaded at runtime, and CORRADE_PLUGIN_IMPORT was the entry point for
every plugin. We were paying for an industrial-strength dynamic loader
to do compile-time registration, which has been the root cause of every
build pain in recent memory: macOS clang vector-include workaround,
musl strerror_r patch, vendored conan recipe for cci.20260327, fragile
StringView::Global / _s literal handling, etc.

Replace it with a ~80-line PluginRegistry<T> in src/PluginRegistry.h:
each plugin .cpp invokes VISOR_REGISTER_HANDLER_PLUGIN / _INPUT_PLUGIN
at file scope, which appends a {alias, version, interface, factory}
entry to the per-base-class singleton registry at static-init time.
CoreRegistry::start() iterates entries() and instantiates each plugin
directly. Plugin metadata that previously lived in *.conf files is now
baked into the registration macro arguments. Multi-alias support
(flow/sflow input) is handled via an _ALIAS variant.

Linker pruning of static archives is solved by having the macro emit
an extern "C" int visor_force_link_<SYMBOL> = 1 in each plugin .cpp,
and having handlers/static_plugins.h and inputs/static_plugins.h
reference those symbols — same approach Corrade used internally.

Build/CI changes:
- conanfile.py: drop corrade requires + tool_requires
- CMakeLists.txt: drop find_package(Corrade)
- src/CMakeLists.txt: drop CORRADE_USE_PEDANTIC_FLAGS, drop Corrade::Corrade link
- plugin CMakeLists.txt (all 16): corrade_add_static_plugin/corrade_add_plugin
  -> add_library(... STATIC ...), drop .conf metadata file argument
- delete all *.conf plugin metadata files
- .github/workflows/build-develop.yml: drop the macOS -include vector
  workaround from CONAN_INSTALL_ARGS (corrade was the only target needing it)
- .github/workflows/build_cross.yml: drop -DCORRADE_RC_PROGRAM
- .github/actions/build-cpp/entrypoint.sh: drop -DCORRADE_RC_PROGRAM
- cmd/pktvisord/main.cpp: --module-list now iterates the new registry;
  --module-dir logs a deprecation warning (dynamic loading is gone)

Verified locally on macOS arm64: clean build succeeds, pktvisord
--module-list correctly reports all 17 plugins (6 inputs incl. flow/sflow,
11 handlers), 19/23 unit tests pass (the 4 failures match develop and
are pre-existing).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@leoparente

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 413db0eb1d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/handlers/static_plugins.h Outdated
Three failures on PR #770 CI; one fix each:

1. Linux unit-tests-linux (Codex P1): GCC at -O2 was eliding the
   force_link_*_plugins() call entirely because the static const int
   anchor variable was unused and the function had no observable side
   effects. The plugin .o files then never got pulled out of the static
   archives, so the registrars never ran and the runtime registry was
   empty (test_policies.cpp:817+ all FAILED).

   Fix: replace the int-returning anchor function with an inline pointer
   array whose initializers take the address of each visor_force_link_*
   extern. Mark the array [[gnu::used]] on GCC/Clang so the compiler
   must emit it even though it's never read; inline + comdat keeps MSVC
   honest. The pointer references force the linker to resolve the
   symbols and pull the .o files in.

2. macOS unit-tests-mac: Conan's CMakeToolchain set CMAKE_OSX_SYSROOT
   to the bare string "macosx" instead of the resolved SDK path, so
   clang failed every compile with 'no such sysroot directory' and
   could not find <exception>, <stdio.h>, etc. Same issue we fixed on
   chore/upgrade-corrade.

   Fix: append tools.apple:sdk_path=$(xcrun --sdk macosx --show-sdk-path)
   to the conan default profile after `conan profile detect`, so the
   toolchain uses the resolved SDK path.

3. Windows build-win64: 3rd/rng/jsf.h failed to compile because windows.h
   was defining min/max as macros, mangling the static constexpr min()
   and max() member functions. This used to work because Corrade's
   headers transitively defined NOMINMAX before any windows.h could leak
   in; with Corrade gone, that protection is gone.

   Fix: add NOMINMAX (and WIN32_LEAN_AND_MEAN while we're here) as
   compile definitions for Windows in the root CMakeLists.txt.

Verified locally on macOS arm64 that the new force-link pattern still
correctly registers all 17 plugins and unit-tests-visor-core passes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@leoparente

Copy link
Copy Markdown
Contributor Author

Fixed in 9fa2751. Three failures, three fixes:

1. Codex P1 — Linux test_policies failures: Confirmed the diagnosis. GCC at -O2 was eliminating the [[maybe_unused]] static const int = force_link_handler_plugins() initializer because the variable was unused and the call had no observable side effects, so the plugin .o files never got pulled out of the static archives.

Fix: replaced the int-returning anchor with an inline int *const pointer array whose initializers take the address of each visor_force_link_* extern. The array is marked [[gnu::used]] on GCC/Clang so the compiler must emit it even when it's never read; the pointer references then force the linker to resolve the symbols and pull in the plugin .o files. Same approach in both handlers/static_plugins.h and inputs/static_plugins.h.

2. macOS unit-tests-mac: Conan's CMakeToolchain was setting CMAKE_OSX_SYSROOT=macosx (bare string) so clang couldn't find any system headers. Added tools.apple:sdk_path to the conan profile after conan profile detect — same fix we used on chore/upgrade-corrade.

3. Windows build-win64: 3rd/rng/jsf.h failed because windows.h was defining min/max as macros — Corrade's headers used to set NOMINMAX for us, and with Corrade gone that protection vanished. Added add_compile_definitions(NOMINMAX WIN32_LEAN_AND_MEAN) to the Windows branch of root CMakeLists.txt.

@leoparente

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9fa275170a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/handlers/static_plugins.h Outdated
Comment thread src/inputs/static_plugins.h Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR removes Corrade’s PluginManager dependency and replaces it with a small in-process static plugin registry (PluginRegistry<T>), switching all plugins from Corrade registration/metadata (*.conf, CORRADE_PLUGIN_REGISTER/IMPORT) to file-scope static registration via new VISOR_REGISTER_* macros and a force-link anchoring mechanism.

Changes:

  • Introduce PluginRegistry<T> + registration macros and update CoreRegistry / CLI module listing to enumerate registry entries directly.
  • Convert all input/handler plugins and their CMake targets from Corrade plugin macros + .conf metadata to plain static libraries + macro-based registration.
  • Remove Corrade from Conan/CMake/CI plumbing and delete plugin .conf metadata files.

Reviewed changes

Copilot reviewed 98 out of 98 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/tests/test_taps.cpp Removes unused Corrade include; relies on new static plugin anchoring headers.
src/tests/test_policies.cpp Removes unused Corrade include; relies on new static plugin anchoring headers.
src/PluginRegistry.h Adds templated registry, registrar helper, and registration macros.
src/inputs/static_plugins.h Replaces Corrade import initializer with force-link anchors for input plugins.
src/inputs/pcap/PcapInputModulePlugin.h Updates plugin constructor to no longer require Corrade manager.
src/inputs/pcap/PcapInputModulePlugin.cpp Replaces Corrade registration with VISOR_REGISTER_INPUT_PLUGIN.
src/inputs/pcap/PcapInput.conf Deletes Corrade plugin metadata.
src/inputs/pcap/CMakeLists.txt Converts Corrade static plugin target to a plain static library.
src/inputs/netprobe/NetProbeInputModulePlugin.h Updates plugin constructor to no longer require Corrade manager.
src/inputs/netprobe/NetProbeInputModulePlugin.cpp Replaces Corrade registration with VISOR_REGISTER_INPUT_PLUGIN.
src/inputs/netprobe/NetProbeInput.conf Deletes Corrade plugin metadata.
src/inputs/netprobe/CMakeLists.txt Converts Corrade static plugin target to a plain static library.
src/inputs/mock/VisorInputMock.conf Deletes Corrade plugin metadata.
src/inputs/mock/MockInputModulePlugin.h Updates plugin constructor to no longer require Corrade manager.
src/inputs/mock/MockInputModulePlugin.cpp Replaces Corrade registration with VISOR_REGISTER_INPUT_PLUGIN.
src/inputs/mock/CMakeLists.txt Converts Corrade static plugin target to a plain static library.
src/inputs/flow/FlowInputModulePlugin.h Updates plugin constructor to no longer require Corrade manager.
src/inputs/flow/FlowInputModulePlugin.cpp Adds flow + sflow alias registrations via new macros.
src/inputs/flow/FlowInput.conf Deletes Corrade plugin metadata.
src/inputs/flow/CMakeLists.txt Converts Corrade static plugin target to a plain static library.
src/inputs/dnstap/DnstapInputModulePlugin.h Updates plugin constructor to no longer require Corrade manager.
src/inputs/dnstap/DnstapInputModulePlugin.cpp Replaces Corrade registration with VISOR_REGISTER_INPUT_PLUGIN.
src/inputs/dnstap/Dnstap.conf Deletes Corrade plugin metadata.
src/inputs/dnstap/CMakeLists.txt Converts Corrade static plugin target to a plain static library.
src/InputModulePlugin.h Removes Corrade types; redefines registry/pointer types using PluginRegistry + std::unique_ptr.
src/InputModulePlugin.cpp Deletes empty Corrade-era translation unit.
src/handlers/static_plugins.h Replaces Corrade import initializer with force-link anchors for handler plugins.
src/handlers/pcap/PcapStreamHandler.h Removes Corrade debug include.
src/handlers/pcap/PcapHandlerModulePlugin.h Updates plugin constructor to no longer require Corrade manager.
src/handlers/pcap/PcapHandlerModulePlugin.cpp Replaces Corrade registration with VISOR_REGISTER_HANDLER_PLUGIN.
src/handlers/pcap/PcapHandler.conf Deletes Corrade plugin metadata.
src/handlers/pcap/CMakeLists.txt Converts Corrade static plugin target to a plain static library.
src/handlers/netprobe/NetProbeStreamHandler.h Removes Corrade debug include.
src/handlers/netprobe/NetProbeHandlerModulePlugin.h Updates plugin constructor to no longer require Corrade manager.
src/handlers/netprobe/NetProbeHandlerModulePlugin.cpp Replaces Corrade registration with VISOR_REGISTER_HANDLER_PLUGIN.
src/handlers/netprobe/NetProbeHandler.conf Deletes Corrade plugin metadata.
src/handlers/netprobe/CMakeLists.txt Converts Corrade static plugin target to a plain static library.
src/handlers/net/v2/NetStreamHandler.h Removes Corrade debug include.
src/handlers/net/v2/NetStreamHandler.cpp Removes Corrade debug include.
src/handlers/net/v2/NetHandlerModulePlugin.h Updates plugin constructor to no longer require Corrade manager.
src/handlers/net/v2/NetHandlerModulePlugin.cpp Replaces Corrade registration with VISOR_REGISTER_HANDLER_PLUGIN.
src/handlers/net/v2/NetHandler.conf Deletes Corrade plugin metadata.
src/handlers/net/v2/CMakeLists.txt Converts Corrade static plugin target to a plain static library.
src/handlers/net/v1/NetStreamHandler.h Removes Corrade debug include.
src/handlers/net/v1/NetStreamHandler.cpp Removes Corrade debug include.
src/handlers/net/v1/NetHandlerModulePlugin.h Updates plugin constructor to no longer require Corrade manager.
src/handlers/net/v1/NetHandlerModulePlugin.cpp Replaces Corrade registration with VISOR_REGISTER_HANDLER_PLUGIN.
src/handlers/net/v1/NetHandler.conf Deletes Corrade plugin metadata.
src/handlers/net/v1/CMakeLists.txt Converts Corrade static plugin target to a plain static library.
src/handlers/mock/VisorHandlerMock.conf Deletes Corrade plugin metadata.
src/handlers/mock/MockStreamHandler.h Removes Corrade debug include.
src/handlers/mock/MockHandlerModulePlugin.h Updates plugin constructor to no longer require Corrade manager.
src/handlers/mock/MockHandlerModulePlugin.cpp Replaces Corrade registration with VISOR_REGISTER_HANDLER_PLUGIN.
src/handlers/mock/CMakeLists.txt Converts Corrade plugin target to a plain static library.
src/handlers/input_resources/InputResourcesStreamHandler.h Removes Corrade debug include.
src/handlers/input_resources/InputResourcesHandlerModulePlugin.h Updates plugin constructor to no longer require Corrade manager.
src/handlers/input_resources/InputResourcesHandlerModulePlugin.cpp Replaces Corrade registration with VISOR_REGISTER_HANDLER_PLUGIN.
src/handlers/input_resources/InputResourcesHandler.conf Deletes Corrade plugin metadata.
src/handlers/input_resources/CMakeLists.txt Converts Corrade static plugin target to a plain static library.
src/handlers/flow/FlowStreamHandler.h Removes Corrade debug include.
src/handlers/flow/FlowStreamHandler.cpp Removes Corrade debug include.
src/handlers/flow/FlowHandlerModulePlugin.h Updates plugin constructor to no longer require Corrade manager.
src/handlers/flow/FlowHandlerModulePlugin.cpp Replaces Corrade registration with VISOR_REGISTER_HANDLER_PLUGIN.
src/handlers/flow/FlowHandler.conf Deletes Corrade plugin metadata.
src/handlers/flow/CMakeLists.txt Converts Corrade static plugin target to a plain static library.
src/handlers/dns/v2/DnsStreamHandler.h Removes Corrade debug include.
src/handlers/dns/v2/DnsStreamHandler.cpp Removes Corrade debug include.
src/handlers/dns/v2/DnsHandlerModulePlugin.h Updates plugin constructor to no longer require Corrade manager.
src/handlers/dns/v2/DnsHandlerModulePlugin.cpp Replaces Corrade registration with VISOR_REGISTER_HANDLER_PLUGIN.
src/handlers/dns/v2/DnsHandler.conf Deletes Corrade plugin metadata.
src/handlers/dns/v2/CMakeLists.txt Converts Corrade static plugin target to a plain static library.
src/handlers/dns/v1/DnsStreamHandler.h Removes Corrade debug include.
src/handlers/dns/v1/DnsStreamHandler.cpp Removes Corrade debug include.
src/handlers/dns/v1/DnsHandlerModulePlugin.h Updates plugin constructor to no longer require Corrade manager.
src/handlers/dns/v1/DnsHandlerModulePlugin.cpp Replaces Corrade registration with VISOR_REGISTER_HANDLER_PLUGIN.
src/handlers/dns/v1/DnsHandler.conf Deletes Corrade plugin metadata.
src/handlers/dns/v1/CMakeLists.txt Converts Corrade static plugin target to a plain static library.
src/handlers/dhcp/DhcpStreamHandler.h Removes Corrade debug include.
src/handlers/dhcp/DhcpHandlerModulePlugin.h Updates plugin constructor to no longer require Corrade manager.
src/handlers/dhcp/DhcpHandlerModulePlugin.cpp Replaces Corrade registration with VISOR_REGISTER_HANDLER_PLUGIN.
src/handlers/dhcp/DhcpHandler.conf Deletes Corrade plugin metadata.
src/handlers/dhcp/CMakeLists.txt Converts Corrade static plugin target to a plain static library.
src/handlers/bgp/CMakeLists.txt Converts Corrade static plugin target to a plain static library.
src/handlers/bgp/BgpStreamHandler.h Removes Corrade debug include.
src/handlers/bgp/BgpHandlerModulePlugin.h Updates plugin constructor to no longer require Corrade manager.
src/handlers/bgp/BgpHandlerModulePlugin.cpp Replaces Corrade registration with VISOR_REGISTER_HANDLER_PLUGIN.
src/handlers/bgp/BgpHandler.conf Deletes Corrade plugin metadata.
src/HandlerModulePlugin.h Removes Corrade types; redefines registry/pointer types using PluginRegistry + std::unique_ptr.
src/CoreRegistry.h Removes Corrade manager members/accessors; documents new registry-driven plugin instance maps.
src/CoreRegistry.cpp Initializes plugins by iterating PluginRegistry<T>::entries() and calling factories.
src/CMakeLists.txt Removes Corrade flags/linking and drops empty InputModulePlugin.cpp from build.
src/AbstractPlugin.h Removes Corrade inheritance; stores alias locally and keeps init/setup hooks.
conanfile.py Drops Corrade requirement/tool requirement.
cmd/pktvisord/main.cpp Deprecates --module-dir and updates --module-list to use the new registries.
CMakeLists.txt Removes find_package(Corrade); adds WIN32 compile defs previously provided indirectly.
.github/workflows/build-develop.yml Removes Corrade-specific macOS workaround; adds Conan apple SDK profile conf.
.github/workflows/build_cross.yml Removes CORRADE_RC_PROGRAM usage.
.github/actions/build-cpp/entrypoint.sh Removes CORRADE_RC_PROGRAM usage.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/handlers/mock/CMakeLists.txt Outdated
Comment thread src/handlers/static_plugins.h Outdated
Address Codex P1 review feedback on PR #770:

1. Codex P1 (handler/input MSVC anchors): [[gnu::used]] only protects
   GCC/Clang. On MSVC the linker's /OPT:REF strips unreferenced data
   even when the anchor array is present, so the plugin .o files don't
   get pulled in and HandlerPluginRegistry::instance().entries() ends
   up empty in Windows release artifacts. Added per-symbol
   #pragma comment(linker, "/INCLUDE:visor_force_link_*") directives
   guarded by _MSC_VER. These run regardless of optimization level.

2. Copilot (mock handler not statically linked): VisorHandlerMock was
   missing two things after the corrade_add_plugin -> add_library
   conversion:
   - It was never appended to VISOR_STATIC_PLUGINS PARENT_SCOPE in its
     CMakeLists.txt, so the plugin archive wasn't passed to the
     pktvisord/pktvisor-reader link list.
   - It was missing from the extern + anchor list in
     handlers/static_plugins.h, so even if linked, the .o would have
     no force-link reference and could be stripped.

   Both fixed: appended to VISOR_STATIC_PLUGINS, and added
   visor_force_link_VisorHandlerMock to the externs, MSVC pragma list,
   and the anchor array.

Local verification: pktvisord --module-list now reports all 17
plugins (was 16 before; the missing one was mock_dyn).
@leoparente

Copy link
Copy Markdown
Contributor Author

Fixed in 13627f5:

Codex P1 — MSVC anchor preservation (handlers + inputs): confirmed the diagnosis. [[gnu::used]] only protects GCC/Clang; MSVC's /OPT:REF would strip the inline anchor array even though it's defined, since nothing reads from it. Added per-symbol #pragma comment(linker, "/INCLUDE:visor_force_link_*") directives guarded by _MSC_VER — those tell link.exe to keep the symbols regardless of optimization. Both handlers/static_plugins.h and inputs/static_plugins.h are now MSVC-safe.

Copilot — mock handler force-link/static-link: VisorHandlerMock was missing from two places after the corrade_add_plugin → add_library(STATIC) conversion:

  1. Its CMakeLists.txt never appended Visor::Handler::Mock to VISOR_STATIC_PLUGINS PARENT_SCOPE, so the archive wasn't passed to the executables.
  2. It wasn't in the extern list / anchor array in handlers/static_plugins.h.

Both fixed; pktvisord --module-list now correctly reports all 17 plugins (was missing mock_dyn before).

@leoparente

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. What shall we delve into next?

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 98 out of 98 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/AbstractPlugin.h:11

  • This header uses std::move in the AbstractPlugin(std::string) ctor but doesn't include <utility>, relying on transitive includes. Add #include <utility> so it compiles reliably across standard library implementations.
#include <exception>
#include <nlohmann/json.hpp>
#include <string>
#include <unordered_map>

Comment thread src/PluginRegistry.h Outdated
…list

Rethinking the design after the registry-based approach was working: the
PluginRegistry singleton + VISOR_REGISTER_*_PLUGIN macros + force-link
extern symbols + MSVC /INCLUDE: pragmas + [[gnu::used]] markers + the
two static_plugins.h anchor files all existed for one reason — to make
self-registering static initializers visible across static archives.

That whole machinery only matters if plugins are loaded by something
external (the registry singleton). With static linking only, simpler
works better: a central list in CoreRegistry.cpp that explicitly names
each plugin via factory function pointers. The function pointer
references force the linker to keep each plugin .o automatically — no
anchor symbols, no platform-specific keep-alive directives, no static
init ordering concerns.

Plugin headers (e.g. FlowInputModulePlugin.h) transitively pull in
heavy implementation details like netflow.h and pcap headers, so
including them all in CoreRegistry.cpp would couple visor-core to
every plugin's transitive dependency tree. Instead, each plugin .cpp
defines a small extern factory function (make_input_pcap, etc.) and
CoreRegistry.cpp forward-declares those. visor-core stays decoupled
from plugin internals.

What's removed:
- src/PluginRegistry.h (template + macros)
- src/handlers/static_plugins.h (anchor list + MSVC pragmas + [[gnu::used]])
- src/inputs/static_plugins.h (same)
- VISOR_REGISTER_*_PLUGIN macro invocations in 16 plugin .cpp files
- visor_force_link_* extern symbols
- Includes of static_plugins.h in cmd/pktvisord/main.cpp,
  cmd/pktvisor-reader/main.cpp, src/tests/test_taps.cpp,
  src/tests/test_policies.cpp
- HandlerPluginRegistry / InputPluginRegistry typedefs

What's added:
- CoreRegistry::builtin_input_plugins() / builtin_handler_plugins()
  static methods returning {alias, version} metadata for --module-list
- g_builtin_inputs[] / g_builtin_handlers[] arrays in CoreRegistry.cpp
  with one entry per (alias, version) pointing to a factory function
- 16 trivial factory function definitions, one per plugin .cpp

Verified locally: pktvisord --module-list still reports all 17
plugins; same pre-existing dnstap/netprobe Bus error test failures
as before (unrelated to this change).

Net diff vs the registry approach: 27 files changed, 138 fewer lines
of code, and zero platform-specific build hacks.
@leoparente leoparente changed the title chore: drop Corrade in favor of hand-rolled plugin registry chore: replace Corrade plugin manager with hand-rolled in-process registry May 8, 2026
@leoparente

Copy link
Copy Markdown
Contributor Author

Reworked the design after thinking about the actual gains: dropped the templated PluginRegistry<T> singleton + VISOR_REGISTER_*_PLUGIN macros + force-link anchor machinery entirely. Replaced with a simple static array of {alias, version, factory_pointer} in CoreRegistry.cpp that explicitly references each plugin's factory function. Function-pointer references force the linker to keep each plugin .o automatically — no [[gnu::used]], no #pragma comment(linker, /INCLUDE:...), no visor_force_link_* externs, no static_plugins.h files.

Net diff: 27 files changed, 138 fewer lines than the registry approach, zero platform-specific build hacks. PR title and description updated to reflect the new design.

Verified locally: all 17 plugins still load, unit-tests-visor-core passes.

@leoparente

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Nice work!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

CI Linux failure: undefined references to visor::make_input_*,
visor::make_handler_* from CoreRegistry.cpp. The new factory-list design
introduces a real circular dependency at the static-archive level:

  visor-core's CoreRegistry.cpp.o references make_input_pcap, etc.
  libVisorInputPcap.a defines make_input_pcap (in PcapInputModulePlugin.cpp.o)
  libVisorInputPcap.a's PcapInputModulePlugin.cpp.o references AbstractPlugin
  libvisor-core.a defines AbstractPlugin

GNU ld is strictly single-pass: by the time CoreRegistry.cpp.o is pulled
in (introducing unresolved make_input_* symbols), the linker has already
finished scanning the plugin archives that come earlier in the link line.

ld64 (macOS) and MSVC's link.exe both handle this natively (multi-pass
archive resolution), which is why the local build on macOS passed.

Fix: wrap the cyclic targets in -Wl,--start-group / -Wl,--end-group via a
generator expression guarded on CXX_COMPILER_ID:GNU. Applied in all three
places that link the plugin archives + Visor::Core: pktvisord, pktvisor-
reader, and unit-tests-visor-core. CMake's $<LINK_GROUP:RESCAN,...> would
have been the more elegant approach but its built-in feature table doesn't
cover CXX out of the box and would need set_property(GLOBAL APPEND ...)
boilerplate.

Also dropped ${VISOR_STATIC_PLUGINS} from visor-core's target_link_libraries
where it was always empty anyway (variable wasn't yet populated at that
point in src/CMakeLists.txt).
@leoparente

Copy link
Copy Markdown
Contributor Author

Fixed in defb9ea.

Linux undefined references to factory functions: the new design introduced a real circular dep at the static-archive level — visor-core references plugin factory symbols, plugin libs reference AbstractPlugin from visor-core. GNU ld is strictly single-pass so it can't resolve cycles; ld64 (macOS) and MSVC's link.exe handle this natively, which is why my local macOS build was clean.

Fix: wrap the cyclic archives in -Wl,--start-group / -Wl,--end-group via a generator expression guarded on CXX_COMPILER_ID:GNU. Applied at all three link sites (pktvisord, pktvisor-reader, unit-tests-visor-core).

Stale Copilot comment about PluginRegistry.h: that file was deleted in commit fa3ebe4 — comment is no longer applicable, resolving.

@leoparente

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: defb9eaf14

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread cmd/pktvisord/CMakeLists.txt Outdated
…gins lib

Codex P1 was right that gating --start-group on CXX_COMPILER_ID:GNU
misses Linux/Clang+lld. The user pushed back further: avoid the linker
hack entirely. The right fix is to break the cycle, not patch around it.

Old layout (cyclic):
  visor-core ←→ plugin libs       (visor-core's CoreRegistry referenced
                                   plugin factories; plugins linked back
                                   to AbstractPlugin in visor-core)

New layout (linear):
  pktvisord
     └─ Visor::BuiltinPlugins    (new tiny static lib)
            ├─ plugin libs        (Visor::Handler::Net, Visor::Input::Pcap, …)
            │      └─ Visor::Core
            └─ Visor::Core

CoreRegistry no longer knows which plugins exist. It exposes
add_input_plugin() / add_handler_plugin() (called before start()) and
pending_*_plugins() (so --module-list can enumerate without start()).
A new visor-builtin-plugins lib (src/BuiltinPlugins.cpp) is the only
TU that includes plugin headers; it calls add_*_plugin for each of the
17 plugins. Both pktvisord and pktvisor-reader call
load_builtin_plugins(registry) right after constructing the registry.

The factory functions in each plugin .cpp are gone — load_builtin_plugins
calls std::make_unique directly because it has access to the plugin
headers. Plugin .cpp files are now back to just their class definitions.

Linker cycle is gone, so all the $<$<CXX_COMPILER_ID:GNU>:--start-group>
conditional flags in cmd/pktvisord, cmd/pktvisor-reader, and src/
CMakeLists are removed. No platform/compiler/linker conditionals are
needed anywhere — clean linear archive resolution works on every
linker (GNU ld, lld, ld64, link.exe).

Also moved the --module-list check earlier in pktvisord/main.cpp so it
runs before CoreServer construction (which triggers start() and empties
the pending list). Added <vector> include in CoreRegistry.h that Codex
flagged. Tests updated to call load_builtin_plugins() before start().

Verified locally: pktvisord --module-list reports all 17 plugins,
unit-tests-visor-core passes, same pre-existing dnstap/flow/netprobe
Bus/SEGFAULT failures as before (unrelated).
@leoparente

Copy link
Copy Markdown
Contributor Author

Reworked again to drop the linker hack entirely. Codex P1 was right that CXX_COMPILER_ID:GNU misses Linux/Clang+lld, but the better fix is to break the archive cycle so no --start-group is needed on any platform.

New layout (linear, no cycle):

pktvisord
   └─ Visor::BuiltinPlugins    (new tiny static lib, src/BuiltinPlugins.cpp)
          ├─ plugin libs        (Visor::Handler::Net, Visor::Input::Pcap, …)
          │      └─ Visor::Core
          └─ Visor::Core

CoreRegistry no longer knows which plugins exist. It exposes add_input_plugin() / add_handler_plugin() (called before start()) and pending_*_plugins() (for --module-list enumeration). A new visor-builtin-plugins lib is the only TU that includes plugin headers; both pktvisord and pktvisor-reader call load_builtin_plugins(registry) after constructing the registry.

What's gone:

  • All $<$<CXX_COMPILER_ID:GNU>:-Wl,--start-group> and matching --end-group from cmd/pktvisord, cmd/pktvisor-reader, and src/ CMakeLists
  • The make_input_* / make_handler_* factory functions in each plugin .cpp (no longer needed; load_builtin_plugins calls make_unique directly)
  • The extern factory forward declarations in CoreRegistry.cpp
  • The static builtin_input_plugins() / builtin_handler_plugins() methods on CoreRegistry

Also addressed:

  • Codex's <vector> include in CoreRegistry.h
  • The <string_view> / <iterator> complaint about CoreRegistry.cpp is moot now since those types/functions aren't used there anymore

Verified locally: pktvisord --module-list enumerates all 17 plugins; unit-tests-visor-core passes.

@leoparente

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Breezy!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

leoparente added 2 commits May 8, 2026 16:01
The earlier perl substitution that injected the load_builtin_plugins
call after each 'CoreRegistry registry;' line accidentally collapsed
the trailing whitespace into the next statement, leaving lines like

    visor::load_builtin_plugins(registry);        registry.start(nullptr);

Fix all 37 occurrences in test_policies.cpp and 9 in test_taps.cpp.
The mock handler (alias 'mock_dyn', namespace visor::handler::mock) was
a dynamic-loading test artifact: its CMakeLists used corrade_add_plugin
(not _static_plugin) so it was built as a shared lib, the alias name
suffixed _dyn, and the only thing that actually exercised it was the
visor_dyn_mod_int_test integration test gated on DYNAMIC_LIB_SUPPORT,
which invoked 'pktvisord --module-dir … --module-list' to verify
Corrade's runtime .so discovery.

With Corrade gone and dynamic loading dropped, this handler validates
nothing and confuses contributors who see 'mock' alongside the real
handlers. Remove the entire src/handlers/mock/ tree, the
add_subdirectory(mock) line, the BuiltinPlugins.cpp include + register
call, and the dyn_mod integration test macro.

DYNAMIC_LIB_SUPPORT was already unreferenced anywhere else in the
build, so no further cleanup needed.
@leoparente

Copy link
Copy Markdown
Contributor Author

Two more cleanups:

  1. 851cce2 — fixed jammed indentation: my earlier perl substitution had collapsed trailing whitespace, leaving lines like visor::load_builtin_plugins(registry); registry.start(nullptr); on one line. Split all 37 occurrences in test_policies.cpp and 9 in test_taps.cpp.

  2. b744337 — removed the mock handler. It was a dynamic-loading test artifact: built as corrade_add_plugin (not _static_plugin), aliased mock_dyn, and the only thing exercising it was a visor_dyn_mod_int_test gated on DYNAMIC_LIB_SUPPORT that invoked pktvisord --module-dir … --module-list to test Corrade's .so discovery. With Corrade gone and dynamic loading dropped, the handler validated nothing. Removed the whole src/handlers/mock/ tree, the registration in BuiltinPlugins.cpp, and the dyn integration test macro. Plugin count is now 16 (was 17, sans mock_dyn).

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 106 out of 106 changed files in this pull request and generated 4 comments.

Comment thread src/tests/test_taps.cpp Outdated
Comment thread src/tests/test_taps.cpp
Comment thread src/tests/test_policies.cpp Outdated
Comment thread src/tests/test_policies.cpp
@leoparente

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Bravo.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@leoparente
leoparente requested a review from Copilot May 8, 2026 19:57

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 106 out of 106 changed files in this pull request and generated 3 comments.

Comment thread src/BuiltinPlugins.cpp
Comment thread src/BuiltinPlugins.cpp
Comment thread cmd/pktvisord/main.cpp
…ality

Both options previously claimed runtime dynamic-module behavior that no
longer exists. --module-list lists statically-linked built-ins now, and
--module-dir is a deprecated no-op kept only for backwards compatibility.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 106 out of 106 changed files in this pull request and generated 4 comments.

Comment thread src/CMakeLists.txt Outdated
Comment thread src/inputs/pcap/PcapInputModulePlugin.h Outdated
Comment thread src/handlers/pcap/PcapHandlerModulePlugin.h Outdated
Comment thread src/CoreRegistry.cpp
Three Copilot comments, all valid:

1. (CMakeLists.txt:72) visor-builtin-plugins listed Visor::Core
   directly in addition to ${VISOR_STATIC_PLUGINS}. Plugin libs already
   PUBLIC-link to Visor::Core, so the direct dep was redundant and
   created an order-sensitivity hazard on single-pass linkers (the
   exact thing this whole layout is supposed to avoid). Drop it; rely
   on the transitive dep through the plugin libs, leaving a clean
   linear chain: builtin-plugins -> plugin libs -> visor-core.

2. (Plugin headers) constructor parameter `plugin` was Corrade-era
   terminology; what the parameter actually carries is the registry
   alias. Rename it to `alias` across all 16 plugin headers and the
   AbstractPlugin / HandlerModulePlugin / InputModulePlugin base ctors,
   plus the std::move call sites. Pure rename, no behaviour change.

3. (CoreRegistry.cpp) add_input_plugin / add_handler_plugin would crash
   in start() if a caller passed a null unique_ptr or empty alias /
   version. Add an std::invalid_argument throw at the entry point so
   the registry fails fast with a clear message instead of segfaulting
   later on p.mod->pluginInterface().

Verified locally: pktvisord --module-list still reports all 16
plugins; unit-tests-visor-core passes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 106 out of 106 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/AbstractPlugin.h:11

  • AbstractPlugin.h uses std::runtime_error for SchemaException but doesn't include <stdexcept> (it currently includes <exception>). This can break builds if transitive includes change; add an explicit <stdexcept> include (and optionally drop <exception> if no longer needed).
#include <exception>
#include <nlohmann/json.hpp>
#include <string>
#include <unordered_map>

Comment thread src/CoreRegistry.cpp
SchemaException inherits from std::runtime_error which is declared in
<stdexcept>, not <exception> (the latter only provides std::exception).
This previously compiled via transitive includes from <nlohmann/json.hpp>
which is fragile.

Drop the unused <exception> include and add the correct <stdexcept>.
@leoparente

Copy link
Copy Markdown
Contributor Author

Picked up the Copilot low-confidence suggestion about AbstractPlugin.h: SchemaException inherits from std::runtime_error which lives in <stdexcept>, not <exception>. Was working via transitive includes from <nlohmann/json.hpp>. Fixed in 62229cf — dropped the unused <exception> include and added <stdexcept> directly.

@leoparente
leoparente marked this pull request as ready for review May 8, 2026 21:11
@leoparente
leoparente requested a review from mfiedorowicz May 8, 2026 21:12
@leoparente
leoparente merged commit be288a6 into develop May 9, 2026
15 checks passed
@leoparente
leoparente deleted the chore/drop-corrade branch May 9, 2026 12:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants