LCOV - code coverage report
Current view: top level - src/base - plugin.cc (source / functions) Hit Total Coverage
Test: vlink Lines: 138 139 99.3 %
Date: 2026-06-27 19:56:04 Functions: 32 32 100.0 %
Branches: 143 240 59.6 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright (C) 2026 by Thun Lu. All rights reserved.
       3                 :            :  * Author: Thun Lu <thun.lu@zohomail.cn>
       4                 :            :  * Repo:   https://github.com/thun-res/vlink
       5                 :            :  *  _    __   __      _           __
       6                 :            :  * | |  / /  / /     (_) ____    / /__
       7                 :            :  * | | / /  / /     / / / __ \  / //_/
       8                 :            :  * | |/ /  / /___  / / / / / / / ,<
       9                 :            :  * |___/  /_____/ /_/ /_/ /_/ /_/|_|
      10                 :            :  *
      11                 :            :  * Licensed under the Apache License, Version 2.0 (the "License");
      12                 :            :  * you may not use this file except in compliance with the License.
      13                 :            :  * You may obtain a copy of the License at
      14                 :            :  *
      15                 :            :  *     http://www.apache.org/licenses/LICENSE-2.0
      16                 :            :  *
      17                 :            :  * Unless required by applicable law or agreed to in writing, software
      18                 :            :  * distributed under the License is distributed on an "AS IS" BASIS,
      19                 :            :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      20                 :            :  * See the License for the specific language governing permissions and
      21                 :            :  * limitations under the License.
      22                 :            :  */
      23                 :            : 
      24                 :            : #include "./base/plugin.h"
      25                 :            : 
      26                 :            : #include <deque>
      27                 :            : #include <filesystem>
      28                 :            : #include <memory>
      29                 :            : #include <mutex>
      30                 :            : #include <shared_mutex>
      31                 :            : #include <stdexcept>
      32                 :            : #include <string>
      33                 :            : #include <unordered_map>
      34                 :            : #include <utility>
      35                 :            : 
      36                 :            : #if defined(_WIN32) || defined(_WIN64)
      37                 :            : #ifndef WIN32_LEAN_AND_MEAN
      38                 :            : #define WIN32_LEAN_AND_MEAN
      39                 :            : #define VLINK_PLUGIN_UNDEFINE_LEAN_AND_MEAN
      40                 :            : #endif
      41                 :            : #ifndef NOMINMAX
      42                 :            : #define NOMINMAX
      43                 :            : #define VLINK_PLUGIN_UNDEFINE_NOMINMAX
      44                 :            : #endif
      45                 :            : #include <windows.h>
      46                 :            : #ifdef VLINK_PLUGIN_UNDEFINE_LEAN_AND_MEAN
      47                 :            : #undef WIN32_LEAN_AND_MEAN
      48                 :            : #undef VLINK_PLUGIN_UNDEFINE_LEAN_AND_MEAN
      49                 :            : #endif
      50                 :            : #ifdef VLINK_PLUGIN_UNDEFINE_NOMINMAX
      51                 :            : #undef NOMINMAX
      52                 :            : #undef VLINK_PLUGIN_UNDEFINE_NOMINMAX
      53                 :            : #endif
      54                 :            : #else
      55                 :            : #include <dlfcn.h>
      56                 :            : #endif
      57                 :            : 
      58                 :            : #include "./base/helpers.h"
      59                 :            : #include "./base/logger.h"
      60                 :            : #include "./base/utils.h"
      61                 :            : 
      62                 :            : namespace vlink {
      63                 :            : 
      64                 :          9 : [[maybe_unused]] static std::string get_current_dir() {
      65                 :            :   try {
      66   [ +  -  +  - ]:         18 :     return std::filesystem::current_path().string();
      67                 :            :   } catch (std::filesystem::filesystem_error&) {  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
      68                 :            :     return ".";                                   // LCOV_EXCL_LINE GCOVR_EXCL_LINE
      69                 :            :   }  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
      70                 :            : }
      71                 :            : 
      72                 :         52 : [[maybe_unused]] static bool check_exists(const std::string& path) {
      73                 :            :   try {
      74   [ +  -  +  - ]:         52 :     return std::filesystem::exists(path);
      75                 :            :   } catch (std::filesystem::filesystem_error&) {  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
      76                 :            :     return false;                                 // LCOV_EXCL_LINE GCOVR_EXCL_LINE
      77                 :            :   }  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
      78                 :            : }
      79                 :            : 
      80                 :            : // DynamicLibrary
      81                 :            : class DynamicLibrary final {
      82                 :            :  public:
      83                 :            : #if defined(_WIN32) || defined(_WIN64)
      84                 :            :   static constexpr const char* kFilenamePrefix = "";
      85                 :            :   static constexpr const char* kFilenameSuffix = ".dll";
      86                 :            :   using NativeHandle = HINSTANCE;
      87                 :            :   using NativeSymbol = FARPROC;
      88                 :            : #elif defined(__APPLE__)
      89                 :            :   static constexpr const char* kFilenamePrefix = "lib";
      90                 :            :   static constexpr const char* kFilenameSuffix = ".dylib";
      91                 :            :   using NativeHandle = void*;
      92                 :            :   using NativeSymbol = void*;
      93                 :            : #else
      94                 :            :   static constexpr const char* kFilenamePrefix = "lib";
      95                 :            :   static constexpr const char* kFilenameSuffix = ".so";
      96                 :            :   using NativeHandle = void*;
      97                 :            :   using NativeSymbol = void*;
      98                 :            : #endif
      99                 :            : 
     100                 :            :   static_assert(std::is_pointer_v<NativeHandle>, "Expecting native handle to be a pointer.");
     101                 :            :   static_assert(std::is_pointer_v<NativeSymbol>, "Expecting native symbol to be a pointer.");
     102                 :            : 
     103                 :            :   class Exception : public std::runtime_error {
     104                 :            :     using std::runtime_error::runtime_error;
     105                 :            :   };
     106                 :            : 
     107                 :            :   class LoadError : public Exception {
     108                 :            :     using Exception::Exception;
     109                 :            :   };
     110                 :            : 
     111                 :            :   class SymbolError : public Exception {
     112                 :            :     using Exception::Exception;
     113                 :            :   };
     114                 :            : 
     115                 :          5 :   explicit DynamicLibrary(const std::string& path) {
     116                 :          5 :     handle_ = open_library(path.c_str());
     117                 :            : 
     118         [ +  + ]:          5 :     if VUNLIKELY (!handle_) {
     119   [ +  -  +  -  :          1 :       throw LoadError("Could not load library \"" + path + "\"\n" + get_error_description());
             +  -  +  - ]
     120                 :            :     }
     121                 :          4 :   }
     122                 :            : 
     123                 :          4 :   ~DynamicLibrary() {
     124         [ +  - ]:          4 :     if VLIKELY (handle_) {
     125                 :          4 :       close_library(handle_);
     126                 :            :     }
     127                 :          4 :   }
     128                 :            : 
     129                 :            :   DynamicLibrary(DynamicLibrary&& other) noexcept : handle_(other.handle_) { other.handle_ = nullptr; }
     130                 :            : 
     131                 :            :   DynamicLibrary& operator=(DynamicLibrary&& other) noexcept {
     132                 :            :     if VLIKELY (this != &other) {
     133                 :            :       std::swap(handle_, other.handle_);
     134                 :            :     }
     135                 :            : 
     136                 :            :     return *this;
     137                 :            :   }
     138                 :            : 
     139                 :            :   template <typename SignatureT>
     140                 :          6 :   SignatureT* get_function(const std::string& name) const {
     141         [ -  + ]:          6 :     if VUNLIKELY (!handle_) {
     142         [ #  # ]:          0 :       throw std::logic_error("The dynamic library handle is null. This object may have been moved from.");
     143                 :            :     }
     144                 :            : 
     145                 :          6 :     auto* symbol = locate_symbol(handle_, name.c_str());
     146                 :            : 
     147         [ +  + ]:          6 :     if VUNLIKELY (!symbol) {
     148   [ +  -  +  -  :          1 :       throw SymbolError("Could not get symbol \"" + name + "\"\n" + get_error_description());
             +  -  +  - ]
     149                 :            :     }
     150                 :            : 
     151                 :            : #if defined(__GNUC__) && __GNUC__ >= 8
     152                 :            : #pragma GCC diagnostic push
     153                 :            : #pragma GCC diagnostic ignored "-Wcast-function-type"
     154                 :            : #endif
     155                 :            : 
     156                 :          5 :     return reinterpret_cast<SignatureT*>(symbol);
     157                 :            : 
     158                 :            : #if defined(__GNUC__) && __GNUC__ >= 8
     159                 :            : #pragma GCC diagnostic pop
     160                 :            : #endif
     161                 :            :   }
     162                 :            : 
     163                 :            :   NativeHandle native_handle() const noexcept { return handle_; }
     164                 :            : 
     165                 :            :  private:
     166                 :          5 :   static NativeHandle open_library(const char* path) noexcept {
     167                 :            : #if defined(_WIN32) || defined(_WIN64)
     168                 :            :     return ::LoadLibraryA(path);
     169                 :            : #else
     170                 :          5 :     return ::dlopen(path, RTLD_NOW | RTLD_LOCAL);
     171                 :            : #endif
     172                 :            :   }
     173                 :            : 
     174                 :          6 :   static NativeSymbol locate_symbol(NativeHandle lib, const char* name) noexcept {
     175                 :            : #if defined(_WIN32) || defined(_WIN64)
     176                 :            :     return ::GetProcAddress(lib, name);
     177                 :            : #else
     178                 :          6 :     return ::dlsym(lib, name);
     179                 :            : #endif
     180                 :            :   }
     181                 :            : 
     182                 :          4 :   static void close_library(NativeHandle lib) noexcept {
     183                 :            : #if defined(_WIN32) || defined(_WIN64)
     184                 :            :     ::FreeLibrary(lib);
     185                 :            : #else
     186                 :          4 :     ::dlclose(lib);
     187                 :            : #endif
     188                 :          4 :   }
     189                 :            : 
     190                 :          2 :   static std::string get_error_description() noexcept {
     191                 :            : #if defined(_WIN32) || defined(_WIN64)
     192                 :            :     constexpr size_t kBufferSize = 512;
     193                 :            :     const auto error_code = ::GetLastError();
     194                 :            : 
     195                 :            :     if (!error_code) {
     196                 :            :       return "No error reported by GetLastError";
     197                 :            :     }
     198                 :            : 
     199                 :            :     char description[kBufferSize];
     200                 :            : 
     201                 :            :     const auto language = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
     202                 :            :     const DWORD length =
     203                 :            :         ::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, error_code, language, description, kBufferSize, nullptr);
     204                 :            : 
     205                 :            :     return (length == 0) ? "Unknown error (FormatMessage failed)" : description;
     206                 :            : #else
     207                 :          2 :     const auto* description = ::dlerror();
     208                 :            : 
     209         [ +  - ]:          2 :     return (description == nullptr) ? "No error reported by dlerror" : description;
     210                 :            : #endif
     211                 :            :   }
     212                 :            : 
     213                 :            :   NativeHandle handle_{nullptr};
     214                 :            : 
     215                 :            :   VLINK_DISALLOW_COPY_AND_ASSIGN(DynamicLibrary)
     216                 :            : };
     217                 :            : 
     218                 :            : // PluginEntry
     219                 :            : struct PluginEntry final {
     220                 :            :   std::unique_ptr<DynamicLibrary> loader;
     221                 :            :   std::string plugin_complex_id;
     222                 :            :   Logger::Level log_level{Logger::kTrace};
     223                 :            : };
     224                 :            : 
     225                 :            : // Plugin::Impl
     226                 :            : struct Plugin::Impl final {
     227                 :            :   Logger::Level log_level{Logger::kTrace};
     228                 :            :   std::shared_mutex mtx;
     229                 :            :   std::unordered_map<std::string, std::shared_ptr<PluginEntry>> plugin_map;
     230                 :            : };
     231                 :            : 
     232                 :            : // Plugin
     233                 :        173 : Plugin::Plugin() : impl_(std::make_unique<Impl>()) {}
     234                 :            : 
     235                 :          4 : void Plugin::set_log_level(Logger::Level level) { impl_->log_level = level; }
     236                 :            : 
     237                 :          2 : Logger::Level Plugin::get_log_level() const { return impl_->log_level; }
     238                 :            : 
     239                 :        173 : Plugin::~Plugin() = default;
     240                 :            : 
     241                 :          9 : std::deque<std::string> Plugin::default_search_path() {
     242         [ +  - ]:          9 :   const std::string current_dir = get_current_dir();
     243   [ +  -  +  - ]:         18 :   const std::string plugin_dir_env = Utils::get_env("VLINK_PLUGIN_DIR");
     244                 :          9 :   const std::string local_app_dir = Utils::get_app_dir();
     245                 :            : 
     246                 :            :   std::deque<std::string> search_path{
     247                 :            :       current_dir,
     248                 :            :       local_app_dir,
     249                 :            :       local_app_dir + "/../lib64",
     250                 :            :       local_app_dir + "/../lib",
     251                 :            :       local_app_dir + "/lib64",
     252                 :            :       local_app_dir + "/lib",
     253                 :            :       "/lib64",  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     254                 :            :       "/lib",
     255   [ +  -  +  -  :         99 :   };
          +  -  +  -  +  
          -  +  -  +  -  
             +  -  +  - ]
     256                 :            : 
     257                 :          9 :   const auto plugin_dirs = Helpers::split_any(plugin_dir_env);
     258                 :            : 
     259         [ +  + ]:          9 :   if (!plugin_dirs.empty()) {
     260         [ +  - ]:          1 :     search_path.insert(search_path.begin(), plugin_dirs.begin(), plugin_dirs.end());
     261                 :            :   }
     262                 :            : 
     263                 :         18 :   return search_path;
     264                 :          9 : }
     265                 :            : 
     266                 :         59 : void Plugin::clear() {
     267         [ +  - ]:         59 :   std::lock_guard lock(impl_->mtx);
     268                 :         59 :   impl_->plugin_map.clear();
     269                 :         59 : }
     270                 :            : 
     271                 :         14 : Plugin::Handle Plugin::load_and_create(const std::string& plugin_id, const std::string& lib_name,
     272                 :            :                                        uint16_t version_major, uint16_t version_minor, const std::string& dir_name,
     273                 :            :                                        const std::deque<std::string>& search_paths, const std::string& function_name,
     274                 :            :                                        std::shared_ptr<PluginEntry>* plugin_entry) {
     275         [ -  + ]:         14 :   if VUNLIKELY (plugin_id.empty()) {
     276                 :            :     if (impl_->log_level <= Logger::kError) {  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     277                 :            :       VLOG_E("Plugin: Plugin id is empty.");   // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     278                 :            :     }
     279                 :            : 
     280                 :            :     return nullptr;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     281                 :            :   }
     282                 :            : 
     283         [ +  + ]:         14 :   if VUNLIKELY (lib_name.empty()) {
     284         [ +  - ]:          1 :     if (impl_->log_level <= Logger::kError) {
     285   [ +  -  +  - ]:          2 :       VLOG_E("Plugin: Lib name is empty.");
     286                 :            :     }
     287                 :            : 
     288                 :          1 :     return nullptr;
     289                 :            :   }
     290                 :            : 
     291   [ +  -  +  - ]:         13 :   std::string plugin_complex_id = lib_name + "@" + plugin_id;
     292                 :            : 
     293                 :            :   {
     294         [ +  - ]:         13 :     std::unique_lock load_lock(impl_->mtx);
     295                 :            : 
     296   [ +  -  +  + ]:         13 :     if VUNLIKELY (impl_->plugin_map.count(plugin_complex_id) != 0U) {
     297         [ +  - ]:          1 :       if (impl_->log_level <= Logger::kError) {
     298   [ +  -  +  - ]:          2 :         VLOG_E("Plugin: Already loaded (", plugin_complex_id, ").");
     299                 :            :       }
     300                 :            : 
     301                 :          1 :       return nullptr;
     302                 :            :     }
     303         [ +  + ]:         13 :   }
     304                 :            : 
     305                 :         12 :   std::string plugin_path;
     306                 :            : 
     307                 :            :   {
     308   [ +  -  +  - ]:         12 :     std::string plugin_name = DynamicLibrary::kFilenamePrefix + lib_name + DynamicLibrary::kFilenameSuffix;
     309                 :         12 :     std::string check_path;
     310                 :            : 
     311         [ +  + ]:         59 :     for (const auto& path : search_paths) {
     312         [ +  + ]:         52 :       if (dir_name.empty()) {
     313                 :            :         // NOLINTNEXTLINE(performance-inefficient-string-concatenation)
     314   [ +  -  +  - ]:         51 :         check_path = path + "/" + plugin_name;
     315                 :            :       } else {
     316                 :            :         // NOLINTNEXTLINE(performance-inefficient-string-concatenation)
     317   [ +  -  +  -  :          1 :         check_path = path + "/" + dir_name + "/" + plugin_name;
             +  -  +  - ]
     318                 :            :       }
     319                 :            : 
     320   [ +  -  +  + ]:         52 :       if (check_exists(check_path)) {
     321                 :          5 :         plugin_path = std::move(check_path);
     322                 :          5 :         break;
     323                 :            :       }
     324                 :            :     }
     325                 :         12 :   }
     326                 :            : 
     327                 :         12 :   Handle handle = nullptr;
     328                 :            : 
     329         [ +  + ]:         12 :   if VUNLIKELY (plugin_path.empty()) {
     330         [ +  + ]:          7 :     if (impl_->log_level <= Logger::kError) {
     331   [ +  -  +  - ]:         12 :       VLOG_E("Plugin: Cannot find plugin (", plugin_complex_id, ").");
     332                 :            :     }
     333                 :            : 
     334                 :          7 :     return handle;
     335                 :            :   }
     336                 :            : 
     337         [ +  + ]:          5 :   if (impl_->log_level <= Logger::kTrace) {
     338   [ +  -  +  - ]:          8 :     VLOG_T("Plugin: Loading plugin: ", plugin_path, ".");
     339                 :            :   }
     340                 :            : 
     341                 :            :   try {
     342         [ +  - ]:          5 :     auto entry = std::make_shared<PluginEntry>();
     343         [ +  + ]:          5 :     entry->loader = std::make_unique<DynamicLibrary>(std::move(plugin_path));
     344         [ +  - ]:          4 :     entry->plugin_complex_id = plugin_complex_id;
     345                 :          4 :     entry->log_level = impl_->log_level;
     346                 :            : 
     347                 :            :     auto create_function =
     348         [ +  + ]:          4 :         entry->loader->get_function<Handle(const char*, const char*, uint16_t, uint16_t, uint8_t)>(function_name);
     349                 :            : 
     350         [ -  + ]:          3 :     if VUNLIKELY (!create_function) {
     351                 :            :       // LCOV_EXCL_START GCOVR_EXCL_START
     352                 :            :       if (impl_->log_level <= Logger::kError) {
     353                 :            :         VLOG_E("Plugin: Cannot find symbol function to create (", plugin_complex_id, ").");
     354                 :            :       }
     355                 :            : 
     356                 :            :       return handle;
     357                 :            :       // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     358                 :            :     }
     359                 :            : 
     360         [ +  - ]:          3 :     handle = create_function(lib_name.c_str(), plugin_id.c_str(), version_major, version_minor, impl_->log_level);
     361                 :            : 
     362         [ +  + ]:          3 :     if VUNLIKELY (!handle) {
     363         [ +  - ]:          1 :       if (impl_->log_level <= Logger::kError) {
     364   [ +  -  +  - ]:          2 :         VLOG_E("Plugin: Failed to create handle (", plugin_complex_id, ").");
     365                 :            :       }
     366                 :            : 
     367                 :          1 :       return handle;
     368                 :            :     }
     369                 :            : 
     370         [ +  + ]:          2 :     if (impl_->log_level <= Logger::kTrace) {
     371   [ +  -  +  - ]:          2 :       VLOG_T("Plugin: Loaded successfully (", plugin_complex_id, ").");
     372                 :            :     }
     373                 :            : 
     374                 :          2 :     bool inserted = false;
     375                 :            : 
     376                 :            :     {
     377         [ +  - ]:          2 :       std::unique_lock load_lock(impl_->mtx);
     378         [ +  - ]:          2 :       auto [iter, emplaced] = impl_->plugin_map.emplace(plugin_complex_id, entry);
     379                 :            :       (void)iter;
     380                 :          2 :       inserted = emplaced;
     381                 :          2 :     }
     382                 :            : 
     383         [ -  + ]:          2 :     if VUNLIKELY (!inserted) {
     384                 :            :       destroy(entry, handle);  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     385                 :            :       return nullptr;          // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     386                 :            :     }
     387                 :            : 
     388         [ +  - ]:          2 :     if (plugin_entry) {
     389                 :          2 :       *plugin_entry = entry;
     390                 :            :     }
     391                 :            : 
     392                 :          2 :     return handle;
     393         [ -  + ]:          7 :   } catch (const DynamicLibrary::Exception& e) {
     394         [ +  - ]:          2 :     if (impl_->log_level <= Logger::kError) {
     395   [ +  -  +  - ]:          4 :       VLOG_E("Plugin: Failed to load plugin (", plugin_complex_id, "): ", e.what(), ".");
     396                 :            :     }
     397                 :            : 
     398                 :          2 :     return handle;
     399                 :          2 :   }
     400                 :         13 : }
     401                 :            : 
     402                 :          2 : bool Plugin::unload(const std::string& plugin_complex_id) {
     403         [ -  + ]:          2 :   if VUNLIKELY (plugin_complex_id.empty()) {
     404                 :            :     if (impl_->log_level <= Logger::kError) {  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     405                 :            :       VLOG_E("Plugin: Plugin id is empty.");   // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     406                 :            :     }
     407                 :            : 
     408                 :            :     return false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     409                 :            :   }
     410                 :            : 
     411         [ +  + ]:          2 :   if VUNLIKELY (!has_loaded(plugin_complex_id)) {
     412         [ +  - ]:          1 :     if (impl_->log_level <= Logger::kError) {
     413   [ +  -  +  - ]:          2 :       VLOG_E("Plugin: Not loaded (", plugin_complex_id, ").");
     414                 :            :     }
     415                 :            : 
     416                 :          1 :     return false;
     417                 :            :   }
     418                 :            : 
     419                 :            :   {
     420         [ +  - ]:          1 :     std::lock_guard lock(impl_->mtx);
     421         [ +  - ]:          1 :     impl_->plugin_map.erase(plugin_complex_id);
     422                 :          1 :   }
     423                 :            : 
     424                 :          1 :   return true;
     425                 :            : }
     426                 :            : 
     427                 :          8 : bool Plugin::has_loaded(const std::string& plugin_complex_id) {
     428         [ +  - ]:          8 :   std::shared_lock lock(impl_->mtx);
     429         [ +  - ]:         16 :   return impl_->plugin_map.count(plugin_complex_id) != 0;
     430                 :          8 : }
     431                 :            : 
     432                 :          2 : bool Plugin::destroy(std::shared_ptr<PluginEntry> plugin_entry, Handle handle, const std::string& function_name) {
     433   [ +  -  -  +  :          2 :   if VUNLIKELY (!plugin_entry || !plugin_entry->loader || !handle) {
          +  -  -  +  -  
                      + ]
     434                 :            :     return false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     435                 :            :   }
     436                 :            : 
     437                 :            :   try {
     438         [ +  - ]:          2 :     auto destroy_function = plugin_entry->loader->get_function<bool(Handle)>(function_name);
     439                 :            : 
     440         [ -  + ]:          2 :     if VUNLIKELY (!destroy_function) {
     441                 :            :       // LCOV_EXCL_START GCOVR_EXCL_START
     442                 :            :       if (plugin_entry->log_level <= Logger::kError) {
     443                 :            :         VLOG_E("Plugin: Cannot find symbol function to destroy (", plugin_entry->plugin_complex_id, ").");
     444                 :            :       }
     445                 :            : 
     446                 :            :       return false;
     447                 :            :       // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     448                 :            :     }
     449                 :            : 
     450   [ +  -  -  + ]:          2 :     if VUNLIKELY (!destroy_function(handle)) {
     451                 :            :       // LCOV_EXCL_START GCOVR_EXCL_START
     452                 :            :       if (plugin_entry->log_level <= Logger::kError) {
     453                 :            :         VLOG_E("Plugin: Failed to destroy handle (", plugin_entry->plugin_complex_id, ").");
     454                 :            :       }
     455                 :            : 
     456                 :            :       return false;
     457                 :            :     }
     458                 :            :   } catch (const std::exception& e) {
     459                 :            :     if (plugin_entry->log_level <= Logger::kError) {
     460                 :            :       VLOG_E("Plugin: Failed to destroy handle (", plugin_entry->plugin_complex_id, "): ", e.what(), ".");
     461                 :            :     }
     462                 :            : 
     463                 :            :     return false;
     464                 :            :   } catch (...) {
     465                 :            :     if (plugin_entry->log_level <= Logger::kError) {
     466                 :            :       VLOG_E("Plugin: Failed to destroy handle (", plugin_entry->plugin_complex_id, "): non-std exception.");
     467                 :            :     }
     468                 :            : 
     469                 :            :     return false;
     470                 :            :   }
     471                 :            :   // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     472                 :            : 
     473                 :          2 :   return true;
     474                 :            : }
     475                 :            : 
     476                 :         11 : bool Plugin::process_plugin_internal(const std::string& lib_name, const std::string& local_plugin_id,
     477                 :            :                                      uint16_t local_version_major, uint16_t local_version_minor,
     478                 :            :                                      const std::string& target_plugin_id, uint16_t target_version_major,
     479                 :            :                                      uint16_t target_version_minor, uint8_t log_level) {
     480         [ +  + ]:         11 :   if (log_level <= Logger::kInfo) {
     481   [ +  -  +  - ]:          6 :     VLOG_I("Plugin: ", lib_name, "@", local_plugin_id, "#", local_version_major, ".", local_version_minor, ".");
     482                 :            :   }
     483                 :            : 
     484   [ +  +  +  +  :         11 :   if VUNLIKELY (target_plugin_id.empty() || target_plugin_id != local_plugin_id) {
                   +  + ]
     485         [ +  - ]:          3 :     if (log_level <= Logger::kError) {
     486   [ +  -  +  - ]:          6 :       VLOG_E("Plugin: Plugin id mismatch: expected '", local_plugin_id, "', got '", target_plugin_id, "'.");
     487                 :            :     }
     488                 :            : 
     489                 :          3 :     return false;
     490                 :            :   }
     491                 :            : 
     492   [ +  +  +  +  :          8 :   if VUNLIKELY (target_version_major != local_version_major || target_version_minor > local_version_minor) {
                   +  + ]
     493         [ +  - ]:          2 :     if (log_level <= Logger::kError) {
     494   [ +  -  +  - ]:          4 :       VLOG_E("Plugin: Version mismatch: local ", local_version_major, ".", local_version_minor, ", required ",
     495                 :            :              target_version_major, ".", target_version_minor, ".");
     496                 :            :     }
     497                 :            : 
     498                 :          2 :     return false;
     499                 :            :   }
     500                 :            : 
     501                 :          6 :   return true;
     502                 :            : }
     503                 :            : 
     504                 :            : }  // namespace vlink

Generated by: LCOV version 1.14