The Gaudi Framework  master (f31105fd)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
PluginServiceV2.cpp
Go to the documentation of this file.
1 /***********************************************************************************\
2 * (c) Copyright 2013-2025 CERN for the benefit of the LHCb and ATLAS collaborations *
3 * *
4 * This software is distributed under the terms of the Apache version 2 licence, *
5 * copied verbatim in the file "LICENSE". *
6 * *
7 * In applying this licence, CERN does not waive the privileges and immunities *
8 * granted to it by virtue of its status as an Intergovernmental Organization *
9 * or submit itself to any jurisdiction. *
10 \***********************************************************************************/
11 
13 
14 #define GAUDI_PLUGIN_SERVICE_V2
16 
17 #include <dirent.h>
18 #include <dlfcn.h>
19 
20 #include <cstdlib>
21 #include <fstream>
22 #include <iostream>
23 #include <memory>
24 #include <regex>
25 #include <string_view>
26 
27 #include <cxxabi.h>
28 #include <sys/stat.h>
29 
30 #ifdef _GNU_SOURCE
31 # include <cstring>
32 # include <dlfcn.h>
33 #endif
34 
35 #ifdef USE_BOOST_FILESYSTEM
36 # include <boost/filesystem.hpp>
37 namespace fs = boost::filesystem;
38 #else
39 # include <filesystem>
40 namespace fs = std::filesystem;
41 #endif // USE_BOOST_FILESYSTEM
42 
43 namespace {
44  std::mutex registrySingletonMutex;
45 }
46 
47 #include <algorithm>
48 
49 namespace {
50  struct OldStyleCnv {
52  void operator()( const char c ) {
53  switch ( c ) {
54  case '<':
55  case '>':
56  case ',':
57  case '(':
58  case ')':
59  case ':':
60  case '.':
61  name.push_back( '_' );
62  break;
63  case '&':
64  name.push_back( 'r' );
65  break;
66  case '*':
67  name.push_back( 'p' );
68  break;
69  case ' ':
70  break;
71  default:
72  name.push_back( c );
73  break;
74  }
75  }
76  };
78  std::string old_style_name( const std::string& name ) {
79  return std::for_each( name.begin(), name.end(), OldStyleCnv() ).name;
80  }
81 } // namespace
82 
83 namespace Gaudi {
84  namespace PluginService {
86  namespace Details {
88  int status;
90  abi::__cxa_demangle( id.c_str(), nullptr, nullptr, &status ), free );
91  if ( !realname ) return id;
92  return std::regex_replace(
93  realname.get(),
94  std::regex{ "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >( (?=>))?" },
95  "std::string" );
96  }
97  std::string demangle( const std::type_info& id ) { return demangle( id.name() ); }
98 
99  Registry& Registry::instance() {
100  auto _guard = std::scoped_lock{ ::registrySingletonMutex };
101  static Registry r;
102  return r;
103  }
104 
105  void reportBadAnyCast( const std::type_info& factory_type, const std::string& id ) {
106  if ( logger().level() <= Logger::Debug ) {
108  const auto& info = Registry::instance().getInfo( id );
109  msg << "bad any_cast: requested factory " << id << " of type " << demangle( factory_type ) << ", got ";
110  if ( info.is_set() )
111  msg << demangle( info.factory.type() ) << " from " << info.library;
112  else
113  msg << "nothing";
114  logger().debug( msg.str() );
115  }
116  }
117 
118  Registry::Properties::mapped_type Registry::FactoryInfo::getprop( const Properties::key_type& name ) const {
119  auto p = properties.find( name );
120  return ( p != end( properties ) ) ? p->second : Properties::mapped_type{};
121  }
122 
123  Registry::Registry() {}
124 
125  void Registry::initialize() {
126  auto _guard = std::scoped_lock{ m_mutex };
127 #if defined( _WIN32 )
128  const char* envVar = "PATH";
129  const char sep = ';';
130 #elif defined( __APPLE__ )
131  const char* envVar = "GAUDI_PLUGIN_PATH";
132  const char sep = ':';
133 #else
134  const char* envVar = "LD_LIBRARY_PATH";
135  const char sep = ':';
136 #endif
137 
138  std::regex line_format{ "^(?:[[:space:]]*(?:(v[0-9]+)::)?([^:]+):(.*[^[:space:]]))?[[:space:]]*(?:#.*)?$" };
139  std::smatch m;
140 
141  std::string_view search_path;
142  if ( auto ptr = std::getenv( envVar ) ) search_path = std::string_view{ ptr };
143  if ( !search_path.empty() ) {
144  logger().debug( std::string( "searching factories in " ) + envVar );
145 
146  std::string_view::size_type start_pos = 0, end_pos = 0;
147  while ( start_pos != std::string_view::npos ) {
148  // correctly handle begin of string or path separator
149  if ( start_pos ) ++start_pos;
150 
151  end_pos = search_path.find( sep, start_pos );
152  fs::path dirName =
153 #ifdef USE_BOOST_FILESYSTEM
154  std::string{ search_path.substr( start_pos, end_pos - start_pos ) };
155 #else
156  search_path.substr( start_pos, end_pos - start_pos );
157 #endif
158  start_pos = end_pos;
159  logger().debug( " looking into " + dirName.string() );
160  // look for files called "*.components" in the directory
161  if ( is_directory( dirName ) ) {
162  for ( auto& p : fs::directory_iterator( dirName ) ) {
163  if ( p.path().extension() == ".components" && is_regular_file( p.path() ) ) {
164  // read the file
165  const auto& fullPath = p.path().string();
166  logger().debug( " reading " + p.path().filename().string() );
167  std::ifstream factories{ fullPath };
169  int factoriesCount = 0;
170  int lineCount = 0;
171  while ( !factories.eof() ) {
172  ++lineCount;
174  if ( regex_match( line, m, line_format ) ) {
175  if ( m[1] == "v2" ) { // ignore non "v2" and "empty" lines
176  const std::string lib{ m[2] };
177  const std::string fact{ m[3] };
178  m_factories.emplace( fact, FactoryInfo{ lib, {}, { { "ClassName", fact } } } );
179 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
180  // add an alias for the factory using the Reflex convention
181  std::string old_name = old_style_name( fact );
182  if ( fact != old_name ) {
183  m_factories.emplace(
184  old_name, FactoryInfo{ lib, {}, { { "ReflexName", "true" }, { "ClassName", fact } } } );
185  }
186 #endif
187  ++factoriesCount;
188  }
189  } else {
190  logger().warning( "failed to parse line " + fullPath + ':' + std::to_string( lineCount ) );
191  }
192  }
193  if ( logger().level() <= Logger::Debug ) {
194  logger().debug( " found " + std::to_string( factoriesCount ) + " factories" );
195  }
196  }
197  }
198  }
199  }
200  }
201  }
202 
203  const Registry::FactoryMap& Registry::factories() const {
204  std::call_once( m_initialized, &Registry::initialize, const_cast<Registry*>( this ) );
205  return m_factories;
206  }
207 
208  Registry::FactoryMap& Registry::factories() {
209  std::call_once( m_initialized, &Registry::initialize, this );
210  return m_factories;
211  }
212 
213  Registry::FactoryInfo& Registry::add( const KeyType& id, FactoryInfo info ) {
214  auto _guard = std::scoped_lock{ m_mutex };
215  FactoryMap& facts = factories();
216 
217 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
218  // add an alias for the factory using the Reflex convention
219  const auto old_name = old_style_name( id );
220  if ( id != old_name ) {
221  auto new_info = info;
222 
223  new_info.properties["ReflexName"] = "true";
224 
225  add( old_name, new_info );
226  }
227 #endif
228 
229  auto entry = facts.find( id );
230  if ( entry == facts.end() ) {
231  // this factory was not known yet
232  entry = facts.emplace( id, std::move( info ) ).first;
233  } else {
234  // do not replace an existing factory with a new one
235  if ( !entry->second.is_set() ) entry->second = std::move( info );
236  }
237  return entry->second;
238  }
239 
240  Registry::FactoryMap::size_type Registry::erase( const KeyType& id ) {
241  auto _guard = std::scoped_lock{ m_mutex };
242  FactoryMap& facts = factories();
243  return facts.erase( id );
244  }
245 
246  const Registry::FactoryInfo& Registry::getInfo( const KeyType& id, const bool load ) const {
247  auto _guard = std::scoped_lock{ m_mutex };
248  static const FactoryInfo unknown = { "unknown" };
249  const FactoryMap& facts = factories();
250  auto f = facts.find( id );
251 
252  if ( f != facts.end() ) {
253  if ( load && !f->second.is_set() ) {
254  const std::string library = f->second.library;
255 #ifdef __APPLE__
256  std::string_view search_path;
257  if ( auto ptr = std::getenv( "GAUDI_PLUGIN_PATH" ) ) search_path = std::string_view{ ptr };
258  if ( !search_path.empty() ) {
259 
260  std::string_view::size_type start_pos = 0, end_pos = 0;
261  while ( start_pos != std::string_view::npos ) {
262  // correctly handle begin of string or path separator
263  if ( start_pos ) ++start_pos;
264 
265  end_pos = search_path.find( ":", start_pos );
266  fs::path dirName =
267 # ifdef USE_BOOST_FILESYSTEM
268  std::string{ search_path.substr( start_pos, end_pos - start_pos ) };
269 # else
270  search_path.substr( start_pos, end_pos - start_pos );
271 # endif
272  start_pos = end_pos;
273 
274  logger().debug( " looking into " + dirName.string() );
275  // look for files called "*.components" in the directory
276  if ( is_directory( dirName ) && is_regular_file( dirName / fs::path( library ) ) ) {
277  logger().debug( "found " + dirName.string() + "/" + library.c_str() + " for factory " + id );
278  if ( !dlopen( ( dirName.string() + "/" + library ).c_str(), RTLD_LAZY | RTLD_GLOBAL ) ) {
279  logger().warning( "cannot load " + library + " for factory " + id );
280  char* dlmsg = dlerror();
281  if ( dlmsg ) logger().warning( dlmsg );
282  return unknown;
283  }
284  }
285  }
286  }
287 #else
288 
289  if ( !dlopen( library.c_str(), RTLD_LAZY | RTLD_GLOBAL ) ) {
290  logger().warning( "cannot load " + library + " for factory " + id );
291  char* dlmsg = dlerror();
292  if ( dlmsg ) logger().warning( dlmsg );
293  return unknown;
294  }
295 #endif
296  f = facts.find( id ); // ensure that the iterator is valid
297  }
298  return f->second;
299  } else {
300  return unknown;
301  }
302  }
303 
304  Registry& Registry::addProperty( const KeyType& id, const KeyType& k, const std::string& v ) {
305  auto _guard = std::scoped_lock{ m_mutex };
306  FactoryMap& facts = factories();
307  auto f = facts.find( id );
308 
309  if ( f != facts.end() ) f->second.properties[k] = v;
310  return *this;
311  }
312 
313  void Registry::setError( const KeyType& warning ) { m_werror.insert( warning ); }
314 
315  void Registry::unsetError( const KeyType& warning ) { m_werror.erase( warning ); }
316 
317  std::set<Registry::KeyType> Registry::loadedFactoryNames() const {
318  auto _guard = std::scoped_lock{ m_mutex };
320  for ( const auto& f : factories() ) {
321  if ( f.second.is_set() ) l.insert( f.first );
322  }
323  return l;
324  }
325 
326  void Logger::report( Level lvl, const std::string& msg ) {
327  static const char* levels[] = { "DEBUG : ", "INFO : ", "WARNING: ", "ERROR : " };
328  if ( lvl >= level() ) { std::cerr << levels[lvl] << msg << std::endl; }
329  }
330 
331  static auto s_logger = std::make_unique<Logger>();
332  Logger& logger() { return *s_logger; }
333  void setLogger( Logger* logger ) { s_logger.reset( logger ); }
334 
335  // This chunk of code was taken from GaudiKernel (genconf) DsoUtils.h
336  std::string getDSONameFor( void* fptr ) {
337 #if defined _GNU_SOURCE || defined __APPLE__
338  Dl_info info;
339  if ( dladdr( fptr, &info ) == 0 ) return "";
340 
341  auto pos = std::strrchr( info.dli_fname, '/' );
342  if ( pos )
343  ++pos;
344  else
345  return info.dli_fname;
346  return pos;
347 #else
348  return "";
349 #endif
350  }
351  } // namespace Details
352 
353  void SetDebug( int debugLevel ) {
354  using namespace Details;
355  Logger& l = logger();
356  if ( debugLevel > 1 )
357  l.setLevel( Logger::Debug );
358  else if ( debugLevel > 0 )
359  l.setLevel( Logger::Info );
360  else
361  l.setLevel( Logger::Warning );
362  }
363 
364  int Debug() {
365  using namespace Details;
366  switch ( logger().level() ) {
367  case Logger::Debug:
368  return 2;
369  case Logger::Info:
370  return 1;
371  default:
372  return 0;
373  }
374  }
375  }
376  } // namespace PluginService
377 } // namespace Gaudi
std::call_once
T call_once(T... args)
std::for_each
T for_each(T... args)
std::strrchr
T strrchr(T... args)
std::string
STL class.
Gaudi::PluginService::v2::Details::reportBadAnyCast
void reportBadAnyCast(const std::type_info &factory_type, const std::string &id)
Definition: PluginServiceV2.cpp:105
std::move
T move(T... args)
AtlasMCRecoFullPrecedenceDump.path
path
Definition: AtlasMCRecoFullPrecedenceDump.py:49
Gaudi::PluginService::v2::SetDebug
void SetDebug(int debugLevel)
Definition: PluginServiceV2.cpp:353
std::string::find
T find(T... args)
std::type_info
GAUDI_PLUGIN_SERVICE_V2_INLINE
#define GAUDI_PLUGIN_SERVICE_V2_INLINE
Definition: PluginServiceCommon.h:17
GaudiMP.FdsRegistry.msg
msg
Definition: FdsRegistry.py:19
std::stringstream
STL class.
std::regex_match
T regex_match(T... args)
gaudirun.c
c
Definition: gaudirun.py:525
GaudiPartProp.tests.id
id
Definition: tests.py:111
PluginService.h
Gaudi::PluginService::v2::Details::setLogger
void setLogger(Logger *logger)
Definition: PluginServiceV2.cpp:333
Gaudi::Units::m
constexpr double m
Definition: SystemOfUnits.h:108
std::cerr
std::string::c_str
T c_str(T... args)
GaudiPartProp.tests.v2
v2
Definition: tests.py:59
std::getenv
T getenv(T... args)
std::to_string
T to_string(T... args)
Gaudi::PluginService::v2::Details::getDSONameFor
std::string getDSONameFor(void *fptr)
Definition: PluginServiceV2.cpp:336
cpluginsvc.factories
def factories()
Definition: cpluginsvc.py:93
std::regex
gaudirun.level
level
Definition: gaudirun.py:364
Gaudi
This file provides a Grammar for the type Gaudi::Accumulators::Axis It allows to use that type from p...
Definition: __init__.py:1
gaudiComponentHelp.properties
properties
Definition: gaudiComponentHelp.py:68
Gaudi::PluginService::v2::Details::logger
Logger & logger()
Definition: PluginServiceV2.cpp:332
Gaudi::PluginService::v2::Debug
int Debug()
Definition: PluginServiceV2.cpp:364
std::string::substr
T substr(T... args)
MSG::Level
Level
Definition: IMessageSvc.h:25
ConditionsStallTest.name
name
Definition: ConditionsStallTest.py:77
std::endl
T endl(T... args)
gaudirun.l
dictionary l
Definition: gaudirun.py:583
std::getline
T getline(T... args)
plotSpeedupsPyRoot.line
line
Definition: plotSpeedupsPyRoot.py:198
Properties.v
v
Definition: Properties.py:122
std::mutex
STL class.
std::smatch
IOTest.end
end
Definition: IOTest.py:125
std::unique_ptr
STL class.
std::regex_replace
T regex_replace(T... args)
std::set
STL class.
GaudiPython.Persistency.add
def add(instance)
Definition: Persistency.py:50
Gaudi::PluginService::v2::Details::demangle
std::string demangle(const std::string &id)
Definition: PluginServiceV2.cpp:87
std::ifstream
STL class.